UNPKG

12.4 kBJavaScriptView Raw
1/*
2Copyright (c) 2019-present NAVER Corp.
3name: @egjs/list-differ
4license: MIT
5author: NAVER Corp.
6repository: https://github.com/naver/egjs-list-differ
7version: 1.0.0
8*/
9(function (global, factory) {
10 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
11 typeof define === 'function' && define.amd ? define(factory) :
12 (global = global || self, (global.eg = global.eg || {}, global.eg.ListDiffer = factory()));
13}(this, function () { 'use strict';
14
15 /*
16 egjs-list-differ
17 Copyright (c) 2019-present NAVER Corp.
18 MIT license
19 */
20 var PolyMap =
21 /*#__PURE__*/
22 function () {
23 function PolyMap() {
24 this.keys = [];
25 this.values = [];
26 }
27
28 var __proto = PolyMap.prototype;
29
30 __proto.get = function (key) {
31 return this.values[this.keys.indexOf(key)];
32 };
33
34 __proto.set = function (key, value) {
35 var keys = this.keys;
36 var values = this.values;
37 var prevIndex = keys.indexOf(key);
38 var index = prevIndex === -1 ? keys.length : prevIndex;
39 keys[index] = key;
40 values[index] = value;
41 };
42
43 return PolyMap;
44 }();
45
46 /*
47 egjs-list-differ
48 Copyright (c) 2019-present NAVER Corp.
49 MIT license
50 */
51 var HashMap =
52 /*#__PURE__*/
53 function () {
54 function HashMap() {
55 this.object = {};
56 }
57
58 var __proto = HashMap.prototype;
59
60 __proto.get = function (key) {
61 return this.object[key];
62 };
63
64 __proto.set = function (key, value) {
65 this.object[key] = value;
66 };
67
68 return HashMap;
69 }();
70
71 /*
72 egjs-list-differ
73 Copyright (c) 2019-present NAVER Corp.
74 MIT license
75 */
76 var SUPPORT_MAP = typeof Map === "function";
77
78 /*
79 egjs-list-differ
80 Copyright (c) 2019-present NAVER Corp.
81 MIT license
82 */
83 var Link =
84 /*#__PURE__*/
85 function () {
86 function Link() {}
87
88 var __proto = Link.prototype;
89
90 __proto.connect = function (prevLink, nextLink) {
91 this.prev = prevLink;
92 this.next = nextLink;
93 prevLink && (prevLink.next = this);
94 nextLink && (nextLink.prev = this);
95 };
96
97 __proto.disconnect = function () {
98 // In double linked list, diconnect the interconnected relationship.
99 var prevLink = this.prev;
100 var nextLink = this.next;
101 prevLink && (prevLink.next = nextLink);
102 nextLink && (nextLink.prev = prevLink);
103 };
104
105 __proto.getIndex = function () {
106 var link = this;
107 var index = -1;
108
109 while (link) {
110 link = link.prev;
111 ++index;
112 }
113
114 return index;
115 };
116
117 return Link;
118 }();
119
120 /*
121 egjs-list-differ
122 Copyright (c) 2019-present NAVER Corp.
123 MIT license
124 */
125
126 function orderChanged(changed, fixed) {
127 // It is roughly in the order of these examples.
128 // 4, 6, 0, 2, 1, 3, 5, 7
129 var fromLinks = []; // 0, 1, 2, 3, 4, 5, 6, 7
130
131 var toLinks = [];
132 changed.forEach(function (_a) {
133 var from = _a[0],
134 to = _a[1];
135 var link = new Link();
136 fromLinks[from] = link;
137 toLinks[to] = link;
138 }); // `fromLinks` are connected to each other by double linked list.
139
140 fromLinks.forEach(function (link, i) {
141 link.connect(fromLinks[i - 1]);
142 });
143 return changed.filter(function (_, i) {
144 return !fixed[i];
145 }).map(function (_a, i) {
146 var from = _a[0],
147 to = _a[1];
148
149 if (from === to) {
150 return [0, 0];
151 }
152
153 var fromLink = fromLinks[from];
154 var toLink = toLinks[to - 1];
155 var fromIndex = fromLink.getIndex(); // Disconnect the link connected to `fromLink`.
156
157 fromLink.disconnect(); // Connect `fromLink` to the right of `toLink`.
158
159 if (!toLink) {
160 fromLink.connect(undefined, fromLinks[0]);
161 } else {
162 fromLink.connect(toLink, toLink.next);
163 }
164
165 var toIndex = fromLink.getIndex();
166 return [fromIndex, toIndex];
167 });
168 }
169
170 var Result =
171 /*#__PURE__*/
172 function () {
173 function Result(prevList, list, added, removed, changed, maintained, changedBeforeAdded, fixed) {
174 this.prevList = prevList;
175 this.list = list;
176 this.added = added;
177 this.removed = removed;
178 this.changed = changed;
179 this.maintained = maintained;
180 this.changedBeforeAdded = changedBeforeAdded;
181 this.fixed = fixed;
182 }
183
184 var __proto = Result.prototype;
185 Object.defineProperty(__proto, "ordered", {
186 get: function () {
187 if (!this.cacheOrdered) {
188 this.caculateOrdered();
189 }
190
191 return this.cacheOrdered;
192 },
193 enumerable: true,
194 configurable: true
195 });
196 Object.defineProperty(__proto, "pureChanged", {
197 get: function () {
198 if (!this.cachePureChanged) {
199 this.caculateOrdered();
200 }
201
202 return this.cachePureChanged;
203 },
204 enumerable: true,
205 configurable: true
206 });
207
208 __proto.caculateOrdered = function () {
209 var ordered = orderChanged(this.changedBeforeAdded, this.fixed);
210 var changed = this.changed;
211 var pureChanged = [];
212 this.cacheOrdered = ordered.filter(function (_a, i) {
213 var from = _a[0],
214 to = _a[1];
215 var _b = changed[i],
216 fromBefore = _b[0],
217 toBefore = _b[1];
218
219 if (from !== to) {
220 pureChanged.push([fromBefore, toBefore]);
221 return true;
222 }
223 });
224 this.cachePureChanged = pureChanged;
225 };
226
227 return Result;
228 }();
229
230 /**
231 *
232 * @memberof eg.ListDiffer
233 * @static
234 * @function
235 * @param - Previous List <ko> 이전 목록 </ko>
236 * @param - List to Update <ko> 업데이트 할 목록 </ko>
237 * @param - This callback function returns the key of the item. <ko> 아이템의 키를 반환하는 콜백 함수입니다.</ko>
238 * @return - Returns the diff between `prevList` and `list` <ko> `prevList`와 `list`의 다른 점을 반환한다.</ko>
239 * @example
240 * import { diff } from "@egjs/list-differ";
241 * // script => eg.ListDiffer.diff
242 * const result = diff([0, 1, 2, 3, 4, 5], [7, 8, 0, 4, 3, 6, 2, 1], e => e);
243 * // List before update
244 * // [1, 2, 3, 4, 5]
245 * console.log(result.prevList);
246 * // Updated list
247 * // [4, 3, 6, 2, 1]
248 * console.log(result.list);
249 * // Index array of values added to `list`
250 * // [0, 1, 5]
251 * console.log(result.added);
252 * // Index array of values removed in `prevList`
253 * // [5]
254 * console.log(result.removed);
255 * // An array of index pairs of `prevList` and `list` with different indexes from `prevList` and `list`
256 * // [[0, 2], [4, 3], [3, 4], [2, 6], [1, 7]]
257 * console.log(result.changed);
258 * // The subset of `changed` and an array of index pairs that moved data directly. Indicate an array of absolute index pairs of `ordered`.(Formatted by: Array<[index of prevList, index of list]>)
259 * // [[4, 3], [3, 4], [2, 6]]
260 * console.log(result.pureChanged);
261 * // An array of index pairs to be `ordered` that can synchronize `list` before adding data. (Formatted by: Array<[prevIndex, nextIndex]>)
262 * // [[4, 1], [4, 2], [4, 3]]
263 * console.log(result.ordered);
264 * // An array of index pairs of `prevList` and `list` that have not been added/removed so data is preserved
265 * // [[0, 2], [4, 3], [3, 4], [2, 6], [1, 7]]
266 * console.log(result.maintained);
267 */
268
269 function diff(prevList, list, findKeyCallback) {
270 var mapClass = SUPPORT_MAP ? Map : findKeyCallback ? HashMap : PolyMap;
271
272 var callback = findKeyCallback || function (e) {
273 return e;
274 };
275
276 var added = [];
277 var removed = [];
278 var maintained = [];
279 var prevKeys = prevList.map(callback);
280 var keys = list.map(callback);
281 var prevKeyMap = new mapClass();
282 var keyMap = new mapClass();
283 var changedBeforeAdded = [];
284 var fixed = [];
285 var removedMap = {};
286 var changed = [];
287 var addedCount = 0;
288 var removedCount = 0; // Add prevKeys and keys to the hashmap.
289
290 prevKeys.forEach(function (key, prevListIndex) {
291 prevKeyMap.set(key, prevListIndex);
292 });
293 keys.forEach(function (key, listIndex) {
294 keyMap.set(key, listIndex);
295 }); // Compare `prevKeys` and `keys` and add them to `removed` if they are not in `keys`.
296
297 prevKeys.forEach(function (key, prevListIndex) {
298 var listIndex = keyMap.get(key); // In prevList, but not in list, it is removed.
299
300 if (typeof listIndex === "undefined") {
301 ++removedCount;
302 removed.push(prevListIndex);
303 } else {
304 removedMap[listIndex] = removedCount;
305 }
306 }); // Compare `prevKeys` and `keys` and add them to `added` if they are not in `prevKeys`.
307
308 keys.forEach(function (key, listIndex) {
309 var prevListIndex = prevKeyMap.get(key); // In list, but not in prevList, it is added.
310
311 if (typeof prevListIndex === "undefined") {
312 added.push(listIndex);
313 ++addedCount;
314 } else {
315 maintained.push([prevListIndex, listIndex]);
316 removedCount = removedMap[listIndex] || 0;
317 changedBeforeAdded.push([prevListIndex - removedCount, listIndex - addedCount]);
318 fixed.push(listIndex === prevListIndex);
319
320 if (prevListIndex !== listIndex) {
321 changed.push([prevListIndex, listIndex]);
322 }
323 }
324 }); // Sort by ascending order of 'to(list's index).
325
326 removed.reverse();
327 return new Result(prevList, list, added, removed, changed, maintained, changedBeforeAdded, fixed);
328 }
329
330 /**
331 * A module that checks diff when values are added, removed, or changed in an array.
332 * @ko 배열 또는 오브젝트에서 값이 추가되거나 삭제되거나 순서가 변경사항을 체크하는 모듈입니다.
333 * @memberof eg
334 */
335
336 var ListDiffer =
337 /*#__PURE__*/
338 function () {
339 /**
340 * @param - Initializing Data Array. <ko> 초기 설정할 데이터 배열.</ko>
341 * @param - This callback function returns the key of the item. <ko> 아이템의 키를 반환하는 콜백 함수입니다.</ko>
342 * @example
343 * import ListDiffer from "@egjs/list-differ";
344 * // script => eg.ListDiffer
345 * const differ = new ListDiffer([0, 1, 2, 3, 4, 5], e => e);
346 * const result = differ.update([7, 8, 0, 4, 3, 6, 2, 1]);
347 * // List before update
348 * // [1, 2, 3, 4, 5]
349 * console.log(result.prevList);
350 * // Updated list
351 * // [4, 3, 6, 2, 1]
352 * console.log(result.list);
353 * // Index array of values added to `list`.
354 * // [0, 1, 5]
355 * console.log(result.added);
356 * // Index array of values removed in `prevList`.
357 * // [5]
358 * console.log(result.removed);
359 * // An array of index pairs of `prevList` and `list` with different indexes from `prevList` and `list`.
360 * // [[0, 2], [4, 3], [3, 4], [2, 6], [1, 7]]
361 * console.log(result.changed);
362 * // The subset of `changed` and an array of index pairs that moved data directly. Indicate an array of absolute index pairs of `ordered`.(Formatted by: Array<[index of prevList, index of list]>)
363 * // [[4, 3], [3, 4], [2, 6]]
364 * console.log(result.pureChanged);
365 * // An array of index pairs to be `ordered` that can synchronize `list` before adding data. (Formatted by: Array<[prevIndex, nextIndex]>)
366 * // [[4, 1], [4, 2], [4, 3]]
367 * console.log(result.ordered);
368 * // An array of index pairs of `prevList` and `list` that have not been added/removed so data is preserved.
369 * // [[0, 2], [4, 3], [3, 4], [2, 6], [1, 7]]
370 * console.log(result.maintained);
371 */
372 function ListDiffer(list, findKeyCallback) {
373 if (list === void 0) {
374 list = [];
375 }
376
377 this.findKeyCallback = findKeyCallback;
378 this.list = [].slice.call(list);
379 }
380 /**
381 * Update list.
382 * @ko 리스트를 업데이트를 합니다.
383 * @param - List to update <ko> 업데이트할 리스트 </ko>
384 * @return - Returns the results of an update from `prevList` to `list`.<ko> `prevList`에서 `list`로 업데이트한 결과를 반환한다. </ko>
385 */
386
387
388 var __proto = ListDiffer.prototype;
389
390 __proto.update = function (list) {
391 var newData = [].slice.call(list);
392 var result = diff(this.list, newData, this.findKeyCallback);
393 this.list = newData;
394 return result;
395 };
396
397 return ListDiffer;
398 }();
399
400 /*
401 egjs-list-differ
402 Copyright (c) 2019-present NAVER Corp.
403 MIT license
404 */
405
406 /*
407 egjs-list-differ
408 Copyright (c) 2019-present NAVER Corp.
409 MIT license
410 */
411 ListDiffer.diff = diff;
412
413 return ListDiffer;
414
415}));
416//# sourceMappingURL=list-differ.js.map