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.