-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix (runtime):
arraysDiff()
with duplicates (#263)
- Loading branch information
1 parent
05b6f9d
commit c58d78c
Showing
5 changed files
with
286 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { describe, expect, test } from 'vitest' | ||
import { makeCountMap, mapsDiff } from '../utils/maps.js' | ||
|
||
describe('make count map', () => { | ||
test('empty array', () => { | ||
expect(makeCountMap([])).toEqual(new Map()) | ||
}) | ||
|
||
test('array with one item', () => { | ||
expect(makeCountMap(['A'])).toEqual(new Map([['A', 1]])) | ||
}) | ||
|
||
test('array without duplicates', () => { | ||
expect(makeCountMap(['A', 'B', 'C'])).toEqual( | ||
new Map([ | ||
['A', 1], | ||
['B', 1], | ||
['C', 1], | ||
]) | ||
) | ||
}) | ||
|
||
test('array with duplicates', () => { | ||
expect(makeCountMap(['A', 'B', 'A', 'C', 'B', 'B'])).toEqual( | ||
new Map([ | ||
['A', 2], | ||
['B', 3], | ||
['C', 1], | ||
]) | ||
) | ||
}) | ||
}) | ||
|
||
describe('maps diff', () => { | ||
test('empty maps', () => { | ||
const oldMap = new Map() | ||
const newMap = new Map() | ||
|
||
expect(mapsDiff(oldMap, newMap)).toEqual({ | ||
added: [], | ||
removed: [], | ||
updated: [], | ||
}) | ||
}) | ||
|
||
test('maps with the same keys and values', () => { | ||
const oldMap = new Map([ | ||
['a', 1], | ||
['b', 2], | ||
['c', 3], | ||
]) | ||
const newMap = new Map([ | ||
['a', 1], | ||
['b', 2], | ||
['c', 3], | ||
]) | ||
|
||
expect(mapsDiff(oldMap, newMap)).toEqual({ | ||
added: [], | ||
removed: [], | ||
updated: [], | ||
}) | ||
}) | ||
|
||
test('maps with the same keys but different values', () => { | ||
const oldMap = new Map([ | ||
['a', 1], | ||
['b', 2], | ||
['c', 3], | ||
]) | ||
const newMap = new Map([ | ||
['a', 1], | ||
['b', 4], | ||
['c', 3], | ||
]) | ||
|
||
expect(mapsDiff(oldMap, newMap)).toEqual({ | ||
added: [], | ||
removed: [], | ||
updated: ['b'], | ||
}) | ||
}) | ||
|
||
test('maps with different keys', () => { | ||
const oldMap = new Map([ | ||
['a', 1], | ||
['b', 2], | ||
['c', 3], | ||
]) | ||
const newMap = new Map([ | ||
['a', 1], | ||
['b', 2], | ||
['d', 3], | ||
]) | ||
|
||
expect(mapsDiff(oldMap, newMap)).toEqual({ | ||
added: ['d'], | ||
removed: ['c'], | ||
updated: [], | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* Given two maps, returns the keys that have been added, removed or updated. | ||
* The comparison is shallow—only the first level of keys is compared. | ||
* The keys keep their original type. | ||
* | ||
* For example: | ||
* | ||
* Given the following maps: | ||
* | ||
* ```js | ||
* const oldMap = new Map([ | ||
* ['a', 1], | ||
* ['b', 2], | ||
* ['c', 3], | ||
* ]) | ||
* const newMap = new Map([ | ||
* ['a', 1], | ||
* ['b', 4], | ||
* ['d', 5], | ||
* ]) | ||
* ``` | ||
* | ||
* The result will be: | ||
* | ||
* ```js | ||
* mapsDiff(oldMap, newMap) | ||
* // { added: ['d'], removed: ['c'], updated: ['b'] } | ||
* ``` | ||
* | ||
* @param {Map<any, any>} oldMap the old map | ||
* @param {Map<any, any>} newMap the new map | ||
* * @returns {{added: any[], removed: any[], updated: any[]}} | ||
*/ | ||
export function mapsDiff(oldMap, newMap) { | ||
const oldKeys = Array.from(oldMap.keys()) | ||
const newKeys = Array.from(newMap.keys()) | ||
|
||
return { | ||
added: newKeys.filter((key) => !oldMap.has(key)), | ||
removed: oldKeys.filter((key) => !newMap.has(key)), | ||
updated: newKeys.filter( | ||
(key) => oldMap.has(key) && oldMap.get(key) !== newMap.get(key) | ||
), | ||
} | ||
} | ||
|
||
/** | ||
* Creates a `Map` that counts the occurrences of each item in the given array. | ||
* The keys of the `Map` are the items in the array, and the values are the | ||
* number of times each item appears in the array. | ||
* | ||
* A `Map` is used instead of an object because it can store any type of key, | ||
* while an object can only store string keys. Thus, the key type is preserved. | ||
* | ||
* For example, given the array `[A, B, A, C, B, A]`, the object would be: | ||
* ``` | ||
* { | ||
* A: 3, | ||
* B: 2, | ||
* C: 1 | ||
* } | ||
* ``` | ||
* | ||
* @param {any[]} array an array of items | ||
* @returns {Map<any, number>} a map of item counts | ||
*/ | ||
export function makeCountMap(array) { | ||
const map = new Map() | ||
|
||
for (const item of array) { | ||
map.set(item, (map.get(item) || 0) + 1) | ||
} | ||
|
||
return map | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters