Skip to content

countBy

Edit

Counts array values by a derived property key.

countBy.ts
export type CountByKeySelector<Value, Key extends PropertyKey> = (
this: void,
value: Value,
index: number
) => Key;
/**
* Counts are optional because a selector's return type describes every key it
* may produce, not a guarantee that every possible key occurs in the input.
*/
export type CountByResult<Key extends PropertyKey> = Partial<Record<Key, number>>;
/** Counts values by a derived property key. */
export const countBy = <Value, Key extends PropertyKey>(
array: readonly Value[],
getKey: CountByKeySelector<Value, Key>
): CountByResult<Key> => {
const result = Object.create(null) as CountByResult<Key>;
for (let index = 0; index < array.length; index += 1) {
if (!Object.hasOwn(array, index)) continue;
const value = array[index] as Value;
const key = getKey(value, index);
const count = Reflect.get(result, key) as number | undefined;
Reflect.set(result, key, (count ?? 0) + 1);
}
return result;
};
Terminal
wget -O src/lib/countBy.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/functions/countBy.ts
countBy.example.ts
import { countBy } from "./countBy";
const totals = countBy(
["api", "web", "api"] as const,
(value) => value
);
// { api: 2, web: 1 }
const apiCount = totals.api ?? 0;
// 2
  • array (readonly T[]) — Values to count. Empty slots are skipped.
  • getKey (CountByKeySelector<T, Key>) — Selects a bucket and is invoked without a receiver.
  • Returns Partial<Record<Key, number>> with a null prototype. A count is undefined when that key did not occur.