Pregunta

I start with multi-dimensional array like so:

[
    ['prop1', 'prop2', 'prop3', 'prop3'],
    ['val1', 'val2', 'val3', 'val4']
]

I need to transform this in an object like this one:

{ prop1: 'val1',
  prop2: 'val2',
  prop3: ['val3', 'val4']
}

Normally I could use _.object(array[0], array[1]) to solve this but the presence of the duplicate key prop3 complicates things quite a bit;

Actual _.object will set prop3: 'val4', erasing val3

Anyone got an idea on how I could achieve the results presented above?

Thank you

¿Fue útil?

Solución

I'd probably do something like this, which would be easy enough to turn into an underscore mixin:

var arr = [
    ['prop1', 'prop2', 'prop3', 'prop3'],
    ['val1', 'val2', 'val3', 'val4']
],
    keys = arr[0],
    vals = arr[1],
    obj = {};

keys.forEach( function( name, index )
{
    if ( !Object.prototype.hasOwnProperty.call(obj, name) )
    {
        obj[name] = vals[index];
    }
    else
    {
        if ( obj[name] instanceof Array )
        {
            obj[name].push( vals[index] );
        }
        else
        {
            var val = obj[name];
            obj[name] = [val, vals[index]];
        }
    }
} );

Example in a fiddle. (turned into a mixin)

jsPerf comparison with underscore-based code.

Otros consejos

Simply zipping the arrays and then using a custom reduce should do the trick. Should be pretty simple to adapt this if your data arr can contain more than 2 collections.

_.mixin({
   asArray: function(value) {//simple helper from https://github.com/jashkenas/underscore/pull/1467
     if (value == null) return [];
     if (value.length === +value.length && !_.isString(value)) return _.toArray(value);
     return [value];
   }
});


var arr = [
    ['prop1', 'prop2', 'prop3', 'prop3'],
    ['val1', 'val2', 'val3', 'val4']
];
/* ... */
_.chain(arr)
 .zip()
 .reduce(function(memo, propKey) {
   if(_.has(memo, propKey[0])) { // property already in the object. Make array if necessary and insert item at end
       (memo[propKey[0]] = _.asArray(memo[propKey[0]])).push(propKey[1]); //push value into object
   }
   else {
      memo[propKey[0]] = propKey[1];
   }
   return memo;
}, {})
 .value();

Produces: {"prop1":"val1","prop2":"val2","prop3":["val3","val4"]}

The asArray can be easily replaced if you don't want to mixin. See previous revision.

Just for completeness I wanted to mention this method:

http://lodash.com/docs#zipObject - or (new) _.object()

Creates an object composed from arrays of keys and values. Provide either a single two dimensional array, i.e. [[key1, value1], [key2, value2]] or two arrays, one of keys and one of corresponding values.

Example

_.zipObject(['fred', 'barney'], [30, 40]);
// → { 'fred': 30, 'barney': 40 }

Unfortunately, it doesn't combine several values into an array for one key:

_.object(['key1', 'key2'], [1,2]);
// { key1: 1, key2: 2 }
_.object(['key1', 'key1'], [1,2]);
// { key1: 2 }
_.object(['key1'], [1,2]);
// { key1: 1 }

So this is not really the solution to your answer but I think it's still valuable to take note of this function.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top