How to Use Object.keys, Object.values, and Object.entries in JavaScript
Plain objects in JavaScript are not iterable. You cannot use for...of directly on them, and they do not have built-in keys(), values(), or entries() methods like Map and Set do. Instead, JavaScript provides static methods on the Object constructor that extract an object's data into arrays, which you can then process with all the powerful array methods you already know.
Object.keys(), Object.values(), and Object.entries() are three essential methods that bridge the gap between objects and arrays. Together with Object.fromEntries(), they form a complete pipeline: extract data from an object, transform it using array methods like map, filter, and reduce, and convert the result back into an object. This pattern is one of the most practical and frequently used techniques in everyday JavaScript programming.
This guide covers each method in detail, shows how to combine them with array methods for powerful data transformations, explains Object.fromEntries() for converting back to objects, and clarifies why these methods return arrays rather than iterators.
Object.keys(obj): Array of Keys
Object.keys() returns an array of an object's own enumerable string-keyed property names.
Basic Usage
let user = {
name: "Alice",
age: 30,
role: "developer"
};
let keys = Object.keys(user);
console.log(keys); // ["name", "age", "role"]
console.log(keys.length); // 3
Key Characteristics
Returns only own properties (not inherited ones):
let parent = { inherited: true };
let child = Object.create(parent);
child.own = "yes";
console.log(Object.keys(child)); // ["own"] (inherited property not included)
console.log("inherited" in child); // true (it exists via prototype)
Returns only enumerable properties:
let obj = {};
Object.defineProperty(obj, "hidden", {
value: "secret",
enumerable: false
});
obj.visible = "public";
console.log(Object.keys(obj)); // ["visible"] ()"hidden" is not enumerable)
Returns only string keys (symbols are excluded):
let sym = Symbol("id");
let obj = {
name: "Alice",
[sym]: 42
};
console.log(Object.keys(obj)); // ["name"] (symbol key not included)
Property order follows a specific pattern:
let mixed = {
b: 2,
2: "two",
a: 1,
1: "one",
c: 3,
0: "zero"
};
console.log(Object.keys(mixed));
// ["0", "1", "2", "b", "a", "c"]
// Integer-like keys first (sorted numerically), then string keys (insertion order)
Integer-like keys (like "0", "1", "2") are sorted numerically and placed first. Other string keys maintain their insertion order.
Practical Uses
// Counting properties
let config = { host: "localhost", port: 3000, debug: true };
console.log(Object.keys(config).length); // 3
// Checking if an object is empty
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
console.log(isEmpty({})); // true
console.log(isEmpty({ name: "Alice" })); // false
// Iterating over property names
let user = { name: "Alice", age: 30, city: "Rome" };
Object.keys(user).forEach(key => {
console.log(`${key}: ${user[key]}`);
});
// name: Alice
// age: 30
// city: Rome
Object.values(obj): Array of Values
Object.values() returns an array of an object's own enumerable string-keyed property values.
Basic Usage
let user = {
name: "Alice",
age: 30,
role: "developer"
};
let values = Object.values(user);
console.log(values); // ["Alice", 30, "developer"]
The Same Rules Apply
Like Object.keys(), this method returns only own, enumerable, string-keyed property values:
let sym = Symbol("id");
let obj = {
visible: "yes",
[sym]: "symbol value"
};
Object.defineProperty(obj, "hidden", {
value: "secret",
enumerable: false
});
console.log(Object.values(obj)); // ["yes"] (hidden and symbol excluded)
Practical Uses
// Sum all numeric values
let scores = { math: 95, science: 88, history: 72, english: 91 };
let total = Object.values(scores).reduce((sum, score) => sum + score, 0);
console.log(total); // 346
let average = total / Object.values(scores).length;
console.log(average); // 86.5
// Find the maximum value
let maxScore = Math.max(...Object.values(scores));
console.log(maxScore); // 95
// Check if any value meets a condition
let hasHighScore = Object.values(scores).some(score => score >= 90);
console.log(hasHighScore); // true
// Collect unique values
let roles = {
alice: "admin",
bob: "editor",
charlie: "admin",
diana: "viewer",
eve: "editor"
};
let uniqueRoles = [...new Set(Object.values(roles))];
console.log(uniqueRoles); // ["admin", "editor", "viewer"]
Object.entries(obj): Array of [key, value] Pairs
Object.entries() returns an array of an object's own enumerable string-keyed property [key, value] pairs. This is the most versatile of the three methods because it gives you access to both keys and values simultaneously.
Basic Usage
let user = {
name: "Alice",
age: 30,
role: "developer"
};
let entries = Object.entries(user);
console.log(entries);
// [ [ 'name', 'Alice' ], [ 'age', 30 ], [ 'role', 'developer' ] ]
Iterating with Destructuring
The most common pattern is combining Object.entries() with for...of and destructuring:
let prices = {
coffee: 4.50,
tea: 3.00,
juice: 5.25,
water: 1.50
};
for (let [item, price] of Object.entries(prices)) {
console.log(`${item}: $${price.toFixed(2)}`);
}
// coffee: $4.50
// tea: $3.00
// juice: $5.25
// water: $1.50
This is the cleanest way to iterate over both keys and values of a plain object. It provides the same readability as for...of with Map, but for plain objects.
Converting Object to Map
Since Object.entries() returns the exact format that the Map constructor expects ([key, value] pairs), conversion is direct:
let config = {
host: "localhost",
port: 3000,
debug: true
};
let configMap = new Map(Object.entries(config));
console.log(configMap.get("host")); // "localhost"
console.log(configMap.size); // 3
Practical Uses
// Find the key with the highest value
let scores = { Alice: 95, Bob: 88, Charlie: 72, Diana: 91 };
let [topStudent, topScore] = Object.entries(scores)
.reduce((best, current) => current[1] > best[1] ? current : best);
console.log(`${topStudent}: ${topScore}`); // "Alice: 95"
// Filter properties by value
let inventory = { apples: 5, bananas: 0, cherries: 12, dates: 0, elderberries: 3 };
let inStock = Object.entries(inventory)
.filter(([item, count]) => count > 0);
console.log(inStock); // [["apples", 5], ["cherries", 12], ["elderberries", 3]]
// Create a formatted string
let user = { name: "Alice", age: 30, city: "Rome" };
let description = Object.entries(user)
.map(([key, value]) => `${key}=${value}`)
.join("&");
console.log(description); // "name=Alice&age=30&city=Rome"
Object.fromEntries(): Transforming Back to an Object
Object.fromEntries() is the inverse of Object.entries(). It takes an iterable of [key, value] pairs and creates a new plain object.
Basic Usage
let entries = [
["name", "Alice"],
["age", 30],
["role", "developer"]
];
let user = Object.fromEntries(entries);
console.log(user); // { name: "Alice", age: 30, role: "developer" }
From Map to Object
Since Maps iterate as [key, value] pairs, conversion is seamless:
let priceMap = new Map([
["coffee", 4.50],
["tea", 3.00],
["juice", 5.25]
]);
let priceObj = Object.fromEntries(priceMap);
console.log(priceObj); // { coffee: 4.5, tea: 3, juice: 5.25 }
From URL Search Parameters
URLSearchParams is iterable with [key, value] pairs, making it directly compatible:
let params = new URLSearchParams("name=Alice&age=30&city=Rome");
let obj = Object.fromEntries(params);
console.log(obj); // { name: "Alice", age: "30", city: "Rome" }
Handling Duplicate Keys
When multiple entries have the same key, the last one wins:
let entries = [
["a", 1],
["b", 2],
["a", 3] // Duplicate key "a"
];
let obj = Object.fromEntries(entries);
console.log(obj); // { a: 3, b: 2 } (last value for "a" wins)
Round-Trip: Object → Entries → Object
The fundamental power of Object.fromEntries() is completing the transformation pipeline:
let original = { a: 1, b: 2, c: 3 };
// Object → entries → back to object (identity transformation)
let copy = Object.fromEntries(Object.entries(original));
console.log(copy); // { a: 1, b: 2, c: 3 }
console.log(copy === original); // false (new object)
Transforming Objects with Entries + Array Methods
The most powerful pattern is combining Object.entries(), array transformation methods, and Object.fromEntries() to create a complete pipeline for transforming objects.
The Pattern
let result = Object.fromEntries(
Object.entries(source)
.filter(/* ... */)
.map(/* ... */)
);
This pipeline lets you apply any array operation to object properties and get an object back.
Filtering Properties by Value
let scores = {
Alice: 95,
Bob: 42,
Charlie: 78,
Diana: 91,
Eve: 55
};
// Keep only passing scores (>= 60)
let passing = Object.fromEntries(
Object.entries(scores).filter(([name, score]) => score >= 60)
);
console.log(passing); // { Alice: 95, Charlie: 78, Diana: 91 }
Filtering Properties by Key
let config = {
DB_HOST: "localhost",
DB_PORT: 5432,
DB_NAME: "myapp",
API_KEY: "secret123",
API_URL: "https://api.example.com",
LOG_LEVEL: "debug"
};
// Extract only database config
let dbConfig = Object.fromEntries(
Object.entries(config).filter(([key]) => key.startsWith("DB_"))
);
console.log(dbConfig);
// { DB_HOST: "localhost", DB_PORT: 5432, DB_NAME: "myapp" }
// Exclude sensitive keys
let safeConfig = Object.fromEntries(
Object.entries(config).filter(([key]) => !key.includes("KEY"))
);
console.log(safeConfig);
// { DB_HOST: "localhost", DB_PORT: 5432, DB_NAME: "myapp", API_URL: "https://api.example.com", LOG_LEVEL: "debug" }
Transforming Values
let prices = {
coffee: 4.50,
tea: 3.00,
juice: 5.25,
water: 1.50
};
// Apply 20% discount to all prices
let discounted = Object.fromEntries(
Object.entries(prices).map(([item, price]) => [item, +(price * 0.8).toFixed(2)])
);
console.log(discounted);
// { coffee: 3.6, tea: 2.4, juice: 4.2, water: 1.2 }
// Convert all values to strings
let stringPrices = Object.fromEntries(
Object.entries(prices).map(([item, price]) => [item, `$${price.toFixed(2)}`])
);
console.log(stringPrices);
// { coffee: "$4.50", tea: "$3.00", juice: "$5.25", water: "$1.50" }
Transforming Keys
let user = {
first_name: "Alice",
last_name: "Smith",
email_address: "alice@example.com"
};
// Convert snake_case keys to camelCase
function toCamelCase(str) {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
let camelCased = Object.fromEntries(
Object.entries(user).map(([key, value]) => [toCamelCase(key), value])
);
console.log(camelCased);
// { firstName: "Alice", lastName: "Smith", emailAddress: "alice@example.com" }
Combining Filter and Map
let products = {
laptop: { price: 999, stock: 5 },
phone: { price: 699, stock: 0 },
tablet: { price: 499, stock: 12 },
watch: { price: 299, stock: 0 },
headphones: { price: 149, stock: 25 }
};
// Get only in-stock products with discounted prices
let sale = Object.fromEntries(
Object.entries(products)
.filter(([name, data]) => data.stock > 0)
.map(([name, data]) => [
name,
{ ...data, price: +(data.price * 0.9).toFixed(2), onSale: true }
])
);
console.log(sale);
// {
// laptop: { price: 899.1, stock: 5, onSale: true },
// tablet: { price: 449.1, stock: 12, onSale: true },
// headphones: { price: 134.1, stock: 25, onSale: true }
// }
Reducing Object to a Summary
let expenses = {
rent: 1200,
food: 450,
transport: 120,
utilities: 200,
entertainment: 150
};
// Total expenses
let total = Object.values(expenses).reduce((sum, cost) => sum + cost, 0);
console.log(`Total: $${total}`); // "Total: $2120"
// Percentage breakdown
let breakdown = Object.fromEntries(
Object.entries(expenses).map(([category, cost]) => [
category,
`${((cost / total) * 100).toFixed(1)}%`
])
);
console.log(breakdown);
// { rent: "56.6%", food: "21.2%", transport: "5.7%", utilities: "9.4%", entertainment: "7.1%" }
Sorting Object Properties
Objects do not have a guaranteed iteration order for non-integer keys in the same way arrays do, but you can use entries to create a new object with properties in a specific order:
let scores = { Charlie: 78, Alice: 95, Eve: 55, Bob: 88, Diana: 91 };
// Sort by value (descending)
let ranked = Object.fromEntries(
Object.entries(scores).sort(([, a], [, b]) => b - a)
);
console.log(ranked);
// { Alice: 95, Diana: 91, Bob: 88, Charlie: 78, Eve: 55 }
// Sort by key (alphabetical)
let alphabetical = Object.fromEntries(
Object.entries(scores).sort(([a], [b]) => a.localeCompare(b))
);
console.log(alphabetical);
// { Alice: 95, Bob: 88, Charlie: 78, Diana: 91, Eve: 55 }
Picking and Omitting Properties
// Pick specific properties
function pick(obj, ...keys) {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => keys.includes(key))
);
}
let user = { name: "Alice", age: 30, email: "alice@example.com", password: "secret" };
let publicInfo = pick(user, "name", "age");
console.log(publicInfo); // { name: "Alice", age: 30 }
// Omit specific properties
function omit(obj, ...keys) {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key))
);
}
let safeUser = omit(user, "password", "email");
console.log(safeUser); // { name: "Alice", age: 30 }
Grouping with reduce and entries
let people = [
{ name: "Alice", department: "Engineering" },
{ name: "Bob", department: "Marketing" },
{ name: "Charlie", department: "Engineering" },
{ name: "Diana", department: "Marketing" },
{ name: "Eve", department: "Design" }
];
// Group by department and get names
let departments = Object.fromEntries(
Object.entries(
people.reduce((groups, person) => {
let dept = person.department;
if (!groups[dept]) groups[dept] = [];
groups[dept].push(person.name);
return groups;
}, {})
).sort(([a], [b]) => a.localeCompare(b)) // Alphabetical departments
);
console.log(departments);
// { Design: ["Eve"], Engineering: ["Alice", "Charlie"], Marketing: ["Bob", "Diana"] }
Inverting an Object (Swapping Keys and Values)
let colors = {
red: "#FF0000",
green: "#00FF00",
blue: "#0000FF"
};
let hexToName = Object.fromEntries(
Object.entries(colors).map(([name, hex]) => [hex, name])
);
console.log(hexToName);
// { "#FF0000": "red", "#00FF00": "green", "#0000FF": "blue" }
console.log(hexToName["#FF0000"]); // "red"
Why These Return Arrays (Not Iterators Like Maps)
You might wonder why Object.keys(), Object.values(), and Object.entries() return arrays rather than iterators (like Map.keys(), Map.values(), and Map.entries() do).
The Difference
let map = new Map([["a", 1], ["b", 2]]);
let obj = { a: 1, b: 2 };
// Map methods return iterators
let mapKeys = map.keys();
console.log(mapKeys); // MapIterator {"a", "b"}
console.log(Array.isArray(mapKeys)); // false
// Object methods return arrays
let objKeys = Object.keys(obj);
console.log(objKeys); // ["a", "b"]
console.log(Array.isArray(objKeys)); // true
Reasons for the Design
1. Historical timing.
Object.keys() was introduced in ES5 (2009), years before iterators and the iteration protocol existed (ES6, 2015). By the time iterators were standardized, changing Object.keys() to return an iterator would have broken existing code.
2. Immediate array method access.
Because the result is an array, you can immediately chain array methods without conversion:
let obj = { a: 1, b: 2, c: 3 };
// Direct chaining: no conversion needed
let filtered = Object.keys(obj).filter(key => key !== "b");
console.log(filtered); // ["a", "c"]
// If it returned an iterator, you'd need Array.from() first:
// let filtered = Array.from(Object.keys(obj)).filter(key => key !== "b");
3. Objects are "snapshots" by nature.
Map and Set iterators are "live" in the sense that the iterator walks through the collection. But object property enumeration has always been based on taking a snapshot of the current state. Returning an array is consistent with this snapshot behavior.
4. Consistency with the static method pattern.
Object.keys(), Object.values(), and Object.entries() are static methods on Object, not instance methods on individual objects. This is different from Map and Set, which have keys(), values(), and entries() as instance methods. The static method pattern naturally fits with returning a concrete value (array) rather than an iterator.
Practical Implications
The array return type means:
let obj = { a: 1, b: 2, c: 3 };
// ✅ All array methods work immediately
Object.keys(obj).forEach(key => console.log(key));
Object.values(obj).reduce((sum, val) => sum + val, 0);
Object.entries(obj).map(([key, val]) => `${key}: ${val}`);
Object.keys(obj).includes("a");
Object.values(obj).some(val => val > 2);
// ✅ for...of also works (arrays are iterable)
for (let key of Object.keys(obj)) {
console.log(key);
}
// a
// b
// c
// a
// b
// c
// ✅ Destructuring works
let [firstKey] = Object.keys(obj);
console.log(firstKey); // "a"
// ✅ Spread works
let allKeys = [...Object.keys(obj), ...Object.keys({ d: 4 })];
console.log(allKeys); // ["a", "b", "c", "d"]
If you need lazy iteration over a very large object (avoiding creating the full array in memory), you can use for...in with hasOwnProperty:
let largeObj = { /* thousands of properties */ };
// Lazy: doesn't create an intermediate array
for (let key in largeObj) {
if (Object.hasOwn(largeObj, key)) {
// Process one key at a time
console.log(key, largeObj[key]);
}
}
In practice, the array-returning behavior of Object.keys/values/entries is more convenient than inconvenient, and the memory cost of creating the array is negligible for all but the most extreme cases.
Comparison with Related Methods
| Method | Returns | Includes Inherited | Includes Non-Enumerable | Includes Symbols |
|---|---|---|---|---|
Object.keys(obj) | string[] | No | No | No |
Object.values(obj) | any[] | No | No | No |
Object.entries(obj) | [string, any][] | No | No | No |
for...in | Keys (one at a time) | Yes | No | No |
Object.getOwnPropertyNames(obj) | string[] | No | Yes | No |
Object.getOwnPropertySymbols(obj) | symbol[] | No | Yes | Yes (only) |
Reflect.ownKeys(obj) | (string|symbol)[] | No | Yes | Yes |
let sym = Symbol("id");
let parent = { inherited: true };
let obj = Object.create(parent);
obj.visible = "yes";
obj[sym] = "symbol";
Object.defineProperty(obj, "hidden", { value: "secret", enumerable: false });
console.log(Object.keys(obj)); // ["visible"]
console.log(Object.getOwnPropertyNames(obj)); // ["visible", "hidden"]
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(id)]
console.log(Reflect.ownKeys(obj)); // ["visible", "hidden", Symbol(id)]
Summary
Object.keys(obj)returns an array of own, enumerable, string-keyed property names. Use it when you need to know what properties an object has, count properties, or check if an object is empty.Object.values(obj)returns an array of own, enumerable, string-keyed property values. Use it for calculations on values (sum, average, max), checking conditions, or collecting unique values.Object.entries(obj)returns an array of own, enumerable, string-keyed[key, value]pairs. Use it when you need both keys and values, especially for iterating with destructuring or transforming objects.Object.fromEntries(iterable)creates a plain object from an iterable of[key, value]pairs. It is the inverse ofObject.entries()and completes the transformation pipeline.- The transformation pipeline (
Object.entries→ array methods →Object.fromEntries) is the standard pattern for filtering properties, transforming keys or values, sorting entries, and creating new objects based on existing ones. - These methods return arrays (not iterators) for historical reasons and practical convenience. This allows immediate chaining with
map,filter,reduce,sort, and all other array methods. - All three methods exclude inherited properties, non-enumerable properties, and symbol-keyed properties. Use
Reflect.ownKeys()when you need everything.