Episode 1 — Fundamentals / 1.24 — Object Methods

1.24.a — Object.entries()

In one sentence: Object.entries(obj) returns an array of [key, value] pairs from an object's own enumerable string-keyed properties, enabling powerful iteration and transformation patterns.

Navigation: <- 1.24 Overview . 1.24.b — Object.assign ->


1. What does Object.entries() do?

Object.entries() takes an object and returns a two-dimensional array where each inner array is a [key, value] pair.

const user = { name: "Alice", age: 30, role: "admin" };
const entries = Object.entries(user);

console.log(entries);
// [
//   ["name", "Alice"],
//   ["age", 30],
//   ["role", "admin"]
// ]

Each entry is a new array — mutating it does not affect the original object.


2. Syntax and return type

Object.entries(obj)
ParameterDescription
objThe object whose own enumerable string-keyed [key, value] pairs are returned
ReturnsArray<[string, any]> — an array of two-element arrays

If obj is not an object, it is coerced to one:

Object.entries("hi");    // [["0", "h"], ["1", "i"]]
Object.entries(42);      // []  (numbers have no enumerable props)
Object.entries(true);    // []

3. Only own, enumerable, string-keyed properties

Object.entries() skips three categories of properties:

SkippedExample
Inherited propertiesProperties from the prototype chain
Non-enumerable propertiesProperties defined with enumerable: false
Symbol-keyed propertiesProperties with Symbol keys
const parent = { inherited: true };
const child = Object.create(parent);
child.own = "yes";

Object.defineProperty(child, "hidden", {
  value: "secret",
  enumerable: false,
});

child[Symbol("id")] = 99;

console.log(Object.entries(child));
// [["own", "yes"]]
// inherited, hidden, and Symbol properties are excluded

4. Iterating with for...of and destructuring

The most common pattern — destructure each entry into key and value:

const scores = { math: 95, science: 88, english: 92 };

for (const [subject, score] of Object.entries(scores)) {
  console.log(`${subject}: ${score}`);
}
// math: 95
// science: 88
// english: 92

This is cleaner than for...in because:

  1. No need for hasOwnProperty check (entries are already own properties)
  2. You get both key and value directly
  3. It works with const destructuring

5. Converting an object to a Map

Map constructor accepts an iterable of [key, value] pairs — exactly what Object.entries() produces:

const config = { theme: "dark", lang: "en", debug: false };
const configMap = new Map(Object.entries(config));

console.log(configMap.get("theme"));  // "dark"
console.log(configMap.size);          // 3

Why convert? Maps preserve insertion order for all key types, have .size, and perform better for frequent additions/deletions.


6. Transformation pipelines: entries -> map/filter -> fromEntries

The real power of Object.entries() emerges when combined with array methods and Object.fromEntries():

Filter object properties by value

const inventory = { apples: 5, bananas: 0, cherries: 12, dates: 0 };

const inStock = Object.fromEntries(
  Object.entries(inventory).filter(([_, qty]) => qty > 0)
);

console.log(inStock);
// { apples: 5, cherries: 12 }

Transform all values

const prices = { shirt: 20, pants: 40, hat: 15 };

const withTax = Object.fromEntries(
  Object.entries(prices).map(([item, price]) => [item, price * 1.1])
);

console.log(withTax);
// { shirt: 22, pants: 44, hat: 16.5 }

Rename keys

const apiResponse = { first_name: "Alice", last_name: "Smith" };

const camelCased = Object.fromEntries(
  Object.entries(apiResponse).map(([key, val]) => [
    key.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
    val,
  ])
);

console.log(camelCased);
// { firstName: "Alice", lastName: "Smith" }

7. Comparison with Object.keys() and Object.values()

MethodReturnsUse when you need
Object.keys(obj)["key1", "key2", ...]Only property names
Object.values(obj)[val1, val2, ...]Only property values
Object.entries(obj)[["key1", val1], ...]Both keys and values together

All three share the same rules: own, enumerable, string-keyed, same order.

const user = { name: "Alice", age: 30 };

Object.keys(user);    // ["name", "age"]
Object.values(user);  // ["Alice", 30]
Object.entries(user);  // [["name", "Alice"], ["age", 30]]

8. Real-world examples

Logging all properties

function logObject(label, obj) {
  console.group(label);
  for (const [key, value] of Object.entries(obj)) {
    console.log(`  ${key}: ${JSON.stringify(value)}`);
  }
  console.groupEnd();
}

logObject("User", { name: "Bob", age: 25, active: true });
// User
//   name: "Bob"
//   age: 25
//   active: true

Converting object to query string

function toQueryString(params) {
  return Object.entries(params)
    .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
    .join("&");
}

console.log(toQueryString({ search: "hello world", page: 2, lang: "en" }));
// "search=hello%20world&page=2&lang=en"

Filtering object by value type

const mixed = { a: 1, b: "two", c: 3, d: "four", e: 5 };

const numbersOnly = Object.fromEntries(
  Object.entries(mixed).filter(([_, v]) => typeof v === "number")
);

console.log(numbersOnly);
// { a: 1, c: 3, e: 5 }

Building an HTML table from an object

function objectToTable(obj) {
  const rows = Object.entries(obj)
    .map(([key, val]) => `<tr><td>${key}</td><td>${val}</td></tr>`)
    .join("\n");
  return `<table>\n<tr><th>Key</th><th>Value</th></tr>\n${rows}\n</table>`;
}

Comparing two objects for differences

function getDifferences(objA, objB) {
  return Object.fromEntries(
    Object.entries(objA).filter(([key, val]) => objB[key] !== val)
  );
}

const before = { name: "Alice", age: 30, role: "user" };
const after  = { name: "Alice", age: 31, role: "admin" };

console.log(getDifferences(before, after));
// { age: 30, role: "user" }  -- values from objA that differ

9. Property order guarantee

Object.entries() returns properties in the same order as for...in (minus inherited):

  1. Integer-like keys in ascending numeric order ("0", "1", "2", ...)
  2. String keys in insertion order
  3. (Symbol keys are excluded entirely)
const obj = { b: 2, 2: "two", a: 1, 1: "one" };

console.log(Object.entries(obj));
// [["1", "one"], ["2", "two"], ["b", 2], ["a", 1]]
// Integer keys first (sorted), then string keys (insertion order)

10. Edge cases

// Empty object
Object.entries({});            // []

// null / undefined — throws TypeError
// Object.entries(null);       // TypeError!
// Object.entries(undefined);  // TypeError!

// Array (arrays are objects)
Object.entries(["a", "b"]);    // [["0", "a"], ["1", "b"]]

// Object with numeric keys
Object.entries({ 10: "ten", 2: "two", 1: "one" });
// [["1", "one"], ["2", "two"], ["10", "ten"]]

Key takeaways

  1. Object.entries(obj) returns an array of [key, value] pairs for own enumerable string-keyed properties.
  2. Destructure in for...of loops: for (const [key, value] of Object.entries(obj)).
  3. Pipe through map/filter then Object.fromEntries() to transform objects functionally.
  4. Convert to Map with new Map(Object.entries(obj)).
  5. Inherited, non-enumerable, and Symbol-keyed properties are excluded.

Explain-It Challenge

Explain without notes:

  1. What does Object.entries({ a: 1, b: 2 }) return? What is the type of each inner element?
  2. How would you filter an object to keep only properties with truthy values using entries and fromEntries?
  3. Why is for (const [k, v] of Object.entries(obj)) preferred over for...in in many cases?

Navigation: <- 1.24 Overview . 1.24.b — Object.assign ->