@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tim-code/my-util",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "Tim Sprowl",
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
+ })