From 0d6a88826182bb6f767eb8dee7d77ec42e2839a4 Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Fri, 7 Jul 2023 20:04:11 +0300
Subject: [PATCH] feat: Add `filterMap` utility for fast array transforms
 (#23252)

---
 lib/util/filter-map.spec.ts | 17 +++++++++++++++++
 lib/util/filter-map.ts      | 26 ++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)
 create mode 100644 lib/util/filter-map.spec.ts
 create mode 100644 lib/util/filter-map.ts

diff --git a/lib/util/filter-map.spec.ts b/lib/util/filter-map.spec.ts
new file mode 100644
index 0000000000..2c198ace4c
--- /dev/null
+++ b/lib/util/filter-map.spec.ts
@@ -0,0 +1,17 @@
+import { filterMap } from './filter-map';
+
+describe('util/filter-map', () => {
+  it('should return an empty array when given an empty array', () => {
+    const input: unknown[] = [];
+    const output = filterMap(input, () => 42);
+    expect(output).toBe(input);
+    expect(output).toBeEmpty();
+  });
+
+  it('should return an array with only the mapped values that pass the filter', () => {
+    const input = [0, 1, 2, 3, 4];
+    const output = filterMap(input, (n) => n * n);
+    expect(output).toBe(input);
+    expect(output).toEqual([1, 4, 9, 16]);
+  });
+});
diff --git a/lib/util/filter-map.ts b/lib/util/filter-map.ts
new file mode 100644
index 0000000000..705d8656d0
--- /dev/null
+++ b/lib/util/filter-map.ts
@@ -0,0 +1,26 @@
+import is from '@sindresorhus/is';
+
+type Falsy = false | '' | 0 | 0n | null | undefined;
+
+/**
+ * Filter and map an array *in place* with single iteration.
+ */
+export function filterMap<T, U>(array: T[], fn: (item: T) => Falsy | U): U[] {
+  const length = array.length;
+  let newIdx = 0;
+  for (let oldIdx = 0; oldIdx < length; oldIdx += 1) {
+    const item = array[oldIdx];
+    const res = fn(item);
+    if (is.truthy(res)) {
+      array[newIdx] = res as never;
+      newIdx += 1;
+    }
+  }
+
+  const deletedCount = length - newIdx;
+  if (deletedCount) {
+    array.length = length - deletedCount;
+  }
+
+  return array as never;
+}
-- 
GitLab