@tim-code/my-util 0.4.1 → 0.4.3
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 +80 -0
- package/src/find.test.js +144 -0
package/package.json
CHANGED
package/src/find.js
CHANGED
|
@@ -268,3 +268,83 @@ export function findMax(array, { key, cutoff = -Infinity } = {}) {
|
|
|
268
268
|
}
|
|
269
269
|
return closest
|
|
270
270
|
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Like Array.prototype.findIndex but starts from given index.
|
|
274
|
+
* @template T
|
|
275
|
+
* @param {Array<T>} array
|
|
276
|
+
* @param {number} fromIndex
|
|
277
|
+
* @param {(value: T, index: number, array: Array<T>) => boolean} callback
|
|
278
|
+
* @returns {number}
|
|
279
|
+
*/
|
|
280
|
+
export function findIndexFrom(array, fromIndex, callback) {
|
|
281
|
+
for (let i = fromIndex; i < array.length; i++) {
|
|
282
|
+
if (callback(array[i], i, array)) {
|
|
283
|
+
return i
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return -1
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Like Array.prototype.find but starts from given index.
|
|
291
|
+
* @template T
|
|
292
|
+
* @param {Array<T>} array
|
|
293
|
+
* @param {number} fromIndex
|
|
294
|
+
* @param {(value: T, index: number, array: Array<T>) => boolean} callback
|
|
295
|
+
* @returns {T|undefined}
|
|
296
|
+
*/
|
|
297
|
+
export function findFrom(array, fromIndex, callback) {
|
|
298
|
+
const foundIndex = findIndexFrom(array, fromIndex, callback)
|
|
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
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return -1
|
|
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
|
|
350
|
+
}
|
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,6 +10,11 @@ const {
|
|
|
9
10
|
findClosest,
|
|
10
11
|
findMin,
|
|
11
12
|
findMax,
|
|
13
|
+
findIndexFrom,
|
|
14
|
+
findFrom,
|
|
15
|
+
findLastIndexFrom,
|
|
16
|
+
findLastFrom,
|
|
17
|
+
isTruthy,
|
|
12
18
|
} = await import("./find.js")
|
|
13
19
|
|
|
14
20
|
describe("findClosestAbs", () => {
|
|
@@ -284,3 +290,141 @@ describe("findMax", () => {
|
|
|
284
290
|
expect(findMax([3, 1, 4, 2], { cutoff: 4 })).toBeUndefined()
|
|
285
291
|
})
|
|
286
292
|
})
|
|
293
|
+
|
|
294
|
+
describe("findIndexFrom", () => {
|
|
295
|
+
it("returns the index of the first matching element from fromIndex", () => {
|
|
296
|
+
expect(findIndexFrom([1, 2, 3, 4], 1, (x) => x > 2)).toBe(2)
|
|
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)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it("returns -1 if no element matches", () => {
|
|
302
|
+
expect(findIndexFrom([1, 2, 3, 4], 2, (x) => x > 10)).toBe(-1)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it("returns -1 for empty array", () => {
|
|
306
|
+
expect(findIndexFrom([], 0, () => true)).toBe(-1)
|
|
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()
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it("passes correct arguments to callback", () => {
|
|
396
|
+
const arr = [10, 20, 30]
|
|
397
|
+
const called = []
|
|
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)
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
describe("isTruthy", () => {
|
|
409
|
+
it("returns the argument if it is truthy", () => {
|
|
410
|
+
expect(isTruthy(1)).toBe(1)
|
|
411
|
+
expect(isTruthy("foo")).toBe("foo")
|
|
412
|
+
expect(isTruthy({})).toEqual({})
|
|
413
|
+
expect(isTruthy([])).toEqual([])
|
|
414
|
+
expect(isTruthy(true)).toBe(true)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it("returns the argument if it is falsy", () => {
|
|
418
|
+
expect(isTruthy(0)).toBe(0)
|
|
419
|
+
expect(isTruthy("")).toBe("")
|
|
420
|
+
expect(isTruthy(null)).toBe(null)
|
|
421
|
+
expect(isTruthy(undefined)).toBe(undefined)
|
|
422
|
+
expect(isTruthy(false)).toBe(false)
|
|
423
|
+
expect(isTruthy(NaN)).toBe(NaN)
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it("can be used with Array.prototype.find to filter out falsy values", () => {
|
|
427
|
+
const arr = [0, null, undefined, false, "", "hello", 42]
|
|
428
|
+
expect(arr.find(isTruthy)).toBe("hello")
|
|
429
|
+
})
|
|
430
|
+
})
|