diff --git a/lib/util/filter-map.spec.ts b/lib/util/filter-map.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c198ace4c01dbb8ff04309f2307bcebe2d9135a
--- /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 0000000000000000000000000000000000000000..705d8656d0539ca9c3e9c6d875267bffe3196815
--- /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;
+}