@tim-code/my-util 0.4.10 → 0.5.1
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/find.js +56 -26
- package/src/find.test.js +64 -20
package/package.json
CHANGED
package/src/find.js
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function findEq(array, desired, { key } = {}) {
|
|
2
|
+
if (typeof key === "function") {
|
|
3
|
+
for (let i = 0; i < array.length; i++) {
|
|
4
|
+
const element = array[i]
|
|
5
|
+
const value = key(element, i, array)
|
|
6
|
+
if (value === desired) {
|
|
7
|
+
return value
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
11
|
+
for (const element of array) {
|
|
12
|
+
const value = element[key]
|
|
13
|
+
if (value === desired) {
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
for (const value of array) {
|
|
19
|
+
if (value === desired) {
|
|
20
|
+
return value
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function findSmallestDiff(array, desired, { key, cutoff = Infinity } = {}) {
|
|
2
28
|
let closest
|
|
3
29
|
if (typeof key === "function") {
|
|
4
30
|
for (let i = 0; i < array.length; i++) {
|
|
5
31
|
const element = array[i]
|
|
6
32
|
const value = key(element, i, array)
|
|
7
33
|
const diff = Math.abs(value - desired)
|
|
8
|
-
if (diff < cutoff) {
|
|
34
|
+
if (diff < cutoff || (diff === cutoff && !closest)) {
|
|
9
35
|
closest = element
|
|
10
36
|
cutoff = diff
|
|
11
37
|
}
|
|
@@ -14,7 +40,7 @@ export function findClosestAbs(array, desired, { key, cutoff = Infinity } = {})
|
|
|
14
40
|
for (const element of array) {
|
|
15
41
|
const value = element[key]
|
|
16
42
|
const diff = Math.abs(value - desired)
|
|
17
|
-
if (diff < cutoff) {
|
|
43
|
+
if (diff < cutoff || (diff === cutoff && !closest)) {
|
|
18
44
|
closest = element
|
|
19
45
|
cutoff = diff
|
|
20
46
|
}
|
|
@@ -22,7 +48,7 @@ export function findClosestAbs(array, desired, { key, cutoff = Infinity } = {})
|
|
|
22
48
|
} else {
|
|
23
49
|
for (const value of array) {
|
|
24
50
|
const diff = Math.abs(value - desired)
|
|
25
|
-
if (diff < cutoff) {
|
|
51
|
+
if (diff < cutoff || (diff === cutoff && !closest)) {
|
|
26
52
|
closest = value
|
|
27
53
|
cutoff = diff
|
|
28
54
|
}
|
|
@@ -37,7 +63,7 @@ export function findClosestLT(array, desired, { key, cutoff = -Infinity } = {})
|
|
|
37
63
|
for (let i = 0; i < array.length; i++) {
|
|
38
64
|
const element = array[i]
|
|
39
65
|
const value = key(element, i, array)
|
|
40
|
-
if (value < desired && value > cutoff) {
|
|
66
|
+
if (value < desired && (value > cutoff || (value === cutoff && !closest))) {
|
|
41
67
|
closest = element
|
|
42
68
|
cutoff = value
|
|
43
69
|
}
|
|
@@ -45,14 +71,14 @@ export function findClosestLT(array, desired, { key, cutoff = -Infinity } = {})
|
|
|
45
71
|
} else if (typeof key === "number" || typeof key === "string") {
|
|
46
72
|
for (const element of array) {
|
|
47
73
|
const value = element[key]
|
|
48
|
-
if (value < desired && value > cutoff) {
|
|
74
|
+
if (value < desired && (value > cutoff || (value === cutoff && !closest))) {
|
|
49
75
|
closest = element
|
|
50
76
|
cutoff = value
|
|
51
77
|
}
|
|
52
78
|
}
|
|
53
79
|
} else {
|
|
54
80
|
for (const value of array) {
|
|
55
|
-
if (value < desired && value > cutoff) {
|
|
81
|
+
if (value < desired && (value > cutoff || (value === cutoff && !closest))) {
|
|
56
82
|
closest = value
|
|
57
83
|
cutoff = value
|
|
58
84
|
}
|
|
@@ -67,7 +93,7 @@ export function findClosestLTE(array, desired, { key, cutoff = -Infinity } = {})
|
|
|
67
93
|
for (let i = 0; i < array.length; i++) {
|
|
68
94
|
const element = array[i]
|
|
69
95
|
const value = key(element, i, array)
|
|
70
|
-
if (value <= desired && value > cutoff) {
|
|
96
|
+
if (value <= desired && (value > cutoff || (value === cutoff && !closest))) {
|
|
71
97
|
closest = element
|
|
72
98
|
cutoff = value
|
|
73
99
|
}
|
|
@@ -75,14 +101,14 @@ export function findClosestLTE(array, desired, { key, cutoff = -Infinity } = {})
|
|
|
75
101
|
} else if (typeof key === "number" || typeof key === "string") {
|
|
76
102
|
for (const element of array) {
|
|
77
103
|
const value = element[key]
|
|
78
|
-
if (value <= desired && value > cutoff) {
|
|
104
|
+
if (value <= desired && (value > cutoff || (value === cutoff && !closest))) {
|
|
79
105
|
closest = element
|
|
80
106
|
cutoff = value
|
|
81
107
|
}
|
|
82
108
|
}
|
|
83
109
|
} else {
|
|
84
110
|
for (const value of array) {
|
|
85
|
-
if (value <= desired && value > cutoff) {
|
|
111
|
+
if (value <= desired && (value > cutoff || (value === cutoff && !closest))) {
|
|
86
112
|
closest = value
|
|
87
113
|
cutoff = value
|
|
88
114
|
}
|
|
@@ -97,7 +123,7 @@ export function findClosestGT(array, desired, { key, cutoff = Infinity } = {}) {
|
|
|
97
123
|
for (let i = 0; i < array.length; i++) {
|
|
98
124
|
const element = array[i]
|
|
99
125
|
const value = key(element, i, array)
|
|
100
|
-
if (value > desired && value < cutoff) {
|
|
126
|
+
if (value > desired && (value < cutoff || (value === cutoff && !closest))) {
|
|
101
127
|
closest = element
|
|
102
128
|
cutoff = value
|
|
103
129
|
}
|
|
@@ -105,14 +131,14 @@ export function findClosestGT(array, desired, { key, cutoff = Infinity } = {}) {
|
|
|
105
131
|
} else if (typeof key === "number" || typeof key === "string") {
|
|
106
132
|
for (const element of array) {
|
|
107
133
|
const value = element[key]
|
|
108
|
-
if (value > desired && value < cutoff) {
|
|
134
|
+
if (value > desired && (value < cutoff || (value === cutoff && !closest))) {
|
|
109
135
|
closest = element
|
|
110
136
|
cutoff = value
|
|
111
137
|
}
|
|
112
138
|
}
|
|
113
139
|
} else {
|
|
114
140
|
for (const value of array) {
|
|
115
|
-
if (value > desired && value < cutoff) {
|
|
141
|
+
if (value > desired && (value < cutoff || (value === cutoff && !closest))) {
|
|
116
142
|
closest = value
|
|
117
143
|
cutoff = value
|
|
118
144
|
}
|
|
@@ -127,7 +153,7 @@ export function findClosestGTE(array, desired, { key, cutoff = Infinity } = {})
|
|
|
127
153
|
for (let i = 0; i < array.length; i++) {
|
|
128
154
|
const element = array[i]
|
|
129
155
|
const value = key(element, i, array)
|
|
130
|
-
if (value >= desired && value < cutoff) {
|
|
156
|
+
if (value >= desired && (value < cutoff || (value === cutoff && !closest))) {
|
|
131
157
|
closest = element
|
|
132
158
|
cutoff = value
|
|
133
159
|
}
|
|
@@ -135,14 +161,14 @@ export function findClosestGTE(array, desired, { key, cutoff = Infinity } = {})
|
|
|
135
161
|
} else if (typeof key === "number" || typeof key === "string") {
|
|
136
162
|
for (const element of array) {
|
|
137
163
|
const value = element[key]
|
|
138
|
-
if (value >= desired && value < cutoff) {
|
|
164
|
+
if (value >= desired && (value < cutoff || (value === cutoff && !closest))) {
|
|
139
165
|
closest = element
|
|
140
166
|
cutoff = value
|
|
141
167
|
}
|
|
142
168
|
}
|
|
143
169
|
} else {
|
|
144
170
|
for (const value of array) {
|
|
145
|
-
if (value >= desired && value < cutoff) {
|
|
171
|
+
if (value >= desired && (value < cutoff || (value === cutoff && !closest))) {
|
|
146
172
|
closest = value
|
|
147
173
|
cutoff = value
|
|
148
174
|
}
|
|
@@ -163,14 +189,16 @@ export function findClosestGTE(array, desired, { key, cutoff = Infinity } = {})
|
|
|
163
189
|
* @param {string|number|Function=} options.key
|
|
164
190
|
* If specified, will consider the value for each element's key instead of the element itself.
|
|
165
191
|
* If a function, called with the element, index and array (same as .map() callback) to produce the value to sort on.
|
|
166
|
-
* @param {string=} options.comparator "
|
|
192
|
+
* @param {string=} options.comparator "diff", "lt", "lte", "gt", "gte", "eq". Default is "diff" which implies T is number.
|
|
167
193
|
* @param {V=} options.cutoff If specified, sets a initial constraint on how close the found value must be.
|
|
168
|
-
*
|
|
169
|
-
* If used with
|
|
194
|
+
* If used with lt, lte, value must be greater than or equal to cutoff.
|
|
195
|
+
* If used with gt, gte, value must be less than or equal to cutoff.
|
|
196
|
+
* If used with diff, value's difference with desired must be less than or equal to cutoff.
|
|
197
|
+
* No effect with eq.
|
|
170
198
|
* @returns {T|undefined}
|
|
171
199
|
*/
|
|
172
200
|
export function findClosest(array, value, options = {}) {
|
|
173
|
-
const { comparator = "
|
|
201
|
+
const { comparator = "diff" } = options
|
|
174
202
|
switch (comparator) {
|
|
175
203
|
case "lt":
|
|
176
204
|
return findClosestLT(array, value, options)
|
|
@@ -180,15 +208,17 @@ export function findClosest(array, value, options = {}) {
|
|
|
180
208
|
return findClosestGT(array, value, options)
|
|
181
209
|
case "gte":
|
|
182
210
|
return findClosestGTE(array, value, options)
|
|
183
|
-
case "
|
|
184
|
-
return
|
|
211
|
+
case "diff":
|
|
212
|
+
return findSmallestDiff(array, value, options)
|
|
213
|
+
case "eq":
|
|
214
|
+
return findEq(array, value, options)
|
|
185
215
|
default:
|
|
186
216
|
throw new Error(`unknown comparator: ${comparator}`)
|
|
187
217
|
}
|
|
188
218
|
}
|
|
189
219
|
|
|
190
220
|
/**
|
|
191
|
-
* Find the minimum value in an array.
|
|
221
|
+
* Find the minimum value in an array. undefined or null values are ignored.
|
|
192
222
|
* @template T, V
|
|
193
223
|
* @param {Array<T>} array
|
|
194
224
|
* @param {Object} $1
|
|
@@ -196,7 +226,7 @@ export function findClosest(array, value, options = {}) {
|
|
|
196
226
|
* If string, then accesses each element at that key to get value.
|
|
197
227
|
* If function, then calls the callback on each element to get value.
|
|
198
228
|
* @param {V=} $1.cutoff Only values below cutoff will be considered.
|
|
199
|
-
* @returns {T}
|
|
229
|
+
* @returns {T|undefined}
|
|
200
230
|
*/
|
|
201
231
|
export function findMin(array, { key, cutoff = Infinity } = {}) {
|
|
202
232
|
let closest
|
|
@@ -229,7 +259,7 @@ export function findMin(array, { key, cutoff = Infinity } = {}) {
|
|
|
229
259
|
}
|
|
230
260
|
|
|
231
261
|
/**
|
|
232
|
-
* Find the maximum value in an array.
|
|
262
|
+
* Find the maximum value in an array. undefined or null values are ignored.
|
|
233
263
|
* @template T, V
|
|
234
264
|
* @param {Array<T>} array
|
|
235
265
|
* @param {Object} $1
|
|
@@ -237,7 +267,7 @@ export function findMin(array, { key, cutoff = Infinity } = {}) {
|
|
|
237
267
|
* If string, then accesses each element at that key to get value.
|
|
238
268
|
* If function, then calls the callback on each element to get value.
|
|
239
269
|
* @param {V=} $1.cutoff Only values above cutoff will be considered.
|
|
240
|
-
* @returns {T}
|
|
270
|
+
* @returns {T|undefined}
|
|
241
271
|
*/
|
|
242
272
|
export function findMax(array, { key, cutoff = -Infinity } = {}) {
|
|
243
273
|
let closest
|
package/src/find.test.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { describe, expect, it } from "@jest/globals"
|
|
3
3
|
|
|
4
4
|
const {
|
|
5
|
-
|
|
5
|
+
findEq,
|
|
6
|
+
findSmallestDiff,
|
|
6
7
|
findClosestLT,
|
|
7
8
|
findClosestLTE,
|
|
8
9
|
findClosestGT,
|
|
@@ -13,39 +14,75 @@ const {
|
|
|
13
14
|
findTruthy,
|
|
14
15
|
} = await import("./find.js")
|
|
15
16
|
|
|
16
|
-
describe("
|
|
17
|
+
describe("findEq", () => {
|
|
18
|
+
it("returns the first element equal to desired (no key)", () => {
|
|
19
|
+
expect(findEq([1, 2, 3, 2], 2)).toBe(2)
|
|
20
|
+
expect(findEq([1, 2, 3], 4)).toBeUndefined()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it("returns the first value from key function equal to desired", () => {
|
|
24
|
+
const arr = [{ v: 1 }, { v: 2 }, { v: 3 }]
|
|
25
|
+
expect(findEq(arr, 2, { key: (e) => e.v })).toBe(2)
|
|
26
|
+
expect(findEq(arr, 4, { key: (e) => e.v })).toBeUndefined()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it("returns the first value from key string equal to desired", () => {
|
|
30
|
+
const arr = [{ x: 1 }, { x: 2 }, { x: 3 }]
|
|
31
|
+
expect(findEq(arr, 2, { key: "x" })).toBe(2)
|
|
32
|
+
expect(findEq(arr, 4, { key: "x" })).toBeUndefined()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("returns the first value from key number equal to desired", () => {
|
|
36
|
+
const arr = [[1], [2], [3]]
|
|
37
|
+
expect(findEq(arr, 2, { key: 0 })).toBe(2)
|
|
38
|
+
expect(findEq(arr, 4, { key: 0 })).toBeUndefined()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("returns undefined for empty array", () => {
|
|
42
|
+
expect(findEq([], 1)).toBeUndefined()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("returns first matching value if there are duplicates", () => {
|
|
46
|
+
expect(findEq([2, 2, 3], 2)).toBe(2)
|
|
47
|
+
const arr = [{ v: 2 }, { v: 2 }]
|
|
48
|
+
expect(findEq(arr, 2, { key: (e) => e.v })).toBe(2)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe("findSmallestDiff", () => {
|
|
17
53
|
it("returns the element closest in absolute value to desired", () => {
|
|
18
|
-
expect(
|
|
19
|
-
expect(
|
|
20
|
-
expect(
|
|
54
|
+
expect(findSmallestDiff([1, 5, 9], 6)).toBe(5)
|
|
55
|
+
expect(findSmallestDiff([1, 5, 9], 8)).toBe(9)
|
|
56
|
+
expect(findSmallestDiff([1, 5, 9], 1)).toBe(1)
|
|
21
57
|
})
|
|
22
58
|
|
|
23
59
|
it("returns the first element in case of tie", () => {
|
|
24
|
-
expect(
|
|
60
|
+
expect(findSmallestDiff([4, 8], 6)).toBe(4)
|
|
25
61
|
})
|
|
26
62
|
|
|
27
63
|
it("returns undefined for empty array", () => {
|
|
28
|
-
expect(
|
|
64
|
+
expect(findSmallestDiff([], 10)).toBeUndefined()
|
|
29
65
|
})
|
|
30
66
|
|
|
31
67
|
it("supports key as function", () => {
|
|
32
68
|
const arr = [{ v: 2 }, { v: 8 }]
|
|
33
|
-
expect(
|
|
69
|
+
expect(findSmallestDiff(arr, 5, { key: (e) => e.v })).toEqual({ v: 2 })
|
|
34
70
|
})
|
|
35
71
|
|
|
36
72
|
it("supports key as string", () => {
|
|
37
73
|
const arr = [{ x: 1 }, { x: 10 }]
|
|
38
|
-
expect(
|
|
74
|
+
expect(findSmallestDiff(arr, 8, { key: "x" })).toEqual({ x: 10 })
|
|
39
75
|
})
|
|
40
76
|
|
|
41
77
|
it("supports key as number", () => {
|
|
42
78
|
const arr = [[2], [8]]
|
|
43
|
-
expect(
|
|
79
|
+
expect(findSmallestDiff(arr, 7, { key: 0 })).toEqual([8])
|
|
44
80
|
})
|
|
45
81
|
|
|
46
82
|
it("respects cutoff", () => {
|
|
47
|
-
expect(
|
|
48
|
-
expect(
|
|
83
|
+
expect(findSmallestDiff([1, 5, 9], 6, { cutoff: 2 })).toBe(5)
|
|
84
|
+
expect(findSmallestDiff([1, 5, 9], 6, { cutoff: 1 })).toBe(5)
|
|
85
|
+
expect(findSmallestDiff([1, 5, 9], 6, { cutoff: 0 })).toBeUndefined()
|
|
49
86
|
})
|
|
50
87
|
})
|
|
51
88
|
|
|
@@ -81,7 +118,8 @@ describe("findClosestLT", () => {
|
|
|
81
118
|
|
|
82
119
|
it("respects cutoff", () => {
|
|
83
120
|
expect(findClosestLT([1, 5, 9], 6, { cutoff: 4 })).toBe(5)
|
|
84
|
-
expect(findClosestLT([1, 5, 9], 6, { cutoff: 5 })).
|
|
121
|
+
expect(findClosestLT([1, 5, 9], 6, { cutoff: 5 })).toBe(5)
|
|
122
|
+
expect(findClosestLT([1, 5, 9], 6, { cutoff: 6 })).toBe(undefined)
|
|
85
123
|
})
|
|
86
124
|
})
|
|
87
125
|
|
|
@@ -118,7 +156,8 @@ describe("findClosestLTE", () => {
|
|
|
118
156
|
|
|
119
157
|
it("respects cutoff", () => {
|
|
120
158
|
expect(findClosestLTE([1, 5, 9], 6, { cutoff: 4 })).toBe(5)
|
|
121
|
-
expect(findClosestLTE([1, 5, 9], 6, { cutoff: 5 })).
|
|
159
|
+
expect(findClosestLTE([1, 5, 9], 6, { cutoff: 5 })).toBe(5)
|
|
160
|
+
expect(findClosestLTE([1, 5, 9], 6, { cutoff: 6 })).toBe(undefined)
|
|
122
161
|
})
|
|
123
162
|
})
|
|
124
163
|
|
|
@@ -153,8 +192,9 @@ describe("findClosestGT", () => {
|
|
|
153
192
|
})
|
|
154
193
|
|
|
155
194
|
it("respects cutoff", () => {
|
|
156
|
-
expect(
|
|
157
|
-
expect(
|
|
195
|
+
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 10 })).toBe(9)
|
|
196
|
+
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 9 })).toBe(9)
|
|
197
|
+
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 8 })).toBeUndefined()
|
|
158
198
|
})
|
|
159
199
|
})
|
|
160
200
|
|
|
@@ -189,13 +229,14 @@ describe("findClosestGTE", () => {
|
|
|
189
229
|
})
|
|
190
230
|
|
|
191
231
|
it("respects cutoff", () => {
|
|
232
|
+
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 10 })).toBe(9)
|
|
233
|
+
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 9 })).toBe(9)
|
|
192
234
|
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 8 })).toBeUndefined()
|
|
193
|
-
expect(findClosestGTE([1, 5, 9], 4, { cutoff: 8 })).toBe(5)
|
|
194
235
|
})
|
|
195
236
|
})
|
|
196
237
|
|
|
197
238
|
describe("findClosest", () => {
|
|
198
|
-
it("defaults to
|
|
239
|
+
it("defaults to diff comparator", () => {
|
|
199
240
|
expect(findClosest([1, 5, 9], 6)).toBe(5)
|
|
200
241
|
})
|
|
201
242
|
|
|
@@ -204,7 +245,9 @@ describe("findClosest", () => {
|
|
|
204
245
|
expect(findClosest([1, 5, 9], 6, { comparator: "lte" })).toBe(5)
|
|
205
246
|
expect(findClosest([1, 5, 9], 6, { comparator: "gt" })).toBe(9)
|
|
206
247
|
expect(findClosest([1, 5, 9], 6, { comparator: "gte" })).toBe(9)
|
|
207
|
-
expect(findClosest([1, 5, 9], 6, { comparator: "
|
|
248
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "diff" })).toBe(5)
|
|
249
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "eq" })).toBeUndefined()
|
|
250
|
+
expect(findClosest([1, 5, 9], 5, { comparator: "eq" })).toBe(5)
|
|
208
251
|
})
|
|
209
252
|
|
|
210
253
|
it("throws on unknown comparator", () => {
|
|
@@ -215,7 +258,8 @@ describe("findClosest", () => {
|
|
|
215
258
|
|
|
216
259
|
it("passes options to underlying function", () => {
|
|
217
260
|
const arr = [{ x: 1 }, { x: 10 }]
|
|
218
|
-
expect(findClosest(arr, 8, { comparator: "
|
|
261
|
+
expect(findClosest(arr, 8, { comparator: "diff", key: "x" })).toEqual({ x: 10 })
|
|
262
|
+
expect(findClosest(arr, 10, { comparator: "eq", key: "x" })).toBe(10)
|
|
219
263
|
})
|
|
220
264
|
})
|
|
221
265
|
|