@tim-code/my-util 0.4.2 → 0.4.4
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 +62 -60
- package/src/find.test.js +36 -100
package/package.json
CHANGED
package/src/find.js
CHANGED
|
@@ -270,70 +270,72 @@ export function findMax(array, { key, cutoff = -Infinity } = {}) {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
/**
|
|
273
|
-
*
|
|
273
|
+
* Find the first truthy value in an array.
|
|
274
|
+
* Supports "from" and "to" being reversed to "find last truthy".
|
|
274
275
|
* @template T
|
|
275
276
|
* @param {Array<T>} array
|
|
276
|
-
* @param {
|
|
277
|
-
* @param {
|
|
278
|
-
*
|
|
277
|
+
* @param {Object} $1
|
|
278
|
+
* @param {string|Function=} $1.key Specifies an alternative to using each element as the value.
|
|
279
|
+
* If string, then accesses each element at that key to get value.
|
|
280
|
+
* If function, then calls the callback on each element to get value.
|
|
281
|
+
* @param {number=} $1.from Numeric index to start from: inclusive, defaults to 0
|
|
282
|
+
* @param {number=} $1.to Numeric index to end at: inclusive, defaults to `array.length - 1`
|
|
283
|
+
* @returns {T}
|
|
279
284
|
*/
|
|
280
|
-
export function
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
|
|
285
|
+
export function findTruthy(array, { key, from = 0, to = array.length - 1 }) {
|
|
286
|
+
if (typeof key === "function") {
|
|
287
|
+
if (from <= to) {
|
|
288
|
+
for (let i = from; i <= to; i++) {
|
|
289
|
+
const element = array[i]
|
|
290
|
+
const value = key(element, i, array)
|
|
291
|
+
if (value) {
|
|
292
|
+
return element
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
for (let i = from; i >= to; i--) {
|
|
297
|
+
const element = array[i]
|
|
298
|
+
const value = key(element, i, array)
|
|
299
|
+
if (value) {
|
|
300
|
+
return element
|
|
301
|
+
}
|
|
302
|
+
}
|
|
284
303
|
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return i
|
|
304
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
305
|
+
if (from <= to) {
|
|
306
|
+
for (let i = from; i <= to; i++) {
|
|
307
|
+
const element = array[i]
|
|
308
|
+
const value = element[key]
|
|
309
|
+
if (value) {
|
|
310
|
+
return element
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
for (let i = from; i >= to; i--) {
|
|
315
|
+
const element = array[i]
|
|
316
|
+
const value = element[key]
|
|
317
|
+
if (value) {
|
|
318
|
+
return element
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
// eslint-disable-next-line no-lonely-if
|
|
324
|
+
if (from <= to) {
|
|
325
|
+
for (let i = from; i <= to; i++) {
|
|
326
|
+
const value = array[i]
|
|
327
|
+
if (value) {
|
|
328
|
+
return value
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
for (let i = from; i >= to; i--) {
|
|
333
|
+
const value = array[i]
|
|
334
|
+
if (value) {
|
|
335
|
+
return value
|
|
336
|
+
}
|
|
337
|
+
}
|
|
320
338
|
}
|
|
321
339
|
}
|
|
322
|
-
return
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Like Array.prototype.findLast but starts from given index.
|
|
327
|
-
* @template T
|
|
328
|
-
* @param {Array<T>} array
|
|
329
|
-
* @param {number} fromIndex
|
|
330
|
-
* @param {(value: T, index: number, array: Array<T>) => boolean} callback
|
|
331
|
-
* @returns {T|undefined}
|
|
332
|
-
*/
|
|
333
|
-
export function findLastFrom(array, fromIndex, callback) {
|
|
334
|
-
const foundIndex = findLastIndexFrom(array, fromIndex, callback)
|
|
335
|
-
if (foundIndex < 0) {
|
|
336
|
-
return undefined
|
|
337
|
-
}
|
|
338
|
-
return array[foundIndex]
|
|
340
|
+
return undefined
|
|
339
341
|
}
|
package/src/find.test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-syntax */
|
|
1
2
|
import { describe, expect, it } from "@jest/globals"
|
|
2
3
|
|
|
3
4
|
const {
|
|
@@ -9,10 +10,7 @@ const {
|
|
|
9
10
|
findClosest,
|
|
10
11
|
findMin,
|
|
11
12
|
findMax,
|
|
12
|
-
|
|
13
|
-
findFrom,
|
|
14
|
-
findLastIndexFrom,
|
|
15
|
-
findLastFrom,
|
|
13
|
+
findTruthy,
|
|
16
14
|
} = await import("./find.js")
|
|
17
15
|
|
|
18
16
|
describe("findClosestAbs", () => {
|
|
@@ -289,116 +287,54 @@ describe("findMax", () => {
|
|
|
289
287
|
})
|
|
290
288
|
})
|
|
291
289
|
|
|
292
|
-
describe("
|
|
293
|
-
it("returns the
|
|
294
|
-
expect(
|
|
295
|
-
expect(
|
|
296
|
-
expect(findIndexFrom([1, 2, 3, 4], 3, (x) => x > 2)).toBe(3)
|
|
290
|
+
describe("findTruthy", () => {
|
|
291
|
+
it("returns the first truthy value in a simple array", () => {
|
|
292
|
+
expect(findTruthy([0, false, null, 2, 3], {})).toBe(2)
|
|
293
|
+
expect(findTruthy([0, false, null, undefined], {})).toBeUndefined()
|
|
297
294
|
})
|
|
298
295
|
|
|
299
|
-
it("returns
|
|
300
|
-
expect(
|
|
296
|
+
it("returns the first truthy value in a range [from, to]", () => {
|
|
297
|
+
expect(findTruthy([0, 1, 2, 0, 3, 0], { from: 2, to: 5 })).toBe(2)
|
|
298
|
+
expect(findTruthy([0, 0, 0, 4, 0], { from: 1, to: 3 })).toBe(4)
|
|
299
|
+
expect(findTruthy([0, 0, 0, 0], { from: 1, to: 2 })).toBeUndefined()
|
|
301
300
|
})
|
|
302
301
|
|
|
303
|
-
it("returns
|
|
304
|
-
expect(
|
|
302
|
+
it("returns the first truthy value when searching backwards (from > to)", () => {
|
|
303
|
+
expect(findTruthy([0, 1, 2, 0, 3, 0], { from: 4, to: 1 })).toBe(3)
|
|
304
|
+
expect(findTruthy([0, 0, 0, 4, 0], { from: 3, to: 0 })).toBe(4)
|
|
305
|
+
expect(findTruthy([0, 0, 0, 0], { from: 2, to: 0 })).toBeUndefined()
|
|
305
306
|
})
|
|
306
307
|
|
|
307
|
-
it("
|
|
308
|
-
const arr = [
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
return false
|
|
313
|
-
})
|
|
314
|
-
expect(called[0][0]).toBe(20)
|
|
315
|
-
expect(called[0][1]).toBe(1)
|
|
316
|
-
expect(called[0][2]).toBe(arr)
|
|
308
|
+
it("supports key as function", () => {
|
|
309
|
+
const arr = [{ v: 0 }, { v: 2 }, { v: 0 }]
|
|
310
|
+
expect(findTruthy(arr, { key: (e) => e.v })).toEqual({ v: 2 })
|
|
311
|
+
expect(findTruthy(arr, { key: (e) => e.v, from: 2, to: 0 })).toEqual({ v: 2 })
|
|
312
|
+
expect(findTruthy(arr, { key: (e) => e.v, from: 0, to: 0 })).toBeUndefined()
|
|
317
313
|
})
|
|
318
|
-
})
|
|
319
314
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
expect(
|
|
323
|
-
expect(
|
|
324
|
-
expect(
|
|
315
|
+
it("supports key as string", () => {
|
|
316
|
+
const arr = [{ x: 0 }, { x: 2 }, { x: 0 }]
|
|
317
|
+
expect(findTruthy(arr, { key: "x" })).toEqual({ x: 2 })
|
|
318
|
+
expect(findTruthy(arr, { key: "x", from: 2, to: 0 })).toEqual({ x: 2 })
|
|
319
|
+
expect(findTruthy(arr, { key: "x", from: 0, to: 0 })).toBeUndefined()
|
|
325
320
|
})
|
|
326
321
|
|
|
327
|
-
it("
|
|
328
|
-
|
|
322
|
+
it("supports key as number", () => {
|
|
323
|
+
const arr = [[0], [2], [0]]
|
|
324
|
+
expect(findTruthy(arr, { key: 0 })).toEqual([2])
|
|
325
|
+
expect(findTruthy(arr, { key: 0, from: 2, to: 0 })).toEqual([2])
|
|
326
|
+
expect(findTruthy(arr, { key: 0, from: 0, to: 0 })).toBeUndefined()
|
|
329
327
|
})
|
|
330
328
|
|
|
331
329
|
it("returns undefined for empty array", () => {
|
|
332
|
-
expect(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
it("passes correct arguments to callback", () => {
|
|
336
|
-
const arr = [10, 20, 30]
|
|
337
|
-
const called = []
|
|
338
|
-
findFrom(arr, 1, (value, index, array) => {
|
|
339
|
-
called.push([value, index, array])
|
|
340
|
-
return false
|
|
341
|
-
})
|
|
342
|
-
expect(called[0][0]).toBe(20)
|
|
343
|
-
expect(called[0][1]).toBe(1)
|
|
344
|
-
expect(called[0][2]).toBe(arr)
|
|
345
|
-
})
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
describe("findLastIndexFrom", () => {
|
|
349
|
-
it("returns the index of the last matching element from fromIndex (backwards)", () => {
|
|
350
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 2, (x) => x < 3)).toBe(1)
|
|
351
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 3, (x) => x < 3)).toBe(1)
|
|
352
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 1, (x) => x < 3)).toBe(1)
|
|
353
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 0, (x) => x < 3)).toBe(0)
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
it("returns -1 if no element matches", () => {
|
|
357
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 3, (x) => x > 10)).toBe(-1)
|
|
330
|
+
expect(findTruthy([], {})).toBeUndefined()
|
|
331
|
+
expect(findTruthy([], { from: 0, to: 0 })).toBeUndefined()
|
|
358
332
|
})
|
|
359
333
|
|
|
360
|
-
it("returns
|
|
361
|
-
expect(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const arr = [10, 20, 30]
|
|
366
|
-
const called = []
|
|
367
|
-
findLastIndexFrom(arr, 2, (value, index, array) => {
|
|
368
|
-
called.push([value, index, array])
|
|
369
|
-
return false
|
|
370
|
-
})
|
|
371
|
-
expect(called[0][0]).toBe(30)
|
|
372
|
-
expect(called[0][1]).toBe(2)
|
|
373
|
-
expect(called[0][2]).toBe(arr)
|
|
374
|
-
})
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
describe("findLastFrom", () => {
|
|
378
|
-
it("returns the last matching element from fromIndex (backwards)", () => {
|
|
379
|
-
expect(findLastFrom([1, 2, 3, 4], 2, (x) => x < 3)).toBe(2)
|
|
380
|
-
expect(findLastFrom([1, 2, 3, 4], 3, (x) => x < 3)).toBe(2)
|
|
381
|
-
expect(findLastFrom([1, 2, 3, 4], 1, (x) => x < 3)).toBe(2)
|
|
382
|
-
expect(findLastFrom([1, 2, 3, 4], 0, (x) => x < 3)).toBe(1)
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
it("returns undefined if no element matches", () => {
|
|
386
|
-
expect(findLastFrom([1, 2, 3, 4], 3, (x) => x > 10)).toBeUndefined()
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
it("returns undefined for empty array", () => {
|
|
390
|
-
expect(findLastFrom([], 0, () => true)).toBeUndefined()
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
it("passes correct arguments to callback", () => {
|
|
394
|
-
const arr = [10, 20, 30]
|
|
395
|
-
const called = []
|
|
396
|
-
findLastFrom(arr, 2, (value, index, array) => {
|
|
397
|
-
called.push([value, index, array])
|
|
398
|
-
return false
|
|
399
|
-
})
|
|
400
|
-
expect(called[0][0]).toBe(30)
|
|
401
|
-
expect(called[0][1]).toBe(2)
|
|
402
|
-
expect(called[0][2]).toBe(arr)
|
|
334
|
+
it("returns undefined if from/to out of bounds", () => {
|
|
335
|
+
expect(findTruthy([1, 2, 3], { from: 10, to: 12 })).toBeUndefined()
|
|
336
|
+
expect(findTruthy([1, 2, 3], { from: -5, to: -1 })).toBeUndefined()
|
|
337
|
+
expect(findTruthy([1, 2, 3], { from: 2, to: 10 })).toBe(3)
|
|
338
|
+
expect(findTruthy([1, 2, 3], { from: 10, to: 2 })).toBe(3)
|
|
403
339
|
})
|
|
404
340
|
})
|