@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tim-code/my-util",
3
- "version": "0.4.10",
3
+ "version": "0.5.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "Tim Sprowl",
package/src/find.js CHANGED
@@ -1,11 +1,37 @@
1
- export function findClosestAbs(array, desired, { key, cutoff = Infinity } = {}) {
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 "abs", "lt", "lte", "gt", "gte", "abs". Default is "abs" which implies T is number.
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
- * For example, if used with "lt", the found element would need to be greater than the cutoff but still less than the desired value.
169
- * If used with "abs", the found element would need to have a difference with the desired value less than the cutoff.
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 = "abs" } = options
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 "abs":
184
- return findClosestAbs(array, value, options)
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
- findClosestAbs,
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("findClosestAbs", () => {
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(findClosestAbs([1, 5, 9], 6)).toBe(5)
19
- expect(findClosestAbs([1, 5, 9], 8)).toBe(9)
20
- expect(findClosestAbs([1, 5, 9], 1)).toBe(1)
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(findClosestAbs([4, 8], 6)).toBe(4)
60
+ expect(findSmallestDiff([4, 8], 6)).toBe(4)
25
61
  })
26
62
 
27
63
  it("returns undefined for empty array", () => {
28
- expect(findClosestAbs([], 10)).toBeUndefined()
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(findClosestAbs(arr, 5, { key: (e) => e.v })).toEqual({ v: 2 })
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(findClosestAbs(arr, 8, { key: "x" })).toEqual({ x: 10 })
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(findClosestAbs(arr, 7, { key: 0 })).toEqual([8])
79
+ expect(findSmallestDiff(arr, 7, { key: 0 })).toEqual([8])
44
80
  })
45
81
 
46
82
  it("respects cutoff", () => {
47
- expect(findClosestAbs([1, 5, 9], 6, { cutoff: 2 })).toBe(5)
48
- expect(findClosestAbs([1, 5, 9], 6, { cutoff: 1 })).toBeUndefined()
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 })).toBeUndefined()
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 })).toBeUndefined()
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(findClosestGT([1, 5, 9], 6, { cutoff: 8 })).toBeUndefined()
157
- expect(findClosestGT([1, 5, 9], 4, { cutoff: 8 })).toBe(5)
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 abs comparator", () => {
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: "abs" })).toBe(5)
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: "abs", key: "x" })).toEqual({ x: 10 })
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