How to Convert an Array to a Dictionary in TypeScript

The quickest way to convert an array to a dictionary in TypeScript is Object.fromEntries(): map each item to a [key, value] pair and type the result as a Record. Here’s the one-liner I use for an array of objects keyed by id:

const byId: Record<string, Employee> = Object.fromEntries(
  employees.map((e) => [e.id, e]),
);

Every snippet below ran in strict mode on TypeScript 7.0.2 and Node.js v22.22.2, and each output block is pasted straight from my console.

Convert an array of objects to a dictionary with Object.fromEntries()

Say you look up Austin and Denver employees by id all day. Calling find() scans the whole array each time; a dictionary gives you a direct lookup. If typed object arrays are new to you, start with arrays of objects in TypeScript.

interface Employee {
  id: string;
  name: string;
  city: string;
  salaryUSD: number;
}

const employees: Employee[] = [
  { id: "E101", name: "Ava Thompson", city: "Austin", salaryUSD: 98500 },
  { id: "E102", name: "Liam Carter", city: "Denver", salaryUSD: 87250.5 },
  { id: "E103", name: "Mia Rodriguez", city: "Austin", salaryUSD: 104000 },
];

const byId: Record<string, Employee> = Object.fromEntries(
  employees.map((e) => [e.id, e]),
);

console.log(byId["E102"].name);              // direct lookup, no find()
console.log(Object.keys(byId));

Output:

Liam Carter
[ 'E101', 'E102', 'E103' ]
Command Prompt output of TypeScript Object.fromEntries converting an employee array into a dictionary keyed by id
The dictionary answers byId["E102"] instantly.

The value can be any field you put in the pair. It’s the same idea as mapping an array of objects in TypeScript.

If you only need one entry, I show the typed options to get the first element from a TypeScript dictionary.

Build the dictionary with reduce()

Many codebases use reduce() instead, but in strict mode an untyped {} seed won’t compile:

// Employee interface and employees array from the first example

const byId = employees.reduce((acc, e) => {
  acc[e.id] = e;
  return acc;
}, {});

Output:

dict_reduce_err.ts(15,3): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
  No index signature with a parameter of type 'string' was found on type '{}'.
Command Prompt output of tsc error TS7053 element implicitly has an any type when converting an array to a dictionary with reduce
TS7053: TypeScript can’t index the empty type {} with a string.

The fix is to pass the result type as reduce’s generic argument. I mutate the accumulator, because spreading {...acc} copies it every time:

// Employee interface and employees array from the first example

const byId = employees.reduce<Record<string, Employee>>((acc, e) => {
  acc[e.id] = e;                              // mutate the accumulator, no spread copy
  return acc;
}, {});

console.log(byId["E103"].city, byId["E103"].salaryUSD.toFixed(2));

Output:

Austin 104000.00

Everything else reduce() can do is in my guide to the array reduce method in TypeScript.

Convert an array to a dictionary with a for…of loop or forEach

A plain loop is easy to debug, and it handles string arrays too, like counting orders per state:

// Employee interface and employees array from the first example

const nameById: Record<string, string> = {};
for (const e of employees) {
  nameById[e.id] = e.name;
}
console.log(nameById);

const states: string[] = ["TX", "CA", "TX", "NY", "TX", "CA"];
const ordersByState: Record<string, number> = {};
states.forEach((s) => {
  ordersByState[s] = (ordersByState[s] ?? 0) + 1;
});
console.log(ordersByState);

Output:

{ E101: 'Ava Thompson', E102: 'Liam Carter', E103: 'Mia Rodriguez' }
{ TX: 3, CA: 2, NY: 1 }

The ?? 0 starts each new key at zero. For a comparison of loop styles, see how to loop through an array in TypeScript and the forEach() method.

Write a reusable toDictionary() helper

If you convert arrays often, a generic helper keeps the types right. Pass a key picker and, optionally, a value picker:

// Employee interface and employees array from the first example

