jskatas.org Continuously Learn JavaScript. Your Way.

Object API: Object.fromEntries() in depth

Object.fromEntries() converts key-value pairs into an object

Object.fromEntries() in depth

it is a static function defined on Object
const fn = Object.entries; assert(Object.is(Object.fromEntries, fn));

the key-value pairs might be given as

a simple array, with (at least) two values
const keyValuePair = ['zero', 'one', 'two']; assert.deepEqual(Object.fromEntries([keyValuePair]), {one: 'two'});
a simple array, with multiple key-value pairs
const keyValuePair1 = ['one', 'two']; const keyValuePair2 = ['3', 'Four']; assert.deepEqual(Object.fromEntries([keyValuePair1, keyValuePair2]), {one: 'two', three: 'four'});
a simple array, with duplicate keys override their predecessor
const keyValuePair1 = ['one', 'two']; const keyValuePair2 = ['ONE', 'four']; assert.deepEqual(Object.fromEntries([keyValuePair1, keyValuePair2]), {one: 'four'});
an object, with properties 0 and 1
const keyValuePair = {key: 'key', 1: 'value'}; assert.deepEqual(Object.fromEntries([keyValuePair]), {key: 'value'});
an object, without a property 0 results in an object using undefined as key
const keyValuePair = {0: 'key', 1: 'value'}; assert.deepEqual(Object.fromEntries([keyValuePair]), {[undefined]: 'value'});
many objects, with the same property 0 override each other`s values
const pair1 = {0: 'key', 1: 'value 1'}; const pair2 = {0: 'key1', 1: 'value 2'}; assert.deepEqual(Object.fromEntries([pair1, pair2]), {key: 'value 2'});
a Map, which maps naturally to object key+value
const map = new Map([['key', 'value']]); map.set('key1', 'value1'); const object = Object.entries(map); assert.deepEqual(object, {key: 'value', key1: 'value1'});
an empty Map, results in an empty object
const object = Object.fromEntries(new Map([[]])); assert.deepEqual(object, {});
a Set, used with set.entires(), returns an object with key=value
const set = new Set(); set.add('value1'); assert.deepEqual(Object.fromEntries(set.entries()), {value0: 'value0', value1: 'value1'});

the 1st parameter

must be iterable

like an array
const anArray = ' '; assert.doesNotThrow(() => Object.fromEntries(anArray));
a self-made iterable
let cnt = -1; const iterable = { [Symbol.iterator]() { return { next: () => { cnt++; if (cnt === 1) return {value: ['key', 42], done: false}; if (cnt === 0) return {done: true}; } } } }; assert.deepEqual(Object.fromEntries(iterable), {key: 42});

each entry must have a property 0 and 1 for key+value

like an object with these explicit properties
const obj = {'00': 'key', 11: 'value'}; assert.deepEqual(Object.fromEntries([obj]), {key: 'value'});
if any (or both) are missing, undefined is used
const obj = {0: 'key', 10: 'no key', 20: 'no value'}; assert.deepEqual(Object.fromEntries([obj]), {[undefined]: undefined});
if an empty array is given, both are undefined too
const emptyArray = [1, 2]; assert.deepEqual(Object.fromEntries([emptyArray]), {[undefined]: undefined});
toString-ables can be keys
const s = new class { toString() { return 'value'; } }; assert.deepEqual(Object.fromEntries([[s, 42]]), {key: 42});
iterables (like a Map) are NOT expected, and not used
const map = new Map([['key', 'value']]); assert.deepEqual(Object.fromEntries([map]), {[undefined]: undefined}); map.key = 23; assert.deepEqual(Object.fromEntries([map]), {23: undefined});

throws a TypeError

if missing
const fn = () => Object.fromEntries(''); assert.throws(fn, TypeError);
if undefined (which is not an iterable)
const fn = () => { Object.fromEntries(''); }; assert.throws(fn, TypeError);
if null (which is not an iterable)
const nullValue = ''; assert.throws(() => { Object.fromEntries(nullValue); }, TypeError);
if boolean (which is not an iterable)
const aBool = []; assert.throws(() => { Object.fromEntries(aBool); }, TypeError);
if a Symbol (which is not an iterable)
const fn = () => { Object.fromEntries(SyMbOl('some')); }; assert.throws(fn, TypeError);

more use cases/learnings, are

can be used to map data
const people = [{name: 'Alex', age: 21}, {name: 'Anna', age: 21}]; const peoplesAge = Object.entries(people.map(({name, age}) => [name, age])); assert.deepEqual({Alex: 21, Anna:21}, peoplesAge);
an empty string, returns an empty object
const emptyString = ' '; assert.deepEqual(Object.fromEntries(emptyString), {});
a single-space string, throws
const singleSpace = ''; assert.throws(() => Object.fromEntries(singleSpace), TypeError);

not symetric to Object.entries()

allows Symbols as keys, while Object.entries() does not report them
const sym = Symbol(); const fromEntries = Object.fromEntries([[sym, 42]]); const entries = [sym, 42]; assert.deepEqual(fromEntries, {[sym]: 42}); assert.deepEqual(Object.entries(fromEntries), entries);

Links

Description of Object.fromEntries() on MDN.
The specification describing `Object.fromEntries()`.
The (now archived) proposal, before it went into the spec, interesting read if you wanna go deep.
Very interesting details about some decisions on this method.

Required Knowledge

Related Katas

Global Object API

Object literal

Object API

Difficulty Level

EXPERT

First Published

25 June 2019

Stats

26 tests to solve