Proposal: Object.{columns,fromColumns}

Sometimes, we have a list of labels and a list of values we want to associate with them. For example:

const myKeys = ["firstName", "lastName", "age"];
const myValues = ["John", "Doe", 30];
// We would like to have
// const myObj = { firstName: "John", lastName: "Doe", age: 30 };

We could solve this by writing the following:

// Method 1
const myObj = {};
for (let i = 0; i < myKeys.length; i++) {
  myObj[myKeys[i]] = myValues[i]
}

// Method 2
const myObj = Object.fromEntries(myKeys.map((key, i) => [key, myValues[i]]))

I think it would be better to have a method to directly create such an object from these two arrays (or any iterable?). For example Object.fromColumns (first name I thought of, could be something else).

const myObj = Object.fromColumns(myKeys, myValues);

We could also have the reverse operation Object.columns, even though it's already pretty simple with Object.{keys,values}.

const [keys, values] = Object.columns(myObj);
// Equivalent to 
// const keys = Object.keys(myObj);
// const values = Object.values(myObj);

Can/should we add this? Should it only apply to "Object" (then it needs to check that the keys are valid), or extend it to "Map" (or only to Map, since it does not require any validation for keys) and/or "WeakMap"?

An implementation of this proposal might be unnecessary if the stage 1 proposal "joint iteration" makes it to stage 4. We could simply write Object.fromEntries(Array.from(Iterator.zipToArrays([keys, values]))). Although it is still quite long to write.

Prior art in other languages:

Language Syntax (from columns) Syntax (get columns)
PHP array_combine($keys, $values) array_keys($arr) and array_values($arr)
Python dict(zip(keys, values)) list(d.keys()) and list(d.values())
Ruby ? hash.keys and hash.values
Java IntStream.range(0, keys.size()).boxed().collect(Collectors.toMap(keys::get, values::get)); map.keySet() and map.values()

Prior art in JS libraries:

Library Syntax (from columns) Syntax (get columns)
Lodash _.zipObject(keys, values) _.keys(obj) and _.values(obj)
Underscore _.object(keys, values) _.keys(obj) and _.values(obj)
Ramda R.zipObj(keys, values) R.keys(obj) and R.values(obj)

Note: While it's mostly known as "zip" and "unzip", I am not sure that they would be the best name for these methods. Mainly because of slightly different names in libraries and it would be very different from the joint iteration proposal Iterator.zipToObject (at least to my understanding of the spec).

With https://github.com/tc39/proposal-joint-iteration, you could Object.fromEntries(Array.zip(myKeys, myValues)), which seems like it’d suffice?

2 Likes

If we get Array.zip, sure. But I did not find this method in the explainer nor in the specification. That's why I wrote it like Object.fromEntries(Array.from(Iterator.zipToArray([myKeys, myValues]))), which I find quite long to write.

Object.fromEntries works fine with an iterator, so even without Array.zip, Object.fromEntries(Iterator.zip([myKeys, myValues])) will work

1 Like