I've written a method that takes an object whose keys are arrays and converts it to an array of objects.
I feel like the code is fairly complex, after waiting a week or so and revisiting it, I don't think anyone would be able to really understand what is going on, especially if they aren't used to this style of coding. [read: I had no idea what I did there.]
function zipObj(obj) {
// First get keys from object.
var keys = _.keys(obj);
// For each key, grab the data
var propertyArray = _.map(keys, function(k, i) { return obj[k] || undefined; });
// "Shift" propertyArray around, so we can make into array of objects
var zippedArray = _.zip.apply(_, propertyArray);
// Set up function that will take an array,
// Then use the keys variable to re-create.
var createObject = function(d) {
var rObj = {};
_.each(keys, function(k, i) { rObj[k] = d[i]; });
return rObj;
};
// For each item in zipped array, return an object
return _.map(zippedArray, createObject);
}
From what I understand, a lot of people writing in a functional style would reduce this down to smaller functions, however I'm not 100% sure the best way of doing that would be in this example. I don't think reducing each step of this into its own functionality would necessarily increase readability, especially with how many little things that are going on here.
(If you open this and click Run, it will alert you with the function's inputs/ outputs.)
var input = {
key1: [ 'val1', 'val2', 'val3' ],
key2: [ 'val4', 'val5', 'val6' ],
key3: [ 'val7', 'val8', 'val9' ]
}
window.alert("INPUT:\n" + JSON.stringify(input, null, ' '));
function zipObj(obj) {
var arr = _.zip.apply(_, _.map(_.keys(obj), function(k, i) { return obj[k] || undefined; }));
return _.map(arr, function(d) {
var rObj = {};
_.each(_.keys(obj), function(k, i) { rObj[k] = d[i]; });
return rObj;
});
}
window.alert("OUTPUT:\n" + JSON.stringify(zipObj(input), null, ' '));
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
1 Answer 1
When defining propertyArray
, you don't need to handle undefined
explicitly. (It would have been nice to use _.pluck()
, but unfortunately it is designed to take arguments in the wrong order.)
_.object()
is too delicious to pass up! You basically reimplemented it as createObject()
. The function that is passed to map()
can be further simplified by currying.
var input = {
key1: [ 'val1', 'val2', 'val3' ],
key2: [ 'val4', 'val5', 'val6' ],
key3: [ 'val7', 'val8', 'val9' ]
};
window.alert("INPUT:\n" + JSON.stringify(input, null, ' '));
function zipObj(obj) {
var keys = _.keys(obj);
var values = _.map(keys, function(k) { return obj[k]; });
// Transpose the values matrix
var valueSlices = _.zip.apply(_, values);
return _.map(valueSlices, _.partial(_.object, keys));
}
window.alert("OUTPUT:\n" + JSON.stringify(zipObj(input), null, ' '));
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
I suspect that var values = ...
could be simplified as _.values(obj)
, but I don't see anything in the Underscore.js documentation that guarantees that _.values(obj)
will list the values in the same order as _.keys(obj)
, so I hesitate to recommend it.
-
\$\begingroup\$ Thanks a ton, that made it much more readable! You are 100% right about
_.object()
, that's one of those methods I never really committed to memory. The currying example there was pretty neat as well, currying for me never immediately comes to mind. I looked at the source for_.values()
and it seems that it would keep the same order, as it uses_.keys()
under the hood. All in all thanks for the go-over! Very informative. \$\endgroup\$AlbertEngelB– AlbertEngelB2014年11月25日 14:53:13 +00:00Commented Nov 25, 2014 at 14:53 -
\$\begingroup\$ But is there any guarantee that
_.keys()
produces results in some order repeatably? \$\endgroup\$200_success– 200_success2014年11月25日 15:56:06 +00:00Commented Nov 25, 2014 at 15:56 -
\$\begingroup\$ I'm fairly certain it does. If
Object.keys
is defined it uses that method and falls back tofor..in
if it isn't. (According to MDN,Object.keys
uses the same order asfor..in
). That being said, there isn't any guarantee that this won't change in the future. \$\endgroup\$AlbertEngelB– AlbertEngelB2014年11月25日 16:01:04 +00:00Commented Nov 25, 2014 at 16:01
Explore related questions
See similar questions with these tags.