diff --git a/collections/README.md b/collections/README.md index 6dc5897c8980..e51482783d2a 100644 --- a/collections/README.md +++ b/collections/README.md @@ -524,6 +524,21 @@ console.assert( ); ``` +### minWith + +Returns the first element having the smallest value according to the provided +comparator or undefined if there are no elements + +```ts +import { minWith } from "https://deno.land/std@$STD_VERSION/collections/mod.ts"; +import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts"; + +const people = ["Kim", "Anna", "John"]; +const smallestName = minWith(people, (a, b) => a.length - b.length); + +assertEquals(smallestName, "Kim"); +``` + ### includesValue If the given value is part of the given object it returns true, otherwise it diff --git a/collections/min_with.ts b/collections/min_with.ts new file mode 100644 index 000000000000..d7ce6d83a723 --- /dev/null +++ b/collections/min_with.ts @@ -0,0 +1,31 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +/** + * Returns the first element having the smallest value according to the provided comparator or undefined if there are no elements + * + * Example: + * + * ```ts + * import { minWith } from "./min_with.ts"; + * import { assertEquals } from "../testing/asserts.ts"; + * + * const people = ["Kim", "Anna", "John"]; + * const smallestName = minWith(people, (a, b) => a.length - b.length); + * + * assertEquals(smallestName, "Kim"); + * ``` + */ +export function minWith( + array: readonly T[], + comparator: (a: T, b: T) => number, +): T | undefined { + let min: T | undefined = undefined; + + for (const current of array) { + if (min === undefined || comparator(current, min) < 0) { + min = current; + } + } + + return min; +} diff --git a/collections/min_with_test.ts b/collections/min_with_test.ts new file mode 100644 index 000000000000..78484e692d96 --- /dev/null +++ b/collections/min_with_test.ts @@ -0,0 +1,71 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +import { assertEquals } from "../testing/asserts.ts"; +import { minWith } from "./min_with.ts"; + +function minWithTest( + input: [T[], (a: T, b: T) => number], + expected: T | undefined, + message?: string, +) { + const actual = minWith(...input); + assertEquals(actual, expected, message); +} + +Deno.test({ + name: "[collections/minWith] no mutation", + fn() { + const input = [[1, 3], [6, 1, 3], [4]]; + minWith(input, (a, b) => a.length - b.length); + + assertEquals(input, [[1, 3], [6, 1, 3], [4]]); + }, +}); + +Deno.test({ + name: "[collections/minWith] empty input", + fn() { + minWithTest([[], (a, b) => a.length - b.length], undefined); + }, +}); + +Deno.test({ + name: "[collections/minWith] array of arrays", + fn() { + minWithTest([[[1, 3], [6, 1, 3], [4]], (a, b) => a.length - b.length], [4]); + }, +}); + +Deno.test({ + name: "[collections/minWith] array of strings", + fn() { + minWithTest( + [["Kim", "Anna", "John"], (a, b) => a.length - b.length], + "Kim", + ); + }, +}); + +Deno.test({ + name: "[collections/minWith] array of objects", + fn() { + minWithTest( + [ + [ + { name: "Kim", age: 24 }, + { name: "Anna", age: 20 }, + { name: "John", age: 43 }, + ], + (a, b) => a.age - b.age, + ], + { name: "Anna", age: 20 }, + ); + }, +}); + +Deno.test({ + name: "[collections/minWith] duplicates", + fn() { + minWithTest([["John", "Kim", "Kim"], (a, b) => a.length - b.length], "Kim"); + }, +}); diff --git a/collections/mod.ts b/collections/mod.ts index 6ae39b31a39a..932adc1180d1 100644 --- a/collections/mod.ts +++ b/collections/mod.ts @@ -29,6 +29,7 @@ export * from "./union.ts"; export * from "./without_all.ts"; export * from "./unzip.ts"; export * from "./zip.ts"; +export * from "./min_with.ts"; export * from "./includes_value.ts"; export * from "./take_last_while.ts"; export * from "./take_while.ts";