Object.keys, values, entries

You’ve spent a few chapters looking at individual structures on their own: Map, Set, Array, plain objects. Now the question shifts. Once you have a collection, how do you walk across everything inside it?

Back with Map and Set you met three methods: map.keys(), map.values(), and map.entries(). They hand you the keys, the values, or the [key, value] pairs so you can loop over them.

Those three names aren’t arbitrary. There’s a shared convention in JavaScript that iterable collections expose exactly this trio. If you ever build your own collection type, following the same naming lets other developers (and language features like for..of) treat it the way they’d treat any built-in.

The convention holds for:

  • Map
  • Set
  • Array

Plain objects support the same idea, but the call looks different. That difference is the whole point of this chapter.

Map / Set / Array
collection.keys()
collection.values()
collection.entries()
method on the instance
Plain object
Object.keys(obj)
Object.values(obj)
Object.entries(obj)
static method, object passed in
The same three questions, asked of two kinds of collections

Object.keys, values, entries

For a plain object you reach for these:

Two things about them differ from the Map versions, and both are easy to trip over:

Map Object
Call syntax map.keys() Object.keys(obj), but not obj.keys()
Returns iterable a real Array

The first difference is where the method lives. You call Object.keys(obj) and hand the object in as an argument. You do not call obj.keys().

Why route it through Object instead of putting keys() on every object? Flexibility. Objects are the foundation under nearly every complex structure in JavaScript, so a given object might already define its own keys property for its own purposes — say a data object with a real data.values() method that does something domain-specific. If values() were forced onto every object, it would collide. Keeping the generic operation as Object.values(data) leaves your own data.values() free to mean whatever you want.

The second difference: Object.keys/values/entries return genuine arrays, not just something iterable. map.keys() gives you an iterator you can loop over but can’t index or .push() into directly. Object.keys() gives you an honest-to-goodness Array, ready for .map, .filter, .length, [0], all of it. That’s mostly a historical accident in how the language grew, but it works in your favor here.

Here’s the shape of what comes back:

let user = {
  name: "Maya",
  age: 30
};
  • Object.keys(user)["name", "age"]
  • Object.values(user)["Maya", 30]
  • Object.entries(user)[ ["name","Maya"], ["age",30] ]
user
name: “Maya”
age: 30
Object.keys(user)
[ “name”, “age” ]
Object.values(user)
[ “Maya”, 30 ]
Object.entries(user)
[ [“name”,“Maya”],
  [“age”,30] ]
One object, three different views of the same data

Because Object.values returns an array, for..of walks straight over it:

let user = {
  name: "Maya",
  age: 30
};

// loop over the values
for (let value of Object.values(user)) {
  alert(value); // Maya, then 30
}

Type your own object below and watch all three methods respond. Add a key, change a value, and the three arrays update together — notice they always line up by position.

interactiveThree views of one object

Transforming objects

Arrays come loaded with methods: map, filter, reduce, and the rest. Plain objects have none of them. There’s no obj.map(...).

The workaround is a round trip. Turn the object into an array of pairs, use array methods on that, then turn it back into an object:

  1. Object.entries(obj) gives you an array of [key, value] pairs.
  2. Run array methods on that array — map, filter, whatever you need.
  3. Object.fromEntries(array) folds the pairs back into a plain object.

Object.fromEntries is the mirror image of Object.entries: give it a list of [key, value] pairs and it builds the object.

object
toStation: 4
toGym: 3
Object.entries
pairs
[“toStation”,4]
[“toGym”,3]
.map(double)
pairs
[“toStation”,8]
[“toGym”,6]
fromEntries
object
toStation: 8
toGym: 6
The object → array → object round trip

Say you have a set of one-way distances (in km) and want each round-trip length, which is just double the one-way value:

let legs = {
  toStation: 4,
  toOffice: 6,
  toGym: 3,
};

let roundTrips = Object.fromEntries(
  // 1. turn legs into [key, value] pairs
  // 2. map each pair into a new pair with the value doubled
  // 3. fromEntries rebuilds the object
  Object.entries(legs).map(entry => [entry[0], entry[1] * 2])
);

alert(roundTrips.toGym); // 6

The dense line in the middle unpacks like this: entry is a [key, value] pair, so entry[0] is the key and entry[1] is the value. The arrow function returns a fresh pair [key, value * 2], keeping the key and doubling the value.

Here is the whole round trip running live. Move the slider to change the multiplier and watch a brand-new object come out the other end while the original stays exactly as it was.

interactiveentries → map → fromEntries

It looks like a lot the first time. After you write it once or twice, the pattern sticks, and you can build real transformation pipelines out of it. Want only the short legs? Slot a .filter before the .map:

let short = Object.fromEntries(
  Object.entries(legs).filter(([key, value]) => value < 5)
);
// short = { toStation: 4, toGym: 3 }

Drag the threshold below to see which entries survive the .filter and get rebuilt into the result object.

interactiveFilter, then rebuild