From a6f80810b2200da0cbb389f3945513a0b99c9499 Mon Sep 17 00:00:00 2001 From: tuhm1 <50200070+tuhm1@users.noreply.github.com> Date: Fri, 27 Dec 2024 16:38:57 +0700 Subject: [PATCH] perf(pull): faster `pull` (#915) * improve pull * fix lint error * refactor to use hasOwn --------- Co-authored-by: Sojin Park --- benchmarks/performance/pull.bench.ts | 42 ++++++++++++++++++++++++++++ src/array/pull.ts | 15 ++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 benchmarks/performance/pull.bench.ts diff --git a/benchmarks/performance/pull.bench.ts b/benchmarks/performance/pull.bench.ts new file mode 100644 index 000000000..c889a08bb --- /dev/null +++ b/benchmarks/performance/pull.bench.ts @@ -0,0 +1,42 @@ +import { bench, describe } from 'vitest'; +import { pull as pullToolkit } from 'es-toolkit/compat'; +import { pull as pullLodash } from 'lodash'; + +describe('pull array size 100', () => { + const array = [...Array(100)].map((_, i) => i); + const even = [...Array(50)].map((_, i) => i * 2); + + bench('es-toolkit/pull', () => { + pullToolkit([...array], even); + }); + + bench('lodash/pull', () => { + pullLodash([...array], ...even); + }); +}); + +describe('pull array size 1000', () => { + const array = [...Array(1000)].map((_, i) => i); + const even = [...Array(500)].map((_, i) => i * 2); + + bench('es-toolkit/pull', () => { + pullToolkit([...array], [...even]); + }); + + bench('lodash/pull', () => { + pullLodash([...array], ...even); + }); +}); + +describe('pull array size 10000', () => { + const array = [...Array(10000)].map((_, i) => i); + const even = [...Array(5000)].map((_, i) => i * 2); + + bench('es-toolkit/pull', () => { + pullToolkit([...array], [...even]); + }); + + bench('lodash/pull', () => { + pullLodash([...array], ...even); + }); +}); diff --git a/src/array/pull.ts b/src/array/pull.ts index 51d15019d..c12b10a80 100644 --- a/src/array/pull.ts +++ b/src/array/pull.ts @@ -16,12 +16,23 @@ */ export function pull(arr: T[], valuesToRemove: readonly unknown[]): T[] { const valuesSet = new Set(valuesToRemove); + let resultIndex = 0; - for (let i = arr.length - 1; i >= 0; i--) { + for (let i = 0; i < arr.length; i++) { if (valuesSet.has(arr[i])) { - arr.splice(i, 1); + continue; } + + // For handling sparse arrays + if (!Object.hasOwn(arr, i)) { + delete arr[resultIndex++]; + continue; + } + + arr[resultIndex++] = arr[i]; } + arr.length = resultIndex; + return arr; }