Skip to content

feat(collections): add index arg to methods #6382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions collections/distinct_by.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@
*/
export function distinctBy<T, D>(
array: Iterable<T>,
discriminator: (el: T) => D,
discriminator: (el: T, index: number) => D,
): T[] {
const keys = new Set<D>();
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);
Expand Down
13 changes: 12 additions & 1 deletion collections/distinct_by_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { distinctBy } from "./distinct_by.ts";

function distinctByTest<I>(
array: Array<I>,
selector: (element: I) => unknown,
selector: (element: I, index: number) => unknown,
expected: Array<I>,
message?: string,
) {
Expand Down Expand Up @@ -117,3 +117,14 @@ Deno.test({
);
},
});

Deno.test({
name: "distinctBy() passes index to discriminator",
fn() {
distinctByTest(
[25, "asdf", true],
(_, index) => index > 1,
[25, true],
);
},
});
4 changes: 2 additions & 2 deletions collections/drop_last_while.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
*/
export function dropLastWhile<T>(
array: readonly T[],
predicate: (el: T) => boolean,
predicate: (el: T, index: number) => boolean,
): T[] {
let offset = array.length;
while (0 < offset && predicate(array[offset - 1] as T)) offset--;
while (0 < offset && predicate(array[offset - 1] as T, offset - 1)) offset--;

return array.slice(0, offset);
}
8 changes: 8 additions & 0 deletions collections/drop_last_while_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ Deno.test("dropLastWhile() returns empty array when all elements get dropped", (

assertEquals(actual, []);
});

Deno.test("dropLastWhile() passes index to predicate", () => {
const array = [20, 30, 20];

const actual = dropLastWhile(array, (_, index) => index > 1);

assertEquals(actual, [20, 30]);
});
4 changes: 2 additions & 2 deletions collections/drop_while.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
*/
export function dropWhile<T>(
array: readonly T[],
predicate: (el: T) => boolean,
predicate: (el: T, index: number) => boolean,
): T[] {
let offset = 0;
const length = array.length;

while (length > offset && predicate(array[offset] as T)) {
while (length > offset && predicate(array[offset] as T, offset)) {
offset++;
}

Expand Down
8 changes: 8 additions & 0 deletions collections/drop_while_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ Deno.test("dropWhile() returns the same array when all elements match the predic

assertEquals(actual, []);
});

Deno.test("dropWhile() passes index to predicate", () => {
const array = [20, 30, 20];

const actual = dropWhile(array, (_, index) => index < 1);

assertEquals(actual, [30, 20]);
});
5 changes: 3 additions & 2 deletions collections/find_single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@
*/
export function findSingle<T>(
array: Iterable<T>,
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;
Expand Down
12 changes: 11 additions & 1 deletion collections/find_single_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert";
import { findSingle } from "./find_single.ts";

function findSingleTest<I>(
input: [Array<I>, (element: I) => boolean],
input: [Array<I>, (element: I, index: number) => boolean],
expected: I | undefined,
message?: string,
) {
Expand Down Expand Up @@ -103,3 +103,13 @@ Deno.test({
);
},
});

Deno.test({
name: "findSingle() passes index to predicate",
fn() {
findSingleTest(
[[9, 12, 13], (_, index) => index === 1],
12,
);
},
});
5 changes: 3 additions & 2 deletions collections/first_not_nullish_of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@
*/
export function firstNotNullishOf<T, O>(
array: Iterable<T>,
selector: (item: T) => O | undefined | null,
selector: (item: T, index: number) => O | undefined | null,
): NonNullable<O> | 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<O>;
Expand Down
12 changes: 11 additions & 1 deletion collections/first_not_nullish_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert";
import { firstNotNullishOf } from "./first_not_nullish_of.ts";

function firstNotNullishOfTest<T, O>(
input: [Array<T>, (el: T) => O | undefined | null],
input: [Array<T>, (el: T, index: number) => O | undefined | null],
expected: NonNullable<O> | undefined,
message?: string,
) {
Expand Down Expand Up @@ -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,
);
},
});
4 changes: 2 additions & 2 deletions collections/join_to_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export type JoinToStringOptions = {
*/
export function joinToString<T>(
array: Iterable<T>,
selector: (el: T) => string,
selector: (el: T, index: number) => string,
options: Readonly<JoinToStringOptions> = {},
): string {
const {
Expand All @@ -101,7 +101,7 @@ export function joinToString<T>(
break;
}

result += selector(el);
result += selector(el, index);
index++;
}

Expand Down
11 changes: 11 additions & 0 deletions collections/join_to_string_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
},
});
5 changes: 3 additions & 2 deletions collections/map_not_nullish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@
*/
export function mapNotNullish<T, O>(
array: Iterable<T>,
transformer: (el: T) => O,
transformer: (el: T, index: number) => O,
): NonNullable<O>[] {
const result: NonNullable<O>[] = [];
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<O>);
Expand Down
15 changes: 14 additions & 1 deletion collections/map_not_nullish_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { assertEquals } from "@std/assert";
import { mapNotNullish } from "./map_not_nullish.ts";

function mapNotNullishTest<T, O>(
input: [Array<T>, (el: T) => O | undefined | null],
input: [Array<T>, (el: T, index: number) => O | undefined | null],
expected: Array<O>,
message?: string,
) {
Expand Down Expand Up @@ -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],
);
},
});
19 changes: 10 additions & 9 deletions collections/max_by.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
*/
export function maxBy<T>(
array: Iterable<T>,
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
Expand Down Expand Up @@ -63,7 +63,7 @@ export function maxBy<T>(
*/
export function maxBy<T>(
array: Iterable<T>,
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
Expand Down Expand Up @@ -95,7 +95,7 @@ export function maxBy<T>(
*/
export function maxBy<T>(
array: Iterable<T>,
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
Expand Down Expand Up @@ -127,21 +127,22 @@ export function maxBy<T>(
*/
export function maxBy<T>(
array: Iterable<T>,
selector: (el: T) => Date,
selector: (el: T, index: number) => Date,
): T | undefined;
export function maxBy<T>(
array: Iterable<T>,
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<typeof selector> | 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;
Expand Down
11 changes: 11 additions & 0 deletions collections/max_by_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
},
});
14 changes: 10 additions & 4 deletions collections/max_of.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
*/
export function maxOf<T>(
array: Iterable<T>,
selector: (el: T) => number,
selector: (el: T, index: number) => number,
): number | undefined;
/**
* Applies the given selector to all elements of the provided collection and
Expand Down Expand Up @@ -65,16 +65,22 @@ export function maxOf<T>(
*/
export function maxOf<T>(
array: Iterable<T>,
selector: (el: T) => bigint,
selector: (el: T, index: number) => bigint,
): bigint | undefined;
export function maxOf<T, S extends ((el: T) => number) | ((el: T) => bigint)>(
export function maxOf<
T,
S extends
| ((el: T, index: number) => number)
| ((el: T, index: number) => bigint),
>(
array: Iterable<T>,
selector: S,
): ReturnType<S> | undefined {
let maximumValue: ReturnType<S> | undefined;
let index = 0;

for (const element of array) {
const currentValue = selector(element) as ReturnType<S>;
const currentValue = selector(element, index++) as ReturnType<S>;

if (maximumValue === undefined || currentValue > maximumValue) {
maximumValue = currentValue;
Expand Down
11 changes: 11 additions & 0 deletions collections/max_of_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
},
});
Loading
Loading