If you’re scanning an array every time you need a specific item, that’s O(n) per lookup. Convert it to a keyed object once and every access becomes O(1). This utility takes any array and a getter function that picks the key from each item.
/**
* Converts an array to a keyed object using a custom attribute getter function.
* @param array The array to convert.
* @param attributeGetter The function to get the key from the item.
*/
function arrayToKeyedObject<T, K extends string>(
array: T[],
attributeGetter: (item: T) => K,
) {
if (!Array.isArray(array)) {
throw new TypeError('First argument must be an array')
}
if (!attributeGetter || typeof attributeGetter !== 'function') {
throw new TypeError('attributeGetter argument must be a function')
}
if (array.length === 0) {
return {} as Record<K, T>
}
const map = {} as Record<K, T>
for (const value of array) {
const key = attributeGetter(value)
map[key] = value
}
return map
}Usage
Converting a users array to an object keyed by id:
interface User {
id: number
name: string
}
const users: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
]
const usersById = arrayToKeyedObject(users, (user) => String(user.id))
console.log(usersById)
// Output: { '1': { id: 1, name: 'Alice' }, '2': { id: 2, name: 'Bob' }, '3': { id: 3, name: 'Charlie' } }
const getUserById = (id: string) => usersById[id]
console.log(getUserById('2'))
// Output: { id: 2, name: 'Bob' }Why it works
Fast lookups are the obvious win, but the bigger benefit is clarity. When you call arrayToKeyedObject(users, user => String(user.id)), the data shape is explicit — you know exactly what you’re getting and how it’s keyed. The generic K extends string keeps TypeScript honest and ensures key types stay consistent throughout.
Edge cases
Duplicate keys silently overwrite earlier entries. If that matters for your data, add a check before assigning. Also, if you need non-string keys, use Map instead — object keys are always coerced to strings anyway. Empty arrays just return {}.
Alternatives
Array.prototype.reduce works fine for one-liners or inline logic. Map is better for non-string keys or when insertion order matters. Object.fromEntries is useful when you already have [key, value] tuples ready.
Complexity
- Time: O(n)
- Space: O(n)