diff --git a/collections/deno.json b/collections/deno.json index 3dbfc5545..e3fc369cb 100644 --- a/collections/deno.json +++ b/collections/deno.json @@ -46,9 +46,10 @@ "./take-last-while": "./take_last_while.ts", "./take-while": "./take_while.ts", "./union": "./union.ts", + "./unstable-chunk": "./unstable_chunk.ts", "./unstable-sort-by": "./unstable_sort_by.ts", "./unstable-take-while": "./unstable_take_while.ts", - "./unstable-chunk": "./unstable_chunk.ts", + "./unstable-without-all": "./unstable_without_all.ts", "./unzip": "./unzip.ts", "./without-all": "./without_all.ts", "./zip": "./zip.ts" diff --git a/collections/unstable_without_all.ts b/collections/unstable_without_all.ts new file mode 100644 index 000000000..d622ad40c --- /dev/null +++ b/collections/unstable_without_all.ts @@ -0,0 +1,41 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// This module is browser compatible. + +/** + * Returns an array excluding all given values from an iterable. + * + * @experimental **UNSTABLE**: New API, yet to be vetted. + * + * @typeParam T The type of the elements in the iterable. + * + * @param iterable The iterable to exclude values from. + * @param values The values to exclude from the iterable. + * + * @returns An array containing all elements from iterables except the + * ones that are in the values iterable. + * + * @remarks + * If both inputs are a {@linkcode Set}, and you want the difference as a + * {@linkcode Set}, you could use {@linkcode Set.prototype.difference} instead. + * + * @example Basic usage + * ```ts + * import { withoutAll } from "@std/collections/unstable-without-all"; + * import { assertEquals } from "@std/assert"; + * + * const withoutList = withoutAll([2, 1, 2, 3], [1, 2]); + * + * assertEquals(withoutList, [3]); + * ``` + */ +export function withoutAll(iterable: Iterable, values: Iterable): T[] { + const excludedSet = new Set(values); + const result: T[] = []; + for (const value of iterable) { + if (excludedSet.has(value)) { + continue; + } + result.push(value); + } + return result; +} diff --git a/collections/unstable_without_all_test.ts b/collections/unstable_without_all_test.ts new file mode 100644 index 000000000..a8e01e9d8 --- /dev/null +++ b/collections/unstable_without_all_test.ts @@ -0,0 +1,105 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { assertEquals } from "@std/assert"; +import { withoutAll } from "./unstable_without_all.ts"; + +function withoutAllTest( + input: Array, + excluded: Array, + expected: Array, + message?: string, +) { + const actual = withoutAll(input, excluded); + assertEquals(actual, expected, message); +} + +Deno.test({ + name: "(unstable) withoutAll() handles no mutation", + fn() { + const array = [1, 2, 3, 4]; + withoutAll(array, [2, 3]); + assertEquals(array, [1, 2, 3, 4]); + }, +}); + +Deno.test({ + name: "(unstable) withoutAll() handles empty input", + fn() { + withoutAllTest([], [], []); + }, +}); + +Deno.test({ + name: "(unstable) withoutAll() handles no matches", + fn() { + withoutAllTest([1, 2, 3, 4], [0, 7, 9], [1, 2, 3, 4]); + }, +}); + +Deno.test({ + name: "(unstable) withoutAll() handles single match", + fn() { + withoutAllTest([1, 2, 3, 4], [1], [2, 3, 4]); + withoutAllTest([1, 2, 3, 2], [2], [1, 3]); + }, +}); + +Deno.test({ + name: "(unstable) withoutAll() handles multiple matches", + fn() { + withoutAllTest([1, 2, 3, 4, 6, 3], [1, 2], [3, 4, 6, 3]); + withoutAllTest([7, 2, 9, 8, 7, 6, 5, 7], [7, 9], [2, 8, 6, 5]); + }, +}); + +Deno.test({ + name: "(unstable) withoutAll() leaves duplicate elements", + fn() { + withoutAllTest( + Array.from({ length: 110 }, () => 3), + [1], + Array.from({ length: 110 }, () => 3), + ); + }, +}); + +Deno.test("(unstable) withoutAll() handles generators", () => { + function* genInput() { + yield 1; + yield 2; + yield 3; + yield 4; + } + function* genExcluded() { + yield 2; + yield 3; + } + const result = withoutAll(genInput(), genExcluded()); + assertEquals(result, [1, 4]); +}); + +Deno.test("(unstable) withoutAll() handles iterators", () => { + const input = new Set([1, 2, 3, 4]); + const excluded = new Set([2, 3]); + const result = withoutAll(input.values(), excluded.values()); + assertEquals(result, [1, 4]); +}); + +Deno.test("(unstable) withoutAll() handles a mix of inputs", () => { + const a = [1, 2, 3, 4]; + const b = new Set([2, 3, 5]); + assertEquals(withoutAll(a, b), [1, 4], "Array and Set"); + assertEquals(withoutAll(b, a), [5], "Set and Array"); +}); + +Deno.test("(unstable) withoutAll() handles allows excluded to be a superset of types", () => { + const a = [1, 2, 3, 4]; + const b = [1, "other", 3, 4]; + assertEquals(withoutAll(a, b), [2]); +}); + +Deno.test("(unstable) withoutAll() works with sets", () => { + const a = new Set([1, 2, 3, 4]); + const b = new Set([2, 3]); + assertEquals(withoutAll(a, b), [1, 4]); +});