Lodash _.mapKeys() method
What you’ll learn
- How
_.mapKeys(object, [iteratee])rewrites every key while keeping its value intact. - The iteratee signature
(value, key, object)and the four shorthand forms. - The silent last-write-wins behavior when transformed keys collide.
- How to pair
_.mapKeyswith _.mapValues() for full object reshaping.
Prerequisites
Comfortable with Lodash iteratee shorthands and the idea that object keys must be unique strings.
- Keys, not values: use
_.mapValuesif you only want to transform values. - Pure function: a new object is returned; the source stays the same.
- Own enumerable only: inherited and symbol-keyed properties are skipped.
Overview
_.mapKeys iterates the source’s own enumerable string-keyed properties, calls the iteratee with (value, key, object), and uses the returned string as the new key. Values are copied through unchanged. The result is the fast path for renaming, normalizing, and namespacing keys without rebuilding objects by hand.
Rename in one pass
Convert snake_case to camelCase, prefix keys, or apply a lookup table.
Last-write-wins
Collisions silently overwrite. Encode uniqueness into the iteratee output.
Pure transform
A new object is returned. The input is never mutated.
Syntax
_.mapKeys(object, [iteratee=_.identity]) - object: source to iterate over. Only own enumerable string-keyed properties are visited.
- iteratee (optional): function, property name string,
[path, value], or matcher object. Receives(value, key, object). - Returns: a new object with the same values and the iteratee-produced keys.
Uppercase & prefix keys
The simplest form—return a transformed key string from the iteratee. The iteratee receives (value, key, object), but you usually only need key.
import mapKeys from "lodash/mapKeys";
const counts = { a: 1, b: 2, c: 3 };
mapKeys(counts, (value, key) => key.toUpperCase());
// -> { A: 1, B: 2, C: 3 }
mapKeys(counts, (value, key) => "n_" + key);
// -> { n_a: 1, n_b: 2, n_c: 3 }
counts;
// -> { a: 1, b: 2, c: 3 }
// Source is unchanged. Normalize snake_case to camelCase
A real-world use case: converting API responses to JavaScript conventions in one pass.
import mapKeys from "lodash/mapKeys";
const apiUser = {
first_name: "John",
last_name: "Doe",
email_address: "john@example.com",
is_verified: true
};
const camelize = (str) =>
str.replace(/_([a-z])/g, (_, ch) => ch.toUpperCase());
mapKeys(apiUser, (value, key) => camelize(key));
// -> {
// firstName: "John",
// lastName: "Doe",
// emailAddress: "john@example.com",
// isVerified: true
// }
// Renaming a specific key with a lookup table:
const aliases = { is_verified: "verified" };
mapKeys(apiUser, (value, key) => aliases[key] || key);
// -> { first_name: "John", last_name: "Doe", email_address: "...", verified: true } Collisions silently overwrite
This is the single biggest _.mapKeys pitfall. If two source keys transform to the same string, the later iteration overwrites the earlier value—no warning, no error.
import mapKeys from "lodash/mapKeys";
const fruits = { apple: 1, avocado: 2, banana: 3 };
// First letter only -> "a" collides for apple and avocado.
mapKeys(fruits, (value, key) => key[0]);
// -> { a: 2, b: 3 }
// apple's value is silently lost.
// Safer alternative: include the original key in the new key.
mapKeys(fruits, (value, key) => key[0] + "_" + key);
// -> { a_apple: 1, a_avocado: 2, b_banana: 3 }
// If you actually want to keep every colliding source key,
// fold manually so you can decide the merge strategy:
const grouped = Object.entries(fruits).reduce((acc, [key, value]) => {
const bucket = key[0];
(acc[bucket] ||= []).push({ key, value });
return acc;
}, {});
// -> {
// a: [{ key: "apple", value: 1 }, { key: "avocado", value: 2 }],
// b: [{ key: "banana", value: 3 }]
// } 📋 _.mapKeys vs related helpers
| Topic | _.mapKeys | _.mapValues | Native Object.fromEntries |
|---|---|---|---|
| Transforms | Keys (values copied) | Values (keys copied) | Both at once |
| Iteratee args | (value, key, object) | (value, key, object) | ([key, value]) |
| Mutates input | No | No | No |
| Shorthand support | Yes | Yes | No (write the map function) |
| Collisions | Last wins | N/A (keys preserved) | Last wins |
Use _.mapKeys when only keys change. Use _.mapValues when only values change. Reach for Object.fromEntries(Object.entries(obj).map(...)) when you want to rewrite keys and values in the same pass.
Pitfalls to avoid
Silent overwrites on duplicate keys
The old reference’s key.slice(0, 1) example only worked because the source keys were already single characters. With real data that would lose values.
Don’t rely on the default iteratee
Without an iteratee, each value becomes the new key. Only meaningful if your values are already unique strings.
Iteratee return is stringified
Numbers, booleans, and objects are all converted to strings when used as keys. Returning an object collapses to "[object Object]".
Inherited and symbol keys are skipped
Only own enumerable string-keyed properties are processed. Bring symbols or prototype properties forward manually if you need them.
❓ FAQ
Summary
- Purpose: build a new object whose keys come from an iteratee while keeping values intact.
- Remember: collisions silently overwrite—encode uniqueness into the iteratee output.
- Next: Lodash _.mapValues(), _.invert(), or the official Lodash docs for _.mapKeys.
_.mapKeys keeps the values intact and only rewrites the keys. If two transformed keys collide, the later iteration wins—silently. Sanitize your iteratee or use a unique-prefix strategy when collisions are possible.
5 people found this page helpful
