@tim-code/my-util 0.4.3 → 0.4.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/find.js +32 -71
- package/src/find.test.js +24 -128
package/package.json
CHANGED
package/src/find.js
CHANGED
|
@@ -270,81 +270,42 @@ 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.until Numeric index to end at: exclusive, defaults to `array.length`
|
|
283
|
+
* @returns {T}
|
|
279
284
|
*/
|
|
280
|
-
export function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
285
|
+
export function findTruthy(array, { key, from = 0, until = array.length } = {}) {
|
|
286
|
+
if (typeof key === "function") {
|
|
287
|
+
for (let i = from; i < until; i++) {
|
|
288
|
+
const element = array[i]
|
|
289
|
+
const value = key(element, i, array)
|
|
290
|
+
if (value) {
|
|
291
|
+
return element
|
|
292
|
+
}
|
|
284
293
|
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (foundIndex < 0) {
|
|
300
|
-
return undefined
|
|
301
|
-
}
|
|
302
|
-
return array[foundIndex]
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Like Array.prototype.findLastIndex but starts from given index.
|
|
307
|
-
* @template T
|
|
308
|
-
* @param {Array<T>} array
|
|
309
|
-
* @param {number} fromIndex If greater or equal to length, coerces to last index in array.
|
|
310
|
-
* @param {(value: T, index: number, array: Array<T>) => boolean} callback
|
|
311
|
-
* @returns {number}
|
|
312
|
-
*/
|
|
313
|
-
export function findLastIndexFrom(array, fromIndex, callback) {
|
|
314
|
-
if (fromIndex >= array.length) {
|
|
315
|
-
fromIndex = array.length - 1
|
|
316
|
-
}
|
|
317
|
-
for (let i = fromIndex; i >= 0; i--) {
|
|
318
|
-
if (callback(array[i], i, array)) {
|
|
319
|
-
return i
|
|
294
|
+
} else if (typeof key === "number" || typeof key === "string") {
|
|
295
|
+
for (let i = from; i < until; i++) {
|
|
296
|
+
const element = array[i]
|
|
297
|
+
const value = element[key]
|
|
298
|
+
if (value) {
|
|
299
|
+
return element
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
for (let i = from; i < until; i++) {
|
|
304
|
+
const value = array[i]
|
|
305
|
+
if (value) {
|
|
306
|
+
return value
|
|
307
|
+
}
|
|
320
308
|
}
|
|
321
309
|
}
|
|
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]
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Returns truthy if argument is truthy and falsy otherwise.
|
|
343
|
-
* Meant to used with find(): `array.find(isTruthy)`
|
|
344
|
-
* @template T
|
|
345
|
-
* @param {T} anything
|
|
346
|
-
* @returns {T}
|
|
347
|
-
*/
|
|
348
|
-
export function isTruthy(anything) {
|
|
349
|
-
return anything
|
|
310
|
+
return undefined
|
|
350
311
|
}
|
package/src/find.test.js
CHANGED
|
@@ -10,11 +10,7 @@ const {
|
|
|
10
10
|
findClosest,
|
|
11
11
|
findMin,
|
|
12
12
|
findMax,
|
|
13
|
-
|
|
14
|
-
findFrom,
|
|
15
|
-
findLastIndexFrom,
|
|
16
|
-
findLastFrom,
|
|
17
|
-
isTruthy,
|
|
13
|
+
findTruthy,
|
|
18
14
|
} = await import("./find.js")
|
|
19
15
|
|
|
20
16
|
describe("findClosestAbs", () => {
|
|
@@ -291,140 +287,40 @@ describe("findMax", () => {
|
|
|
291
287
|
})
|
|
292
288
|
})
|
|
293
289
|
|
|
294
|
-
describe("
|
|
295
|
-
it("returns the
|
|
296
|
-
expect(
|
|
297
|
-
expect(findIndexFrom([1, 2, 3, 4], 2, (x) => x > 2)).toBe(2)
|
|
298
|
-
expect(findIndexFrom([1, 2, 3, 4], 3, (x) => x > 2)).toBe(3)
|
|
290
|
+
describe("findTruthy", () => {
|
|
291
|
+
it("returns the first truthy value in array", () => {
|
|
292
|
+
expect(findTruthy([0, null, false, 2, 3], {})).toBe(2)
|
|
299
293
|
})
|
|
300
294
|
|
|
301
|
-
it("returns
|
|
302
|
-
expect(
|
|
295
|
+
it("returns undefined if no truthy value", () => {
|
|
296
|
+
expect(findTruthy([0, null, false], {})).toBeUndefined()
|
|
303
297
|
})
|
|
304
298
|
|
|
305
|
-
it("
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
it("passes correct arguments to callback", () => {
|
|
310
|
-
const arr = [10, 20, 30]
|
|
311
|
-
const called = []
|
|
312
|
-
findIndexFrom(arr, 1, (value, index, array) => {
|
|
313
|
-
called.push([value, index, array])
|
|
314
|
-
return false
|
|
315
|
-
})
|
|
316
|
-
expect(called[0][0]).toBe(20)
|
|
317
|
-
expect(called[0][1]).toBe(1)
|
|
318
|
-
expect(called[0][2]).toBe(arr)
|
|
319
|
-
})
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
describe("findFrom", () => {
|
|
323
|
-
it("returns the first matching element from fromIndex", () => {
|
|
324
|
-
expect(findFrom([1, 2, 3, 4], 1, (x) => x > 2)).toBe(3)
|
|
325
|
-
expect(findFrom([1, 2, 3, 4], 2, (x) => x > 2)).toBe(3)
|
|
326
|
-
expect(findFrom([1, 2, 3, 4], 3, (x) => x > 2)).toBe(4)
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
it("returns undefined if no element matches", () => {
|
|
330
|
-
expect(findFrom([1, 2, 3, 4], 2, (x) => x > 10)).toBeUndefined()
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
it("returns undefined for empty array", () => {
|
|
334
|
-
expect(findFrom([], 0, () => true)).toBeUndefined()
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
it("passes correct arguments to callback", () => {
|
|
338
|
-
const arr = [10, 20, 30]
|
|
339
|
-
const called = []
|
|
340
|
-
findFrom(arr, 1, (value, index, array) => {
|
|
341
|
-
called.push([value, index, array])
|
|
342
|
-
return false
|
|
343
|
-
})
|
|
344
|
-
expect(called[0][0]).toBe(20)
|
|
345
|
-
expect(called[0][1]).toBe(1)
|
|
346
|
-
expect(called[0][2]).toBe(arr)
|
|
347
|
-
})
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
describe("findLastIndexFrom", () => {
|
|
351
|
-
it("returns the index of the last matching element from fromIndex (backwards)", () => {
|
|
352
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 2, (x) => x < 3)).toBe(1)
|
|
353
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 3, (x) => x < 3)).toBe(1)
|
|
354
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 1, (x) => x < 3)).toBe(1)
|
|
355
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 0, (x) => x < 3)).toBe(0)
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
it("returns -1 if no element matches", () => {
|
|
359
|
-
expect(findLastIndexFrom([1, 2, 3, 4], 3, (x) => x > 10)).toBe(-1)
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
it("returns -1 for empty array", () => {
|
|
363
|
-
expect(findLastIndexFrom([], 0, () => true)).toBe(-1)
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
it("passes correct arguments to callback", () => {
|
|
367
|
-
const arr = [10, 20, 30]
|
|
368
|
-
const called = []
|
|
369
|
-
findLastIndexFrom(arr, 2, (value, index, array) => {
|
|
370
|
-
called.push([value, index, array])
|
|
371
|
-
return false
|
|
372
|
-
})
|
|
373
|
-
expect(called[0][0]).toBe(30)
|
|
374
|
-
expect(called[0][1]).toBe(2)
|
|
375
|
-
expect(called[0][2]).toBe(arr)
|
|
376
|
-
})
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
describe("findLastFrom", () => {
|
|
380
|
-
it("returns the last matching element from fromIndex (backwards)", () => {
|
|
381
|
-
expect(findLastFrom([1, 2, 3, 4], 2, (x) => x < 3)).toBe(2)
|
|
382
|
-
expect(findLastFrom([1, 2, 3, 4], 3, (x) => x < 3)).toBe(2)
|
|
383
|
-
expect(findLastFrom([1, 2, 3, 4], 1, (x) => x < 3)).toBe(2)
|
|
384
|
-
expect(findLastFrom([1, 2, 3, 4], 0, (x) => x < 3)).toBe(1)
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
it("returns undefined if no element matches", () => {
|
|
388
|
-
expect(findLastFrom([1, 2, 3, 4], 3, (x) => x > 10)).toBeUndefined()
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
it("returns undefined for empty array", () => {
|
|
392
|
-
expect(findLastFrom([], 0, () => true)).toBeUndefined()
|
|
299
|
+
it("supports key as function", () => {
|
|
300
|
+
const arr = [{ v: 0 }, { v: 2 }, { v: 0 }]
|
|
301
|
+
expect(findTruthy(arr, { key: (e) => e.v })).toEqual({ v: 2 })
|
|
393
302
|
})
|
|
394
303
|
|
|
395
|
-
it("
|
|
396
|
-
const arr = [
|
|
397
|
-
|
|
398
|
-
findLastFrom(arr, 2, (value, index, array) => {
|
|
399
|
-
called.push([value, index, array])
|
|
400
|
-
return false
|
|
401
|
-
})
|
|
402
|
-
expect(called[0][0]).toBe(30)
|
|
403
|
-
expect(called[0][1]).toBe(2)
|
|
404
|
-
expect(called[0][2]).toBe(arr)
|
|
304
|
+
it("supports key as string", () => {
|
|
305
|
+
const arr = [{ x: 0 }, { x: 2 }, { x: 0 }]
|
|
306
|
+
expect(findTruthy(arr, { key: "x" })).toEqual({ x: 2 })
|
|
405
307
|
})
|
|
406
|
-
})
|
|
407
308
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
expect(
|
|
411
|
-
expect(isTruthy("foo")).toBe("foo")
|
|
412
|
-
expect(isTruthy({})).toEqual({})
|
|
413
|
-
expect(isTruthy([])).toEqual([])
|
|
414
|
-
expect(isTruthy(true)).toBe(true)
|
|
309
|
+
it("supports key as number", () => {
|
|
310
|
+
const arr = [[0], [2], [0]]
|
|
311
|
+
expect(findTruthy(arr, { key: 0 })).toEqual([2])
|
|
415
312
|
})
|
|
416
313
|
|
|
417
|
-
it("
|
|
418
|
-
|
|
419
|
-
expect(
|
|
420
|
-
expect(
|
|
421
|
-
expect(
|
|
422
|
-
expect(
|
|
423
|
-
expect(isTruthy(NaN)).toBe(NaN)
|
|
314
|
+
it("respects from and until (forward)", () => {
|
|
315
|
+
const arr = [0, 1, 2, 3]
|
|
316
|
+
expect(findTruthy(arr, { from: 2, until: 4 })).toBe(2)
|
|
317
|
+
expect(findTruthy(arr, { from: 1, until: 3 })).toBe(1)
|
|
318
|
+
expect(findTruthy(arr, { from: 3, until: 4 })).toBe(3)
|
|
319
|
+
expect(findTruthy(arr, { from: 3, until: 3 })).toBeUndefined()
|
|
424
320
|
})
|
|
425
321
|
|
|
426
|
-
it("
|
|
427
|
-
|
|
428
|
-
expect(
|
|
322
|
+
it("returns undefined if from >= until", () => {
|
|
323
|
+
expect(findTruthy([0, 1, 2], { from: 2, until: 2 })).toBeUndefined()
|
|
324
|
+
expect(findTruthy([0, 1, 2], { from: 3, until: 2 })).toBeUndefined()
|
|
429
325
|
})
|
|
430
326
|
})
|