function toDictionary<T, K extends PropertyKey, V = T>(
  items: readonly T[],
  getKey: (item: T) => K,
  getValue: (item: T) => V = (item) => item as unknown as V,
): Record<K, V> {
  const result = {} as Record<K, V>;
  for (const item of items) {
    result[getKey(item)] = getValue(item);
  }
  return result;
}

const byId = toDictionary(employees, (e) => e.id);
const payById = toDictionary(employees, (e) => e.id, (e) => e.salaryUSD.toLocaleString("en-US", { style: "currency", currency: "USD" }));
console.log(byId["E101"].name, "|", payById);

const lookup: Record<string, Employee | undefined> = byId;
const missing = lookup["E999"];
console.log("E999:", missing?.name ?? "not found", "| hasOwn:", Object.hasOwn(byId, "E999"));

Output:

Ava Thompson | { E101: '$98,500.00', E102: '$87,250.50', E103: '$104,000.00' }
E999: not found | hasOwn: false
Command Prompt output of a generic TypeScript toDictionary helper with key and value selectors and a missing key lookup
The helper builds both dictionaries, and the missing E999 key comes back as not found.

Watch that last line. A Record<string, Employee> claims every key exists, so byId["E999"].name compiles yet crashes at runtime. Widen lookups to Employee | undefined, check Object.hasOwn(), or enable noUncheckedIndexedAccess.

What happens to duplicate keys in a dictionary?

Keying by city drops one of the two Austin employees. Every method in this article keeps the last value. When you need all of them, use Object.groupBy() (ES2024):

// Employee interface and employees array from the first example

const byCity = Object.fromEntries(employees.map((e) => [e.city, e.name]));
console.log("last one wins:", byCity);

const groups = Object.groupBy(employees, (e) => e.city);
for (const [city, people] of Object.entries(groups)) {
  console.log(city, "->", people?.map((p) => p.name).join(", "));
}

Output:

last one wins: { Austin: 'Mia Rodriguez', Denver: 'Liam Carter' }
Austin -> Ava Thompson, Mia Rodriguez
Denver -> Liam Carter
Command Prompt output showing duplicate keys overwritten in a TypeScript dictionary and Object.groupBy grouping employees by city
Ava Thompson is overwritten by Mia Rodriguez, while Object.groupBy() keeps both under Austin.

Should you use a Map instead of an object?

A Map is TypeScript’s other dictionary. It keeps key types and has size and has() built in:

// Employee interface and employees array from the first example

const byId = new Map(employees.map((e) => [e.id, e]));
console.log(byId.get("E101")?.name, "| size:", byId.size, "| has E999:", byId.has("E999"));

const zipCodes = [78701, 80202, 60601];
const asObject = Object.fromEntries(zipCodes.map((z) => [z, true]));
const asMap = new Map(zipCodes.map((z) => [z, true]));
console.log("object keys:", Object.keys(asObject));
console.log("map keys:   ", [...asMap.keys()]);

Output:

Ava Thompson | size: 3 | has E999: false
object keys: [ '60601', '78701', '80202' ]
map keys:    [ 78701, 80202, 60601 ]

Notice the object turned the ZIP codes into strings and sorted them, while the Map kept numbers in insertion order. I go deeper in creating a Map from an array in TypeScript.

ApproachResult typeBest for
Object.fromEntries()Record<string, T>Short, readable one-liners
reduce<Record<...>>()Record<string, T>Extra logic per item
Object.groupBy()Partial<Record<K, T[]>>Keys that repeat
new Map()Map<K, T>Non-string keys, frequent updates

Here are a few related TypeScript guides to read next:

Frequently asked questions

How do I convert an array to a dictionary in TypeScript?

Use Object.fromEntries(arr.map((x) => [x.id, x])) and annotate the result as Record<string, T>.

Why does reduce() give error TS7053 when building a dictionary?

The {} seed has type {}, which has no string index. Write reduce<Record<string, T>>(..., {}).

What happens if two items have the same key?

The later item overwrites the earlier one. Use Object.groupBy() to keep every item per key.

Should I use Record or Map for a dictionary?

Use a Record for JSON-friendly string keys and a Map for number or object keys.

Where is Object.fromEntries() documented?

See Object.fromEntries() on MDN.