@tim-code/my-util 0.1.3 → 0.2.0
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 +59 -56
- package/src/find.test.js +135 -220
package/package.json
CHANGED
package/src/find.js
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
export function findClosestAbs(array, desired, { key,
|
|
1
|
+
export function findClosestAbs(array, desired, { key, cutoff = Infinity } = {}) {
|
|
2
2
|
let closest
|
|
3
|
-
if (
|
|
3
|
+
if (typeof key === "function") {
|
|
4
4
|
for (let i = 0; i < array.length; i++) {
|
|
5
5
|
const element = array[i]
|
|
6
|
-
const value =
|
|
6
|
+
const value = key(element, i, array)
|
|
7
7
|
const diff = Math.abs(value - desired)
|
|
8
|
-
if (diff <
|
|
8
|
+
if (diff < cutoff) {
|
|
9
9
|
closest = element
|
|
10
|
-
|
|
10
|
+
cutoff = diff
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
} else if (key) {
|
|
13
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
14
14
|
for (const element of array) {
|
|
15
15
|
const value = element[key]
|
|
16
16
|
const diff = Math.abs(value - desired)
|
|
17
|
-
if (diff <
|
|
17
|
+
if (diff < cutoff) {
|
|
18
18
|
closest = element
|
|
19
|
-
|
|
19
|
+
cutoff = diff
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
} else {
|
|
23
23
|
for (const value of array) {
|
|
24
24
|
const diff = Math.abs(value - desired)
|
|
25
|
-
if (diff <
|
|
25
|
+
if (diff < cutoff) {
|
|
26
26
|
closest = value
|
|
27
|
-
|
|
27
|
+
cutoff = diff
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
return closest
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function findClosestLT(array, desired, { key,
|
|
34
|
+
export function findClosestLT(array, desired, { key, cutoff = -Infinity } = {}) {
|
|
35
35
|
let closest
|
|
36
|
-
if (
|
|
36
|
+
if (typeof key === "function") {
|
|
37
37
|
for (let i = 0; i < array.length; i++) {
|
|
38
38
|
const element = array[i]
|
|
39
|
-
const value =
|
|
40
|
-
if (value < desired && value >
|
|
39
|
+
const value = key(element, i, array)
|
|
40
|
+
if (value < desired && value > cutoff) {
|
|
41
41
|
closest = element
|
|
42
|
-
|
|
42
|
+
cutoff = value
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
} else if (key) {
|
|
45
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
46
46
|
for (const element of array) {
|
|
47
47
|
const value = element[key]
|
|
48
|
-
if (value < desired && value >
|
|
48
|
+
if (value < desired && value > cutoff) {
|
|
49
49
|
closest = element
|
|
50
|
-
|
|
50
|
+
cutoff = value
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
} else {
|
|
54
54
|
for (const value of array) {
|
|
55
|
-
if (value < desired && value >
|
|
55
|
+
if (value < desired && value > cutoff) {
|
|
56
56
|
closest = value
|
|
57
|
-
|
|
57
|
+
cutoff = value
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
return closest
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export function findClosestLTE(array, desired, { key,
|
|
64
|
+
export function findClosestLTE(array, desired, { key, cutoff = -Infinity } = {}) {
|
|
65
65
|
let closest
|
|
66
|
-
if (
|
|
66
|
+
if (typeof key === "function") {
|
|
67
67
|
for (let i = 0; i < array.length; i++) {
|
|
68
68
|
const element = array[i]
|
|
69
|
-
const value =
|
|
70
|
-
if (value <= desired && value >
|
|
69
|
+
const value = key(element, i, array)
|
|
70
|
+
if (value <= desired && value > cutoff) {
|
|
71
71
|
closest = element
|
|
72
|
-
|
|
72
|
+
cutoff = value
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
} else if (key) {
|
|
75
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
76
76
|
for (const element of array) {
|
|
77
77
|
const value = element[key]
|
|
78
|
-
if (value <= desired && value >
|
|
78
|
+
if (value <= desired && value > cutoff) {
|
|
79
79
|
closest = element
|
|
80
|
-
|
|
80
|
+
cutoff = value
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
} else {
|
|
84
84
|
for (const value of array) {
|
|
85
|
-
if (value <= desired && value >
|
|
85
|
+
if (value <= desired && value > cutoff) {
|
|
86
86
|
closest = value
|
|
87
|
-
|
|
87
|
+
cutoff = value
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
return closest
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
export function findClosestGT(array, desired, { key,
|
|
94
|
+
export function findClosestGT(array, desired, { key, cutoff = Infinity } = {}) {
|
|
95
95
|
let closest
|
|
96
|
-
if (
|
|
96
|
+
if (typeof key === "function") {
|
|
97
97
|
for (let i = 0; i < array.length; i++) {
|
|
98
98
|
const element = array[i]
|
|
99
|
-
const value =
|
|
100
|
-
if (value > desired && value <
|
|
99
|
+
const value = key(element, i, array)
|
|
100
|
+
if (value > desired && value < cutoff) {
|
|
101
101
|
closest = element
|
|
102
|
-
|
|
102
|
+
cutoff = value
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
} else if (key) {
|
|
105
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
106
106
|
for (const element of array) {
|
|
107
107
|
const value = element[key]
|
|
108
|
-
if (value > desired && value <
|
|
108
|
+
if (value > desired && value < cutoff) {
|
|
109
109
|
closest = element
|
|
110
|
-
|
|
110
|
+
cutoff = value
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
} else {
|
|
114
114
|
for (const value of array) {
|
|
115
|
-
if (value > desired && value <
|
|
115
|
+
if (value > desired && value < cutoff) {
|
|
116
116
|
closest = value
|
|
117
|
-
|
|
117
|
+
cutoff = value
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
return closest
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export function findClosestGTE(array, desired, { key,
|
|
124
|
+
export function findClosestGTE(array, desired, { key, cutoff = Infinity } = {}) {
|
|
125
125
|
let closest
|
|
126
|
-
if (
|
|
126
|
+
if (typeof key === "function") {
|
|
127
127
|
for (let i = 0; i < array.length; i++) {
|
|
128
128
|
const element = array[i]
|
|
129
|
-
const value =
|
|
130
|
-
if (value >= desired && value <
|
|
129
|
+
const value = key(element, i, array)
|
|
130
|
+
if (value >= desired && value < cutoff) {
|
|
131
131
|
closest = element
|
|
132
|
-
|
|
132
|
+
cutoff = value
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
} else if (key) {
|
|
135
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
136
136
|
for (const element of array) {
|
|
137
137
|
const value = element[key]
|
|
138
|
-
if (value >= desired && value <
|
|
138
|
+
if (value >= desired && value < cutoff) {
|
|
139
139
|
closest = element
|
|
140
|
-
|
|
140
|
+
cutoff = value
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
} else {
|
|
144
144
|
for (const value of array) {
|
|
145
|
-
if (value >= desired && value <
|
|
145
|
+
if (value >= desired && value < cutoff) {
|
|
146
146
|
closest = value
|
|
147
|
-
|
|
147
|
+
cutoff = value
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -152,17 +152,20 @@ export function findClosestGTE(array, desired, { key, map, threshold = Infinity
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
* Find the closest element in an array.
|
|
156
|
-
* If using for strings, need to specify different values for "
|
|
157
|
-
* "~" and "" are good
|
|
155
|
+
* Find the closest element in an array. If there is a tie, then returns the first matching element by order in the array.
|
|
156
|
+
* If using for strings, need to specify different values for "cutoff" and "comparator".
|
|
157
|
+
* "~" and "" are good cutoff string values for gt/gte and lt/lte respectively.
|
|
158
158
|
* @template T, V
|
|
159
159
|
* @param {Array<T>} array
|
|
160
160
|
* @param {V} value The desired value to search for
|
|
161
161
|
* @param {Object} options
|
|
162
|
-
* @param {string=} options.key
|
|
163
|
-
*
|
|
162
|
+
* @param {string|number|Function=} options.key
|
|
163
|
+
* If specified, will consider the value for each element's key instead of the element itself.
|
|
164
|
+
* If a function, called with the element, index and array (same as .map() callback) to produce the value to sort on.
|
|
164
165
|
* @param {string=} options.comparator "abs", "lt", "lte", "gt", "gte", "abs". Default is "abs" which implies T is number.
|
|
165
|
-
* @param {V=} options.
|
|
166
|
+
* @param {V=} options.cutoff If specified, sets a initial constraint on how close the found value must be.
|
|
167
|
+
* For example, if used with "lt", the found element would need to be greater than the cutoff but still less than the desired value.
|
|
168
|
+
* If used with "abs", the found element would need to have a difference with the desired value less than the cutoff.
|
|
166
169
|
* @returns {T|undefined}
|
|
167
170
|
*/
|
|
168
171
|
export function findClosest(array, value, options = {}) {
|
package/src/find.test.js
CHANGED
|
@@ -1,301 +1,216 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { describe, expect, it } from "@jest/globals"
|
|
2
|
+
|
|
3
|
+
const {
|
|
3
4
|
findClosestAbs,
|
|
4
|
-
findClosestGT,
|
|
5
|
-
findClosestGTE,
|
|
6
5
|
findClosestLT,
|
|
7
6
|
findClosestLTE,
|
|
8
|
-
|
|
7
|
+
findClosestGT,
|
|
8
|
+
findClosestGTE,
|
|
9
|
+
findClosest,
|
|
10
|
+
} = await import("./find.js")
|
|
9
11
|
|
|
10
|
-
describe("
|
|
11
|
-
it("returns the closest
|
|
12
|
-
expect(
|
|
13
|
-
expect(
|
|
14
|
-
expect(
|
|
12
|
+
describe("findClosestAbs", () => {
|
|
13
|
+
it("returns the element closest in absolute value to desired", () => {
|
|
14
|
+
expect(findClosestAbs([1, 5, 9], 6)).toBe(5)
|
|
15
|
+
expect(findClosestAbs([1, 5, 9], 8)).toBe(9)
|
|
16
|
+
expect(findClosestAbs([1, 5, 9], 1)).toBe(1)
|
|
15
17
|
})
|
|
16
18
|
|
|
17
|
-
it("returns
|
|
18
|
-
expect(
|
|
19
|
+
it("returns the first element in case of tie", () => {
|
|
20
|
+
expect(findClosestAbs([4, 8], 6)).toBe(4)
|
|
19
21
|
})
|
|
20
22
|
|
|
21
|
-
it("returns
|
|
22
|
-
|
|
23
|
-
expect(findClosest(arr, 6, { key: "v" })).toEqual({ v: 5 })
|
|
23
|
+
it("returns undefined for empty array", () => {
|
|
24
|
+
expect(findClosestAbs([], 10)).toBeUndefined()
|
|
24
25
|
})
|
|
25
26
|
|
|
26
|
-
it("
|
|
27
|
-
|
|
28
|
-
expect(
|
|
27
|
+
it("supports key as function", () => {
|
|
28
|
+
const arr = [{ v: 2 }, { v: 8 }]
|
|
29
|
+
expect(findClosestAbs(arr, 5, { key: (e) => e.v })).toEqual({ v: 2 })
|
|
29
30
|
})
|
|
30
31
|
|
|
31
|
-
it("
|
|
32
|
-
|
|
33
|
-
expect(
|
|
34
|
-
expect(findClosest([1, 3, 5, 7], 0, { comparator: "lte" })).toBeUndefined()
|
|
32
|
+
it("supports key as string", () => {
|
|
33
|
+
const arr = [{ x: 1 }, { x: 10 }]
|
|
34
|
+
expect(findClosestAbs(arr, 8, { key: "x" })).toEqual({ x: 10 })
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
it("
|
|
38
|
-
|
|
39
|
-
expect(
|
|
37
|
+
it("supports key as number", () => {
|
|
38
|
+
const arr = [[2], [8]]
|
|
39
|
+
expect(findClosestAbs(arr, 7, { key: 0 })).toEqual([8])
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
it("
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
45
|
-
expect(findClosest([1, 3, 5, 7], 8, { comparator: "gte" })).toBeUndefined()
|
|
42
|
+
it("respects cutoff", () => {
|
|
43
|
+
expect(findClosestAbs([1, 5, 9], 6, { cutoff: 2 })).toBe(5)
|
|
44
|
+
expect(findClosestAbs([1, 5, 9], 6, { cutoff: 1 })).toBeUndefined()
|
|
46
45
|
})
|
|
46
|
+
})
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
expect(
|
|
51
|
-
expect(
|
|
52
|
-
expect(
|
|
53
|
-
expect(findClosest(arr, 10, { comparator: "gte", key: "v" })).toEqual({ v: 10 })
|
|
48
|
+
describe("findClosestLT", () => {
|
|
49
|
+
it("returns the closest element less than desired", () => {
|
|
50
|
+
expect(findClosestLT([1, 5, 9], 6)).toBe(5)
|
|
51
|
+
expect(findClosestLT([1, 5, 9], 2)).toBe(1)
|
|
52
|
+
expect(findClosestLT([1, 5, 9], 1)).toBeUndefined()
|
|
54
53
|
})
|
|
55
54
|
|
|
56
|
-
it("
|
|
57
|
-
expect(
|
|
58
|
-
expect(findClosest([1, 5, 10], 6, { threshold: 2 })).toBe(5)
|
|
55
|
+
it("returns first match if tie", () => {
|
|
56
|
+
expect(findClosestLT([2, 2, 1], 3)).toBe(2)
|
|
59
57
|
})
|
|
60
58
|
|
|
61
|
-
it("
|
|
62
|
-
expect(
|
|
63
|
-
expect(findClosest([1, 3, 5, 7], 6, { comparator: "lt", threshold: 5 })).toBeUndefined()
|
|
64
|
-
expect(findClosest([1, 3, 5, 7], 6, { comparator: "gt", threshold: 7 })).toBeUndefined()
|
|
65
|
-
expect(findClosest([1, 3, 5, 7], 6, { comparator: "gt", threshold: 10 })).toBe(7)
|
|
59
|
+
it("returns undefined for empty array", () => {
|
|
60
|
+
expect(findClosestLT([], 10)).toBeUndefined()
|
|
66
61
|
})
|
|
67
62
|
|
|
68
|
-
it("
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
63
|
+
it("supports key as function", () => {
|
|
64
|
+
const arr = [{ v: 2 }, { v: 8 }]
|
|
65
|
+
expect(findClosestLT(arr, 8, { key: (e) => e.v })).toEqual({ v: 2 })
|
|
72
66
|
})
|
|
73
67
|
|
|
74
|
-
it("
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
).toBeUndefined()
|
|
78
|
-
expect(
|
|
79
|
-
findClosest([{ v: 1 }], 0, { comparator: "lt", key: "v", threshold: 1 })
|
|
80
|
-
).toBeUndefined()
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it("works with negative numbers and zero", () => {
|
|
84
|
-
expect(findClosest([-10, -5, 0, 5, 10], -7)).toBe(-5)
|
|
85
|
-
expect(findClosest([-10, -5, 0, 5, 10], 0)).toBe(0)
|
|
86
|
-
expect(findClosest([-10, -5, 0, 5, 10], 7)).toBe(5)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// ISSUE: findClosestAbs and related functions do not skip NaN values in key/map modes, only in value mode.
|
|
90
|
-
it("skips NaN values in abs comparator", () => {
|
|
91
|
-
expect(findClosest([1, NaN, 5], 4)).toBe(5)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it("skips objects missing the key in key-based comparators", () => {
|
|
95
|
-
const arr = [{ v: 1 }, {}, { v: 5 }]
|
|
96
|
-
expect(findClosest(arr, 2, { key: "v" })).toEqual({ v: 1 })
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it("finds the closest string using abs comparator and a custom threshold/comparator", () => {
|
|
100
|
-
// Since abs comparator expects numbers, we need to provide a custom comparator for strings.
|
|
101
|
-
// We'll use threshold and comparator: "lt", "lte", "gt", "gte" for string comparisons.
|
|
102
|
-
const arr = ["apple", "banana", "cherry", "date"]
|
|
103
|
-
// Find the closest string less than "carrot" (alphabetically)
|
|
104
|
-
expect(findClosest(arr, "carrot", { comparator: "lt", threshold: "" })).toBe("banana")
|
|
105
|
-
// Find the closest string less than or equal to "banana"
|
|
106
|
-
expect(findClosest(arr, "banana", { comparator: "lte", threshold: "" })).toBe("banana")
|
|
107
|
-
// Find the closest string greater than "carrot"
|
|
108
|
-
expect(findClosest(arr, "carrot", { comparator: "gt", threshold: "~" })).toBe("cherry")
|
|
109
|
-
// Find the closest string greater than or equal to "date"
|
|
110
|
-
expect(findClosest(arr, "date", { comparator: "gte", threshold: "~" })).toBe("date")
|
|
111
|
-
// If nothing matches, returns undefined
|
|
112
|
-
expect(findClosest(arr, "aardvark", { comparator: "lt", threshold: "" })).toBeUndefined()
|
|
113
|
-
expect(findClosest(arr, "zebra", { comparator: "gt", threshold: "~" })).toBeUndefined()
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it("finds the closest string by key in array of objects", () => {
|
|
117
|
-
const arr = [{ name: "apple" }, { name: "banana" }, { name: "cherry" }]
|
|
118
|
-
expect(
|
|
119
|
-
findClosest(arr, "blueberry", { comparator: "lt", key: "name", threshold: "" })
|
|
120
|
-
).toEqual({
|
|
121
|
-
name: "banana",
|
|
122
|
-
})
|
|
123
|
-
expect(
|
|
124
|
-
findClosest(arr, "banana", { comparator: "lte", key: "name", threshold: "" })
|
|
125
|
-
).toEqual({
|
|
126
|
-
name: "banana",
|
|
127
|
-
})
|
|
128
|
-
expect(
|
|
129
|
-
findClosest(arr, "banana", { comparator: "gt", key: "name", threshold: "~" })
|
|
130
|
-
).toEqual({
|
|
131
|
-
name: "cherry",
|
|
132
|
-
})
|
|
133
|
-
expect(
|
|
134
|
-
findClosest(arr, "cherry", { comparator: "gte", key: "name", threshold: "~" })
|
|
135
|
-
).toEqual({
|
|
136
|
-
name: "cherry",
|
|
137
|
-
})
|
|
138
|
-
expect(
|
|
139
|
-
findClosest(arr, "aardvark", { comparator: "lt", key: "name", threshold: "" })
|
|
140
|
-
).toBeUndefined()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it("returns undefined if no string matches threshold/key criteria", () => {
|
|
144
|
-
const arr = ["apple", "banana", "cherry"]
|
|
145
|
-
expect(findClosest(arr, "apple", { comparator: "lt", threshold: "" })).toBeUndefined()
|
|
146
|
-
expect(findClosest(arr, "cherry", { comparator: "gt" })).toBeUndefined()
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it("can use abs comparator with string lengths", () => {
|
|
150
|
-
// This is a reasonable use-case for abs: find string with length closest to 4
|
|
151
|
-
const arr = ["a", "bb", "ccc", "dddd", "eeeee"]
|
|
152
|
-
// Map to string lengths using key
|
|
153
|
-
expect(findClosest(arr, 4, { comparator: "abs", key: "length" })).toEqual("dddd")
|
|
154
|
-
// If threshold is set so no string length is close enough
|
|
155
|
-
expect(
|
|
156
|
-
findClosest(arr, 4, { comparator: "abs", key: "length", threshold: -1 })
|
|
157
|
-
).toBeUndefined()
|
|
68
|
+
it("supports key as string", () => {
|
|
69
|
+
const arr = [{ x: 1 }, { x: 10 }]
|
|
70
|
+
expect(findClosestLT(arr, 8, { key: "x" })).toEqual({ x: 1 })
|
|
158
71
|
})
|
|
159
|
-
})
|
|
160
72
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
expect(
|
|
164
|
-
expect(findClosestAbs([1, 5, 10], 8)).toBe(10)
|
|
165
|
-
expect(findClosestAbs([1, 5, 10], 1)).toBe(1)
|
|
73
|
+
it("supports key as number", () => {
|
|
74
|
+
const arr = [[2], [8]]
|
|
75
|
+
expect(findClosestLT(arr, 7, { key: 0 })).toEqual([2])
|
|
166
76
|
})
|
|
167
77
|
|
|
168
|
-
it("
|
|
169
|
-
expect(
|
|
78
|
+
it("respects cutoff", () => {
|
|
79
|
+
expect(findClosestLT([1, 5, 9], 6, { cutoff: 4 })).toBe(5)
|
|
80
|
+
expect(findClosestLT([1, 5, 9], 6, { cutoff: 5 })).toBeUndefined()
|
|
170
81
|
})
|
|
82
|
+
})
|
|
171
83
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
expect(
|
|
84
|
+
describe("findClosestLTE", () => {
|
|
85
|
+
it("returns the closest element less than or equal to desired", () => {
|
|
86
|
+
expect(findClosestLTE([1, 5, 9], 5)).toBe(5)
|
|
87
|
+
expect(findClosestLTE([1, 5, 9], 6)).toBe(5)
|
|
88
|
+
expect(findClosestLTE([1, 5, 9], 1)).toBe(1)
|
|
89
|
+
expect(findClosestLTE([1, 5, 9], 0)).toBeUndefined()
|
|
175
90
|
})
|
|
176
91
|
|
|
177
|
-
it("returns
|
|
178
|
-
|
|
179
|
-
expect(findClosestAbs(arr, 6, { map: (el) => el.v })).toEqual({ v: 5 })
|
|
92
|
+
it("returns first match if tie", () => {
|
|
93
|
+
expect(findClosestLTE([2, 2, 1], 2)).toBe(2)
|
|
180
94
|
})
|
|
181
95
|
|
|
182
|
-
it("
|
|
183
|
-
expect(
|
|
184
|
-
expect(findClosestAbs([1, 5, 10], 6, { threshold: 2 })).toBe(5)
|
|
96
|
+
it("returns undefined for empty array", () => {
|
|
97
|
+
expect(findClosestLTE([], 10)).toBeUndefined()
|
|
185
98
|
})
|
|
186
99
|
|
|
187
|
-
it("
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
expect(findClosestAbs(arr, 2, { key: "v" })).toEqual({ v: 1 })
|
|
191
|
-
expect(findClosestAbs(arr, 2, { map: (el) => el.v })).toEqual({ v: 1 })
|
|
100
|
+
it("supports key as function", () => {
|
|
101
|
+
const arr = [{ v: 2 }, { v: 8 }]
|
|
102
|
+
expect(findClosestLTE(arr, 8, { key: (e) => e.v })).toEqual({ v: 8 })
|
|
192
103
|
})
|
|
193
|
-
})
|
|
194
104
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
expect(
|
|
198
|
-
expect(findClosestLT([1, 3, 5, 7], 1)).toBeUndefined()
|
|
105
|
+
it("supports key as string", () => {
|
|
106
|
+
const arr = [{ x: 1 }, { x: 10 }]
|
|
107
|
+
expect(findClosestLTE(arr, 8, { key: "x" })).toEqual({ x: 1 })
|
|
199
108
|
})
|
|
200
109
|
|
|
201
|
-
it("
|
|
202
|
-
const arr = [
|
|
203
|
-
expect(
|
|
110
|
+
it("supports key as number", () => {
|
|
111
|
+
const arr = [[2], [8]]
|
|
112
|
+
expect(findClosestLTE(arr, 7, { key: 0 })).toEqual([2])
|
|
204
113
|
})
|
|
205
114
|
|
|
206
|
-
it("
|
|
207
|
-
|
|
208
|
-
expect(
|
|
115
|
+
it("respects cutoff", () => {
|
|
116
|
+
expect(findClosestLTE([1, 5, 9], 6, { cutoff: 4 })).toBe(5)
|
|
117
|
+
expect(findClosestLTE([1, 5, 9], 6, { cutoff: 5 })).toBeUndefined()
|
|
209
118
|
})
|
|
119
|
+
})
|
|
210
120
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
expect(
|
|
121
|
+
describe("findClosestGT", () => {
|
|
122
|
+
it("returns the closest element greater than desired", () => {
|
|
123
|
+
expect(findClosestGT([1, 5, 9], 6)).toBe(9)
|
|
124
|
+
expect(findClosestGT([1, 5, 9], 0)).toBe(1)
|
|
125
|
+
expect(findClosestGT([1, 5, 9], 9)).toBeUndefined()
|
|
214
126
|
})
|
|
215
127
|
|
|
216
|
-
it("returns
|
|
217
|
-
expect(
|
|
128
|
+
it("returns first match if tie", () => {
|
|
129
|
+
expect(findClosestGT([8, 8, 10], 7)).toBe(8)
|
|
218
130
|
})
|
|
219
|
-
})
|
|
220
131
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
expect(findClosestLTE([1, 3, 5, 7], 5)).toBe(5)
|
|
224
|
-
expect(findClosestLTE([1, 3, 5, 7], 2)).toBe(1)
|
|
225
|
-
expect(findClosestLTE([1, 3, 5, 7], 0)).toBeUndefined()
|
|
132
|
+
it("returns undefined for empty array", () => {
|
|
133
|
+
expect(findClosestGT([], 10)).toBeUndefined()
|
|
226
134
|
})
|
|
227
135
|
|
|
228
|
-
it("
|
|
229
|
-
const arr = [{ v:
|
|
230
|
-
expect(
|
|
136
|
+
it("supports key as function", () => {
|
|
137
|
+
const arr = [{ v: 2 }, { v: 8 }]
|
|
138
|
+
expect(findClosestGT(arr, 2, { key: (e) => e.v })).toEqual({ v: 8 })
|
|
231
139
|
})
|
|
232
140
|
|
|
233
|
-
it("
|
|
234
|
-
const arr = [{
|
|
235
|
-
expect(
|
|
141
|
+
it("supports key as string", () => {
|
|
142
|
+
const arr = [{ x: 1 }, { x: 10 }]
|
|
143
|
+
expect(findClosestGT(arr, 8, { key: "x" })).toEqual({ x: 10 })
|
|
236
144
|
})
|
|
237
145
|
|
|
238
|
-
it("
|
|
239
|
-
|
|
240
|
-
expect(
|
|
146
|
+
it("supports key as number", () => {
|
|
147
|
+
const arr = [[2], [8]]
|
|
148
|
+
expect(findClosestGT(arr, 2, { key: 0 })).toEqual([8])
|
|
241
149
|
})
|
|
242
150
|
|
|
243
|
-
it("
|
|
244
|
-
expect(
|
|
151
|
+
it("respects cutoff", () => {
|
|
152
|
+
expect(findClosestGT([1, 5, 9], 6, { cutoff: 8 })).toBeUndefined()
|
|
153
|
+
expect(findClosestGT([1, 5, 9], 4, { cutoff: 8 })).toBe(5)
|
|
245
154
|
})
|
|
246
155
|
})
|
|
247
156
|
|
|
248
|
-
describe("
|
|
249
|
-
it("returns closest
|
|
250
|
-
expect(
|
|
251
|
-
expect(
|
|
157
|
+
describe("findClosestGTE", () => {
|
|
158
|
+
it("returns the closest element greater than or equal to desired", () => {
|
|
159
|
+
expect(findClosestGTE([1, 5, 9], 5)).toBe(5)
|
|
160
|
+
expect(findClosestGTE([1, 5, 9], 4)).toBe(5)
|
|
161
|
+
expect(findClosestGTE([1, 5, 9], 10)).toBeUndefined()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it("returns first match if tie", () => {
|
|
165
|
+
expect(findClosestGTE([8, 8, 10], 8)).toBe(8)
|
|
252
166
|
})
|
|
253
167
|
|
|
254
|
-
it("returns
|
|
255
|
-
|
|
256
|
-
expect(findClosestGT(arr, 6, { key: "v" })).toEqual({ v: 10 })
|
|
168
|
+
it("returns undefined for empty array", () => {
|
|
169
|
+
expect(findClosestGTE([], 10)).toBeUndefined()
|
|
257
170
|
})
|
|
258
171
|
|
|
259
|
-
it("
|
|
260
|
-
const arr = [{ v:
|
|
261
|
-
expect(
|
|
172
|
+
it("supports key as function", () => {
|
|
173
|
+
const arr = [{ v: 2 }, { v: 8 }]
|
|
174
|
+
expect(findClosestGTE(arr, 2, { key: (e) => e.v })).toEqual({ v: 2 })
|
|
262
175
|
})
|
|
263
176
|
|
|
264
|
-
it("
|
|
265
|
-
|
|
266
|
-
expect(
|
|
177
|
+
it("supports key as string", () => {
|
|
178
|
+
const arr = [{ x: 1 }, { x: 10 }]
|
|
179
|
+
expect(findClosestGTE(arr, 8, { key: "x" })).toEqual({ x: 10 })
|
|
267
180
|
})
|
|
268
181
|
|
|
269
|
-
it("
|
|
270
|
-
|
|
182
|
+
it("supports key as number", () => {
|
|
183
|
+
const arr = [[2], [8]]
|
|
184
|
+
expect(findClosestGTE(arr, 2, { key: 0 })).toEqual([2])
|
|
271
185
|
})
|
|
272
|
-
})
|
|
273
186
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
expect(findClosestGTE([1,
|
|
277
|
-
expect(findClosestGTE([1, 3, 5, 7], 6)).toBe(7)
|
|
278
|
-
expect(findClosestGTE([1, 3, 5, 7], 8)).toBeUndefined()
|
|
187
|
+
it("respects cutoff", () => {
|
|
188
|
+
expect(findClosestGTE([1, 5, 9], 6, { cutoff: 8 })).toBeUndefined()
|
|
189
|
+
expect(findClosestGTE([1, 5, 9], 4, { cutoff: 8 })).toBe(5)
|
|
279
190
|
})
|
|
191
|
+
})
|
|
280
192
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
expect(
|
|
284
|
-
expect(findClosestGTE(arr, 10, { key: "v" })).toEqual({ v: 10 })
|
|
193
|
+
describe("findClosest", () => {
|
|
194
|
+
it("defaults to abs comparator", () => {
|
|
195
|
+
expect(findClosest([1, 5, 9], 6)).toBe(5)
|
|
285
196
|
})
|
|
286
197
|
|
|
287
|
-
it("
|
|
288
|
-
|
|
289
|
-
expect(
|
|
290
|
-
expect(
|
|
198
|
+
it("calls correct comparator", () => {
|
|
199
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "lt" })).toBe(5)
|
|
200
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "lte" })).toBe(5)
|
|
201
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "gt" })).toBe(9)
|
|
202
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "gte" })).toBe(9)
|
|
203
|
+
expect(findClosest([1, 5, 9], 6, { comparator: "abs" })).toBe(5)
|
|
291
204
|
})
|
|
292
205
|
|
|
293
|
-
it("
|
|
294
|
-
expect(
|
|
295
|
-
|
|
206
|
+
it("throws on unknown comparator", () => {
|
|
207
|
+
expect(() => findClosest([1, 5, 9], 6, { comparator: "foo" })).toThrow(
|
|
208
|
+
"Unknown comparator: foo"
|
|
209
|
+
)
|
|
296
210
|
})
|
|
297
211
|
|
|
298
|
-
it("
|
|
299
|
-
|
|
212
|
+
it("passes options to underlying function", () => {
|
|
213
|
+
const arr = [{ x: 1 }, { x: 10 }]
|
|
214
|
+
expect(findClosest(arr, 8, { comparator: "abs", key: "x" })).toEqual({ x: 10 })
|
|
300
215
|
})
|
|
301
216
|
})
|