@tim-code/my-util 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/array.js +29 -3
- package/src/array.test.js +92 -0
- package/src/object.js +4 -6
- package/src/object.test.js +51 -16
package/package.json
CHANGED
package/src/array.js
CHANGED
|
@@ -20,12 +20,38 @@ export function chunk(array, chunkSize = array.length) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Returns all unique elements in an array, or alternatively by checking a key's value or function's result.
|
|
24
24
|
* @param {Array} array
|
|
25
|
+
* @param {Object} $1
|
|
26
|
+
* @param {string|number|Function=} $1.key
|
|
27
|
+
* If a function, calls the provided function on an element to get the value to check for uniqueness.
|
|
28
|
+
* If a string or number, checks each element's value at key for uniqueness.
|
|
25
29
|
* @returns {Array}
|
|
26
30
|
*/
|
|
27
|
-
export function unique(array) {
|
|
28
|
-
|
|
31
|
+
export function unique(array, { key } = {}) {
|
|
32
|
+
const seen = new Set()
|
|
33
|
+
const result = []
|
|
34
|
+
if (typeof key === "function") {
|
|
35
|
+
for (let i = 0; i < array.length; i++) {
|
|
36
|
+
const element = array[i]
|
|
37
|
+
const value = key(element, i, array)
|
|
38
|
+
if (!seen.has(value)) {
|
|
39
|
+
seen.add(value)
|
|
40
|
+
result.push(element)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else if (typeof key === "string" || typeof key === "number") {
|
|
44
|
+
for (const element of array) {
|
|
45
|
+
const value = element[key]
|
|
46
|
+
if (!seen.has(value)) {
|
|
47
|
+
seen.add(value)
|
|
48
|
+
result.push(element)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
return [...new Set(array)]
|
|
53
|
+
}
|
|
54
|
+
return result
|
|
29
55
|
}
|
|
30
56
|
|
|
31
57
|
// sorts undefined and null to the end if applicable
|
package/src/array.test.js
CHANGED
|
@@ -68,6 +68,98 @@ describe("unique", () => {
|
|
|
68
68
|
const b = {}
|
|
69
69
|
expect(unique([a, b, a])).toEqual([a, b])
|
|
70
70
|
})
|
|
71
|
+
|
|
72
|
+
it("returns unique elements by key (string)", () => {
|
|
73
|
+
const arr = [
|
|
74
|
+
{ id: 1, name: "a" },
|
|
75
|
+
{ id: 2, name: "b" },
|
|
76
|
+
{ id: 1, name: "c" },
|
|
77
|
+
{ id: 3, name: "d" },
|
|
78
|
+
{ id: 2, name: "e" },
|
|
79
|
+
]
|
|
80
|
+
expect(unique(arr, { key: "id" })).toEqual([
|
|
81
|
+
{ id: 1, name: "a" },
|
|
82
|
+
{ id: 2, name: "b" },
|
|
83
|
+
{ id: 3, name: "d" },
|
|
84
|
+
])
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it("returns unique elements by key (number)", () => {
|
|
88
|
+
const arr = [
|
|
89
|
+
{ 0: "a", v: 1 },
|
|
90
|
+
{ 0: "b", v: 2 },
|
|
91
|
+
{ 0: "a", v: 3 },
|
|
92
|
+
{ 0: "c", v: 4 },
|
|
93
|
+
]
|
|
94
|
+
expect(unique(arr, { key: 0 })).toEqual([
|
|
95
|
+
{ 0: "a", v: 1 },
|
|
96
|
+
{ 0: "b", v: 2 },
|
|
97
|
+
{ 0: "c", v: 4 },
|
|
98
|
+
])
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("returns unique elements by function", () => {
|
|
102
|
+
const arr = [
|
|
103
|
+
{ id: 1, name: "a" },
|
|
104
|
+
{ id: 2, name: "b" },
|
|
105
|
+
{ id: 1, name: "c" },
|
|
106
|
+
{ id: 3, name: "d" },
|
|
107
|
+
{ id: 2, name: "e" },
|
|
108
|
+
]
|
|
109
|
+
expect(
|
|
110
|
+
unique(arr, {
|
|
111
|
+
key: (el) => el.id % 2, // group by odd/even id
|
|
112
|
+
})
|
|
113
|
+
).toEqual([
|
|
114
|
+
{ id: 1, name: "a" }, // id % 2 === 1
|
|
115
|
+
{ id: 2, name: "b" }, // id % 2 === 0
|
|
116
|
+
])
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it("returns unique elements by function using index and array", () => {
|
|
120
|
+
const arr = ["a", "b", "c", "a"]
|
|
121
|
+
expect(
|
|
122
|
+
unique(arr, {
|
|
123
|
+
key: (el, i, array) => array.indexOf(el), // only first occurrence is unique
|
|
124
|
+
})
|
|
125
|
+
).toEqual(["a", "b", "c"])
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it("returns unique elements by key when some elements lack the key", () => {
|
|
129
|
+
const arr = [{ id: 1 }, {}, { id: 1 }, { id: 2 }, {}]
|
|
130
|
+
expect(unique(arr, { key: "id" })).toEqual([{ id: 1 }, {}, { id: 2 }])
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("returns unique elements by function when function returns undefined/null", () => {
|
|
134
|
+
const arr = [{ id: 1 }, {}, { id: 2 }, { id: null }, {}]
|
|
135
|
+
expect(
|
|
136
|
+
unique(arr, {
|
|
137
|
+
key: (el) => el.id,
|
|
138
|
+
})
|
|
139
|
+
).toEqual([{ id: 1 }, {}, { id: 2 }, { id: null }])
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it("returns unique elements by key when key value is undefined/null", () => {
|
|
143
|
+
const arr = [{ id: 1 }, { id: undefined }, { id: 2 }, { id: null }, { id: undefined }]
|
|
144
|
+
expect(unique(arr, { key: "id" })).toEqual([
|
|
145
|
+
{ id: 1 },
|
|
146
|
+
{ id: undefined },
|
|
147
|
+
{ id: 2 },
|
|
148
|
+
{ id: null },
|
|
149
|
+
])
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it("returns unique elements for primitive array if key is not provided", () => {
|
|
153
|
+
expect(unique([1, 2, 2, 3], {})).toEqual([1, 2, 3])
|
|
154
|
+
expect(unique([1, 2, 2, 3], { key: undefined })).toEqual([1, 2, 3])
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it("returns unique elements for object array if key is not provided", () => {
|
|
158
|
+
const a = { x: 1 }
|
|
159
|
+
const b = { x: 2 }
|
|
160
|
+
expect(unique([a, b, a], {})).toEqual([a, b])
|
|
161
|
+
expect(unique([a, b, a], { key: undefined })).toEqual([a, b])
|
|
162
|
+
})
|
|
71
163
|
})
|
|
72
164
|
|
|
73
165
|
describe("ascending", () => {
|
package/src/object.js
CHANGED
|
@@ -135,16 +135,14 @@ export function deepMerge(target, ...sources) {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
|
-
*
|
|
138
|
+
* Merges a deep copy of each source object into target. See deepCopy() and deepMerge() documentation for caveats.
|
|
139
|
+
* @param {Object} target The target object that will receive the merged properties.
|
|
139
140
|
* @param {...Object} sources The source objects whose properties will be merged into the returned object
|
|
140
141
|
* @returns {Object}
|
|
141
142
|
*/
|
|
142
|
-
export function deepMergeCopy(...sources) {
|
|
143
|
-
if (!sources.length) {
|
|
144
|
-
return {}
|
|
145
|
-
}
|
|
143
|
+
export function deepMergeCopy(target, ...sources) {
|
|
146
144
|
const copies = sources.map(deepCopy)
|
|
147
|
-
const result = deepMerge(...copies)
|
|
145
|
+
const result = deepMerge(target, ...copies)
|
|
148
146
|
return result
|
|
149
147
|
}
|
|
150
148
|
|
package/src/object.test.js
CHANGED
|
@@ -423,35 +423,70 @@ describe("deepMerge", () => {
|
|
|
423
423
|
|
|
424
424
|
// --- deepMergeCopy ---
|
|
425
425
|
describe("deepMergeCopy", () => {
|
|
426
|
-
it("deeply merges deep copies of sources", () => {
|
|
427
|
-
const
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
expect(merged).
|
|
426
|
+
it("deeply merges deep copies of sources into the target", () => {
|
|
427
|
+
const target = { a: { b: 1 } }
|
|
428
|
+
const s1 = { a: { c: 2 } }
|
|
429
|
+
const s2 = { a: { d: 3 } }
|
|
430
|
+
const origTarget = JSON.stringify(target)
|
|
431
|
+
const merged = deepMergeCopy(target, s1, s2)
|
|
432
|
+
expect(merged).toEqual({ a: { b: 1, c: 2, d: 3 } })
|
|
433
|
+
expect(merged).toBe(target)
|
|
433
434
|
expect(merged.a).not.toBe(s1.a)
|
|
434
435
|
expect(merged.a).not.toBe(s2.a)
|
|
436
|
+
expect(JSON.stringify(target)).not.toBe(origTarget)
|
|
437
|
+
expect(s1).toEqual({ a: { c: 2 } })
|
|
438
|
+
expect(s2).toEqual({ a: { d: 3 } })
|
|
435
439
|
})
|
|
436
440
|
|
|
437
441
|
it("does not mutate source objects", () => {
|
|
438
|
-
const
|
|
439
|
-
const
|
|
442
|
+
const target = { a: 1 }
|
|
443
|
+
const s1 = { b: 2 }
|
|
444
|
+
const s2 = { c: 3 }
|
|
440
445
|
const orig1 = JSON.stringify(s1)
|
|
441
446
|
const orig2 = JSON.stringify(s2)
|
|
442
|
-
deepMergeCopy(s1, s2)
|
|
447
|
+
deepMergeCopy(target, s1, s2)
|
|
443
448
|
expect(JSON.stringify(s1)).toBe(orig1)
|
|
444
449
|
expect(JSON.stringify(s2)).toBe(orig2)
|
|
445
450
|
})
|
|
446
451
|
|
|
447
|
-
it("handles arrays and primitives", () => {
|
|
448
|
-
const
|
|
449
|
-
const
|
|
450
|
-
expect(deepMergeCopy(
|
|
452
|
+
it("handles arrays and primitives in sources", () => {
|
|
453
|
+
const target = { a: [1, 2] }
|
|
454
|
+
const s1 = { a: [3, 4], b: 5 }
|
|
455
|
+
expect(deepMergeCopy(target, s1)).toEqual({ a: [3, 4], b: 5 })
|
|
451
456
|
})
|
|
452
457
|
|
|
453
|
-
it("returns
|
|
454
|
-
|
|
458
|
+
it("returns the target object", () => {
|
|
459
|
+
const target = { x: 1 }
|
|
460
|
+
const s1 = { y: 2 }
|
|
461
|
+
expect(deepMergeCopy(target, s1)).toBe(target)
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
it("merges nothing if no sources provided (returns target as is)", () => {
|
|
465
|
+
const target = { foo: 1 }
|
|
466
|
+
expect(deepMergeCopy(target)).toBe(target)
|
|
467
|
+
expect(target).toEqual({ foo: 1 })
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
it("returns target if called with no arguments", () => {
|
|
471
|
+
expect(deepMergeCopy({})).toEqual({})
|
|
472
|
+
expect(deepMergeCopy()).toEqual(undefined)
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
it("does not mutate the target if no sources are provided", () => {
|
|
476
|
+
const target = { z: 9 }
|
|
477
|
+
const result = deepMergeCopy(target)
|
|
478
|
+
expect(result).toBe(target)
|
|
479
|
+
expect(result).toEqual({ z: 9 })
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
it("does not merge inherited properties from sources", () => {
|
|
483
|
+
const target = {}
|
|
484
|
+
const proto = { x: 1 }
|
|
485
|
+
const s1 = Object.create(proto)
|
|
486
|
+
s1.a = 2
|
|
487
|
+
deepMergeCopy(target, s1)
|
|
488
|
+
expect(target).toEqual({ a: 2 })
|
|
489
|
+
expect("x" in target).toBe(false)
|
|
455
490
|
})
|
|
456
491
|
})
|
|
457
492
|
|