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)