Source: utils.ts

  1. /*
  2. egjs-list-differ
  3. Copyright (c) 2019-present NAVER Corp.
  4. MIT license
  5. */
  6. import { MapInteface, DiffResult } from "./types";
  7. import PolyMap from "./PolyMap";
  8. import HashMap from "./HashMap";
  9. import { SUPPORT_MAP } from "./consts";
  10. import Result from "./Result";
  11. /**
  12. *
  13. * @memberof eg.ListDiffer
  14. * @static
  15. * @function
  16. * @param - Previous List <ko> 이전 목록 </ko>
  17. * @param - List to Update <ko> 업데이트 할 목록 </ko>
  18. * @param - This callback function returns the key of the item. <ko> 아이템의 키를 반환하는 콜백 함수입니다.</ko>
  19. * @return - Returns the diff between `prevList` and `list` <ko> `prevList`와 `list`의 다른 점을 반환한다.</ko>
  20. * @example
  21. * import { diff } from "@egjs/list-differ";
  22. * // script => eg.ListDiffer.diff
  23. * const result = diff([0, 1, 2, 3, 4, 5], [7, 8, 0, 4, 3, 6, 2, 1], e => e);
  24. * // List before update
  25. * // [1, 2, 3, 4, 5]
  26. * console.log(result.prevList);
  27. * // Updated list
  28. * // [4, 3, 6, 2, 1]
  29. * console.log(result.list);
  30. * // Index array of values added to `list`
  31. * // [0, 1, 5]
  32. * console.log(result.added);
  33. * // Index array of values removed in `prevList`
  34. * // [5]
  35. * console.log(result.removed);
  36. * // An array of index pairs of `prevList` and `list` with different indexes from `prevList` and `list`
  37. * // [[0, 2], [4, 3], [3, 4], [2, 6], [1, 7]]
  38. * console.log(result.changed);
  39. * // 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]>)
  40. * // [[4, 3], [3, 4], [2, 6]]
  41. * console.log(result.pureChanged);
  42. * // An array of index pairs to be `ordered` that can synchronize `list` before adding data. (Formatted by: Array<[prevIndex, nextIndex]>)
  43. * // [[4, 1], [4, 2], [4, 3]]
  44. * console.log(result.ordered);
  45. * // An array of index pairs of `prevList` and `list` that have not been added/removed so data is preserved
  46. * // [[0, 2], [4, 3], [3, 4], [2, 6], [1, 7]]
  47. * console.log(result.maintained);
  48. */
  49. export function diff<T>(
  50. prevList: T[],
  51. list: T[],
  52. findKeyCallback?: (e: T, i: number, arr: T[]) => any
  53. ): DiffResult<T> {
  54. const mapClass: new () => MapInteface<any, number> = SUPPORT_MAP ? Map : (findKeyCallback ? HashMap : PolyMap);
  55. const callback = findKeyCallback || ((e: T) => e);
  56. const added: number[] = [];
  57. const removed: number[] = [];
  58. const maintained: number[][] = [];
  59. const prevKeys = prevList.map(callback);
  60. const keys = list.map(callback);
  61. const prevKeyMap: MapInteface<any, number> = new mapClass();
  62. const keyMap: MapInteface<any, number> = new mapClass();
  63. const changedBeforeAdded: number[][] = [];
  64. const fixed: boolean[] = [];
  65. const removedMap: object = {};
  66. let changed: number[][] = [];
  67. let addedCount = 0;
  68. let removedCount = 0;
  69. // Add prevKeys and keys to the hashmap.
  70. prevKeys.forEach((key, prevListIndex) => {
  71. prevKeyMap.set(key, prevListIndex);
  72. });
  73. keys.forEach((key, listIndex) => {
  74. keyMap.set(key, listIndex);
  75. });
  76. // Compare `prevKeys` and `keys` and add them to `removed` if they are not in `keys`.
  77. prevKeys.forEach((key, prevListIndex) => {
  78. const listIndex = keyMap.get(key);
  79. // In prevList, but not in list, it is removed.
  80. if (typeof listIndex === "undefined") {
  81. ++removedCount;
  82. removed.push(prevListIndex);
  83. } else {
  84. removedMap[listIndex] = removedCount;
  85. }
  86. });
  87. // Compare `prevKeys` and `keys` and add them to `added` if they are not in `prevKeys`.
  88. keys.forEach((key, listIndex) => {
  89. const prevListIndex = prevKeyMap.get(key);
  90. // In list, but not in prevList, it is added.
  91. if (typeof prevListIndex === "undefined") {
  92. added.push(listIndex);
  93. ++addedCount;
  94. } else {
  95. maintained.push([prevListIndex, listIndex]);
  96. removedCount = removedMap[listIndex] || 0;
  97. changedBeforeAdded.push([
  98. prevListIndex - removedCount,
  99. listIndex - addedCount,
  100. ]);
  101. fixed.push(listIndex === prevListIndex);
  102. if (prevListIndex !== listIndex) {
  103. changed.push([prevListIndex, listIndex]);
  104. }
  105. }
  106. });
  107. // Sort by ascending order of 'to(list's index).
  108. removed.reverse();
  109. return new Result(
  110. prevList,
  111. list,
  112. added,
  113. removed,
  114. changed,
  115. maintained,
  116. changedBeforeAdded,
  117. fixed,
  118. );
  119. }
comments powered by Disqus