diff --git a/collections/distinct_by.ts b/collections/distinct_by.ts index 94482cc0e3ce..f4bac2143550 100644 --- a/collections/distinct_by.ts +++ b/collections/distinct_by.ts @@ -29,12 +29,13 @@ */ export function distinctBy( array: Iterable, - discriminator: (el: T) => D, + discriminator: (el: T, index: number) => D, ): T[] { const keys = new Set(); const result: T[] = []; + let index = 0; for (const element of array) { - const key = discriminator(element); + const key = discriminator(element, index++); if (!keys.has(key)) { keys.add(key); result.push(element); diff --git a/collections/distinct_by_test.ts b/collections/distinct_by_test.ts index 8fc4d8568bfd..49e8df874645 100644 --- a/collections/distinct_by_test.ts +++ b/collections/distinct_by_test.ts @@ -5,7 +5,7 @@ import { distinctBy } from "./distinct_by.ts"; function distinctByTest( array: Array, - selector: (element: I) => unknown, + selector: (element: I, index: number) => unknown, expected: Array, message?: string, ) { @@ -117,3 +117,14 @@ Deno.test({ ); }, }); + +Deno.test({ + name: "distinctBy() passes index to discriminator", + fn() { + distinctByTest( + [25, "asdf", true], + (_, index) => index > 1, + [25, true], + ); + }, +}); diff --git a/collections/drop_last_while.ts b/collections/drop_last_while.ts index f65b0cb3b788..628538b7aca6 100644 --- a/collections/drop_last_while.ts +++ b/collections/drop_last_while.ts @@ -27,11 +27,11 @@ */ export function dropLastWhile( iterable: Iterable, - predicate: (el: T) => boolean, + predicate: (el: T, index: number) => boolean, ): T[] { const array = Array.isArray(iterable) ? iterable : Array.from(iterable); let offset = array.length - 1; - while (offset >= 0 && predicate(array[offset]!)) { + while (offset >= 0 && predicate(array[offset]!, offset)) { offset--; } return array.slice(0, offset + 1); diff --git a/collections/drop_last_while_test.ts b/collections/drop_last_while_test.ts index ec1e0883fe67..9e429cd29b96 100644 --- a/collections/drop_last_while_test.ts +++ b/collections/drop_last_while_test.ts @@ -80,3 +80,11 @@ Deno.test("dropLastWhile() handles a generator", () => { const actual = dropLastWhile(gen(), (i) => i > 30); assertEquals(actual, [20]); }); + +Deno.test("dropLastWhile() passes index to predicate", () => { + const array = [20, 30, 20]; + + const actual = dropLastWhile(array, (_, index) => index > 1); + + assertEquals(actual, [20, 30]); +}); diff --git a/collections/drop_while.ts b/collections/drop_while.ts index 3b345d613f1c..c6bf6a27e8bd 100644 --- a/collections/drop_while.ts +++ b/collections/drop_while.ts @@ -26,19 +26,20 @@ */ export function dropWhile( iterable: Iterable, - predicate: (el: T) => boolean, + predicate: (el: T, index: number) => boolean, ): T[] { if (Array.isArray(iterable)) { - const idx = iterable.findIndex((el) => !predicate(el)); + const idx = iterable.findIndex((el, index) => !predicate(el, index)); if (idx === -1) { return []; } return iterable.slice(idx); } const array: T[] = []; + let index = 0; let found = false; for (const item of iterable) { - if (found || !predicate(item)) { + if (found || !predicate(item, index++)) { found = true; array.push(item); } diff --git a/collections/drop_while_test.ts b/collections/drop_while_test.ts index f6eacb907dee..2eab805c9cb2 100644 --- a/collections/drop_while_test.ts +++ b/collections/drop_while_test.ts @@ -107,3 +107,11 @@ Deno.test("dropWhile() handles a Map", () => { ["f", 6], ]); }); + +Deno.test("dropWhile() passes index to predicate", () => { + const array = [20, 30, 20]; + + const actual = dropWhile(array, (_, index) => index < 1); + + assertEquals(actual, [30, 20]); +}); diff --git a/collections/find_single.ts b/collections/find_single.ts index d9e94dd3af18..ce2ecf551783 100644 --- a/collections/find_single.ts +++ b/collections/find_single.ts @@ -32,12 +32,13 @@ */ export function findSingle( array: Iterable, - predicate: (el: T) => boolean, + predicate: (el: T, index: number) => boolean, ): T | undefined { let match: T | undefined; let found = false; + let index = 0; for (const element of array) { - if (predicate(element)) { + if (predicate(element, index++)) { if (found) return undefined; found = true; match = element; diff --git a/collections/find_single_test.ts b/collections/find_single_test.ts index b627416b4ed3..642746e8ee35 100644 --- a/collections/find_single_test.ts +++ b/collections/find_single_test.ts @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert"; import { findSingle } from "./find_single.ts"; function findSingleTest( - input: [Array, (element: I) => boolean], + input: [Array, (element: I, index: number) => boolean], expected: I | undefined, message?: string, ) { @@ -103,3 +103,13 @@ Deno.test({ ); }, }); + +Deno.test({ + name: "findSingle() passes index to predicate", + fn() { + findSingleTest( + [[9, 12, 13], (_, index) => index === 1], + 12, + ); + }, +}); diff --git a/collections/first_not_nullish_of.ts b/collections/first_not_nullish_of.ts index 01cc329aba4f..f9fdd648ebfb 100644 --- a/collections/first_not_nullish_of.ts +++ b/collections/first_not_nullish_of.ts @@ -33,10 +33,11 @@ */ export function firstNotNullishOf( array: Iterable, - selector: (item: T) => O | undefined | null, + selector: (item: T, index: number) => O | undefined | null, ): NonNullable | undefined { + let index = 0; for (const current of array) { - const selected = selector(current); + const selected = selector(current, index++); if (selected !== null && selected !== undefined) { return selected as NonNullable; diff --git a/collections/first_not_nullish_of_test.ts b/collections/first_not_nullish_of_test.ts index 32fdf4fad4a1..bce5be238204 100644 --- a/collections/first_not_nullish_of_test.ts +++ b/collections/first_not_nullish_of_test.ts @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert"; import { firstNotNullishOf } from "./first_not_nullish_of.ts"; function firstNotNullishOfTest( - input: [Array, (el: T) => O | undefined | null], + input: [Array, (el: T, index: number) => O | undefined | null], expected: NonNullable | undefined, message?: string, ) { @@ -85,3 +85,13 @@ Deno.test({ ); }, }); + +Deno.test({ + name: "firstNotNullishOf() passes index to selector", + fn() { + firstNotNullishOfTest( + [[1, 2, 3, 4], (it, index) => index < 1 ? null : it], + 2, + ); + }, +}); diff --git a/collections/join_to_string.ts b/collections/join_to_string.ts index e5e3b675bed6..10833681decc 100644 --- a/collections/join_to_string.ts +++ b/collections/join_to_string.ts @@ -77,7 +77,7 @@ export type JoinToStringOptions = { */ export function joinToString( array: Iterable, - selector: (el: T) => string, + selector: (el: T, index: number) => string, options: Readonly = {}, ): string { const { @@ -101,7 +101,7 @@ export function joinToString( break; } - result += selector(el); + result += selector(el, index); index++; } diff --git a/collections/join_to_string_test.ts b/collections/join_to_string_test.ts index 6ee12ac7d9dd..7fae6922a154 100644 --- a/collections/join_to_string_test.ts +++ b/collections/join_to_string_test.ts @@ -144,3 +144,14 @@ Deno.test({ assertEquals(out, "result: Kim and others are winners"); }, }); + +Deno.test({ + name: "joinToString() passes index to selector", + fn() { + const arr = ["Kim", "Anna", "Tim"]; + + const out = joinToString(arr, (it, index) => it + index); + + assertEquals(out, "Kim0,Anna1,Tim2"); + }, +}); diff --git a/collections/map_not_nullish.ts b/collections/map_not_nullish.ts index 0ffa87994410..26d775429dea 100644 --- a/collections/map_not_nullish.ts +++ b/collections/map_not_nullish.ts @@ -33,12 +33,13 @@ */ export function mapNotNullish( array: Iterable, - transformer: (el: T) => O, + transformer: (el: T, index: number) => O, ): NonNullable[] { const result: NonNullable[] = []; + let index = 0; for (const element of array) { - const transformedElement = transformer(element); + const transformedElement = transformer(element, index++); if (transformedElement !== undefined && transformedElement !== null) { result.push(transformedElement as NonNullable); diff --git a/collections/map_not_nullish_test.ts b/collections/map_not_nullish_test.ts index e6efbc4ae786..dabffa890315 100644 --- a/collections/map_not_nullish_test.ts +++ b/collections/map_not_nullish_test.ts @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert"; import { mapNotNullish } from "./map_not_nullish.ts"; function mapNotNullishTest( - input: [Array, (el: T) => O | undefined | null], + input: [Array, (el: T, index: number) => O | undefined | null], expected: Array, message?: string, ) { @@ -90,3 +90,16 @@ Deno.test({ ); }, }); + +Deno.test({ + name: "mapNotNullish() passes index to transformer", + fn() { + mapNotNullishTest( + [ + [1, 2, 3, 4], + (it, index) => index === 1 ? null : it + index, + ], + [1, 5, 7], + ); + }, +}); diff --git a/collections/max_by.ts b/collections/max_by.ts index 968875e2d824..899280f6feb8 100644 --- a/collections/max_by.ts +++ b/collections/max_by.ts @@ -31,7 +31,7 @@ */ export function maxBy( array: Iterable, - selector: (el: T) => number, + selector: (el: T, index: number) => number, ): T | undefined; /** * Returns the first element that is the largest value of the given function or @@ -63,7 +63,7 @@ export function maxBy( */ export function maxBy( array: Iterable, - selector: (el: T) => string, + selector: (el: T, index: number) => string, ): T | undefined; /** * Returns the first element that is the largest value of the given function or @@ -95,7 +95,7 @@ export function maxBy( */ export function maxBy( array: Iterable, - selector: (el: T) => bigint, + selector: (el: T, index: number) => bigint, ): T | undefined; /** * Returns the first element that is the largest value of the given function or @@ -127,21 +127,22 @@ export function maxBy( */ export function maxBy( array: Iterable, - selector: (el: T) => Date, + selector: (el: T, index: number) => Date, ): T | undefined; export function maxBy( array: Iterable, selector: - | ((el: T) => number) - | ((el: T) => string) - | ((el: T) => bigint) - | ((el: T) => Date), + | ((el: T, index: number) => number) + | ((el: T, index: number) => string) + | ((el: T, index: number) => bigint) + | ((el: T, index: number) => Date), ): T | undefined { let max: T | undefined; let maxValue: ReturnType | undefined; + let index = 0; for (const current of array) { - const currentValue = selector(current); + const currentValue = selector(current, index++); if (maxValue === undefined || currentValue > maxValue) { max = current; diff --git a/collections/max_by_test.ts b/collections/max_by_test.ts index e5b4b95a0e83..8f100855d5dc 100644 --- a/collections/max_by_test.ts +++ b/collections/max_by_test.ts @@ -136,3 +136,14 @@ Deno.test({ assertEquals(maxBy(input, (it) => new Date(it)), "February 1, 2022"); }, }); + +Deno.test({ + name: "maxBy() passes index to selector", + fn() { + const input = [4, 3, 2, 1]; + + const max = maxBy(input, (_, index) => index); + + assertEquals(max, 1); + }, +}); diff --git a/collections/max_of.ts b/collections/max_of.ts index 17a5101c91d6..972e3e4e0f0c 100644 --- a/collections/max_of.ts +++ b/collections/max_of.ts @@ -32,7 +32,7 @@ */ export function maxOf( array: Iterable, - selector: (el: T) => number, + selector: (el: T, index: number) => number, ): number | undefined; /** * Applies the given selector to all elements of the provided collection and @@ -65,16 +65,22 @@ export function maxOf( */ export function maxOf( array: Iterable, - selector: (el: T) => bigint, + selector: (el: T, index: number) => bigint, ): bigint | undefined; -export function maxOf number) | ((el: T) => bigint)>( +export function maxOf< + T, + S extends + | ((el: T, index: number) => number) + | ((el: T, index: number) => bigint), +>( array: Iterable, selector: S, ): ReturnType | undefined { let maximumValue: ReturnType | undefined; + let index = 0; for (const element of array) { - const currentValue = selector(element) as ReturnType; + const currentValue = selector(element, index++) as ReturnType; if (maximumValue === undefined || currentValue > maximumValue) { maximumValue = currentValue; diff --git a/collections/max_of_test.ts b/collections/max_of_test.ts index f93852aa6ada..16763d9d1c25 100644 --- a/collections/max_of_test.ts +++ b/collections/max_of_test.ts @@ -100,3 +100,14 @@ Deno.test("maxOf() handles infinity", () => { assertEquals(actual, Infinity); }); + +Deno.test({ + name: "maxBy() passes index to selector", + fn() { + const input = [4, 3, 2, 1]; + + const max = maxOf(input, (it, index) => it * index); + + assertEquals(max, 4); + }, +}); diff --git a/collections/min_by.ts b/collections/min_by.ts index dee32c2ee4df..28a1f09ae285 100644 --- a/collections/min_by.ts +++ b/collections/min_by.ts @@ -31,7 +31,7 @@ */ export function minBy( array: Iterable, - selector: (el: T) => number, + selector: (el: T, index: number) => number, ): T | undefined; /** * Returns the first element that is the smallest value of the given function or @@ -63,7 +63,7 @@ export function minBy( */ export function minBy( array: Iterable, - selector: (el: T) => string, + selector: (el: T, index: number) => string, ): T | undefined; /** * Returns the first element that is the smallest value of the given function or @@ -95,7 +95,7 @@ export function minBy( */ export function minBy( array: Iterable, - selector: (el: T) => bigint, + selector: (el: T, index: number) => bigint, ): T | undefined; /** * Returns the first element that is the smallest value of the given function or @@ -125,21 +125,22 @@ export function minBy( */ export function minBy( array: Iterable, - selector: (el: T) => Date, + selector: (el: T, index: number) => Date, ): T | undefined; export function minBy( array: Iterable, selector: - | ((el: T) => number) - | ((el: T) => string) - | ((el: T) => bigint) - | ((el: T) => Date), + | ((el: T, index: number) => number) + | ((el: T, index: number) => string) + | ((el: T, index: number) => bigint) + | ((el: T, index: number) => Date), ): T | undefined { let min: T | undefined; let minValue: ReturnType | undefined; + let index = 0; for (const current of array) { - const currentValue = selector(current); + const currentValue = selector(current, index++); if (minValue === undefined || currentValue < minValue) { min = current; diff --git a/collections/min_by_test.ts b/collections/min_by_test.ts index b52c338e411c..e4e2d411bfaf 100644 --- a/collections/min_by_test.ts +++ b/collections/min_by_test.ts @@ -137,3 +137,14 @@ Deno.test({ assertEquals(minBy(input, (it) => new Date(it)), "December 17, 1995"); }, }); + +Deno.test({ + name: "minBy() passes index to selector", + fn() { + const input = [4, 3, 2, 1]; + + const max = minBy(input, (_, index) => index); + + assertEquals(max, 4); + }, +}); diff --git a/collections/min_of.ts b/collections/min_of.ts index 0cbe51d9202a..51d1295776d5 100644 --- a/collections/min_of.ts +++ b/collections/min_of.ts @@ -32,7 +32,7 @@ */ export function minOf( array: Iterable, - selector: (el: T) => number, + selector: (el: T, index: number) => number, ): number | undefined; /** * Applies the given selector to all elements of the given collection and @@ -65,16 +65,22 @@ export function minOf( */ export function minOf( array: Iterable, - selector: (el: T) => bigint, + selector: (el: T, index: number) => bigint, ): bigint | undefined; -export function minOf number) | ((el: T) => bigint)>( +export function minOf< + T, + S extends + | ((el: T, index: number) => number) + | ((el: T, index: number) => bigint), +>( array: Iterable, selector: S, ): ReturnType | undefined { let minimumValue: ReturnType | undefined; + let index = 0; for (const element of array) { - const currentValue = selector(element) as ReturnType; + const currentValue = selector(element, index++) as ReturnType; if (minimumValue === undefined || currentValue < minimumValue) { minimumValue = currentValue; diff --git a/collections/min_of_test.ts b/collections/min_of_test.ts index 7f98528ea508..aec7eccc53af 100644 --- a/collections/min_of_test.ts +++ b/collections/min_of_test.ts @@ -100,3 +100,14 @@ Deno.test("minOf() handles minus infinity", () => { assertEquals(actual, -Infinity); }); + +Deno.test({ + name: "minOf() passes index to selector", + fn() { + const input = [4, 3, 2, 1]; + + const max = minOf(input, (it, index) => it * index); + + assertEquals(max, 0); + }, +}); diff --git a/collections/partition.ts b/collections/partition.ts index 1aa03a469cc9..ded7c4078391 100644 --- a/collections/partition.ts +++ b/collections/partition.ts @@ -29,7 +29,7 @@ */ export function partition( array: Iterable, - predicate: (el: T) => boolean, + predicate: (el: T, index: number) => boolean, ): [T[], T[]]; /** * Returns a tuple of two arrays with the first one containing all elements in @@ -64,17 +64,18 @@ export function partition( */ export function partition( array: Iterable, - predicate: (el: T) => el is U, + predicate: (el: T, index: number) => el is U, ): [U[], Exclude[]]; export function partition( array: Iterable, - predicate: (el: unknown) => boolean, + predicate: (el: unknown, index: number) => boolean, ): [unknown[], unknown[]] { const matches: Array = []; const rest: Array = []; + let index = 0; for (const element of array) { - if (predicate(element)) { + if (predicate(element, index++)) { matches.push(element); } else { rest.push(element); diff --git a/collections/partition_test.ts b/collections/partition_test.ts index 49a2c1c1a58f..e47a762a9bd4 100644 --- a/collections/partition_test.ts +++ b/collections/partition_test.ts @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert"; import { partition } from "./partition.ts"; function partitionTest( - input: [Array, (element: I) => boolean], + input: [Array, (element: I, index: number) => boolean], expected: [Array, Array], message?: string, ) { @@ -73,3 +73,13 @@ Deno.test({ ); }, }); + +Deno.test({ + name: "partition() passes index to predicate", + fn() { + partitionTest( + [[2, 4, 6], (_, index) => index % 2 === 0], + [[2, 6], [4]], + ); + }, +}); diff --git a/collections/permutations.ts b/collections/permutations.ts index 2570425197d2..b8436ec952ca 100644 --- a/collections/permutations.ts +++ b/collections/permutations.ts @@ -2,7 +2,7 @@ // This module is browser compatible. /** - * Builds all possible orders of all elements in the given array + * Builds all possible orders of all elements in the given array. * Ignores equality of elements, meaning this will always return the same * number of permutations for a given length of input. * diff --git a/collections/sort_by.ts b/collections/sort_by.ts index a9c2d1b474d3..77d53885df0a 100644 --- a/collections/sort_by.ts +++ b/collections/sort_by.ts @@ -118,16 +118,18 @@ export type SortByOptions = { export function sortBy( iterator: Iterable, selector: - | ((el: T) => number) - | ((el: T) => string) - | ((el: T) => bigint) - | ((el: T) => Date), + | ((el: T, index: number) => number) + | ((el: T, index: number) => string) + | ((el: T, index: number) => bigint) + | ((el: T, index: number) => Date), options?: SortByOptions, ): T[] { const array: { value: T; selected: string | number | bigint | Date }[] = []; + let index = 0; for (const item of iterator) { - array.push({ value: item, selected: selector(item) }); + array.push({ value: item, selected: selector(item, index) }); + index++; } array.sort((oa, ob) => { diff --git a/collections/sort_by_test.ts b/collections/sort_by_test.ts index 3260fa67caae..5a7a1f76a082 100644 --- a/collections/sort_by_test.ts +++ b/collections/sort_by_test.ts @@ -254,3 +254,10 @@ Deno.test({ ]); }, }); + +Deno.test({ + name: "sortBy() passes index to selector", + fn() { + assertEquals(sortBy([2, 3, 1], (_, index) => -index), [1, 3, 2]); + }, +}); diff --git a/collections/sum_of.ts b/collections/sum_of.ts index 349a31268fd7..8e25914ca939 100644 --- a/collections/sum_of.ts +++ b/collections/sum_of.ts @@ -30,12 +30,13 @@ */ export function sumOf( array: Iterable, - selector: (el: T) => number, + selector: (el: T, index: number) => number, ): number { let sum = 0; + let index = 0; for (const i of array) { - sum += selector(i); + sum += selector(i, index++); } return sum; diff --git a/collections/sum_of_test.ts b/collections/sum_of_test.ts index f20b4ac3ae81..d270f13b4570 100644 --- a/collections/sum_of_test.ts +++ b/collections/sum_of_test.ts @@ -130,3 +130,11 @@ Deno.test("sumOf() handles Infinity", () => { assertEquals(actual, Infinity); }); + +Deno.test("sumOf() passes index to selector", () => { + const array = [1, 2, 3]; + + const actual = sumOf(array, (_, index) => index); + + assertEquals(actual, 3); +}); diff --git a/collections/take_last_while.ts b/collections/take_last_while.ts index 5d11ef28113a..448a401f6f02 100644 --- a/collections/take_last_while.ts +++ b/collections/take_last_while.ts @@ -26,18 +26,19 @@ */ export function takeLastWhile( iterable: Iterable, - predicate: (el: T) => boolean, + predicate: (el: T, index: number) => boolean, ): T[] { if (Array.isArray(iterable)) { let offset = iterable.length; - while (0 < offset && predicate(iterable[offset - 1] as T)) { + while (0 < offset && predicate(iterable[offset - 1] as T, offset - 1)) { offset--; } return iterable.slice(offset); } + let index = 0; const result: T[] = []; for (const el of iterable) { - if (predicate(el)) { + if (predicate(el, index++)) { result.push(el); } else { result.length = 0; diff --git a/collections/take_last_while_test.ts b/collections/take_last_while_test.ts index 74557b91f69b..f641aef806c9 100644 --- a/collections/take_last_while_test.ts +++ b/collections/take_last_while_test.ts @@ -116,3 +116,11 @@ Deno.test("takeLastWhile() gets from last matching element from a generator", () const actual = takeLastWhile(gen(), (i) => i !== 2 && i !== 4); assertEquals(actual, [5, 6]); }); + +Deno.test("takeLastWhile() passes the index to the predicate", () => { + const arr = [1, 2, 3, 4]; + + const actual = takeLastWhile(arr, (_, index) => index > 1); + + assertEquals(actual, [3, 4]); +}); diff --git a/collections/take_while.ts b/collections/take_while.ts index ba9b51cadf6a..b23566ec5b9c 100644 --- a/collections/take_while.ts +++ b/collections/take_while.ts @@ -28,11 +28,12 @@ */ export function takeWhile( iterable: Iterable, - predicate: (el: T) => boolean, + predicate: (el: T, index: number) => boolean, ): T[] { + let index = 0; const result: T[] = []; for (const element of iterable) { - if (!predicate(element)) { + if (!predicate(element, index++)) { break; } result.push(element); diff --git a/collections/take_while_test.ts b/collections/take_while_test.ts index ff27e261b9e3..fd7114cf24a7 100644 --- a/collections/take_while_test.ts +++ b/collections/take_while_test.ts @@ -90,3 +90,11 @@ Deno.test("takeWhile() handles a Map", () => { ["c", 3], ]); }); + +Deno.test("takeWhile() passes the index to the predicate", () => { + const arr = [1, 2, 3, 4]; + + const actual = takeWhile(arr, (_, index) => index < 1); + + assertEquals(actual, [1]); +});