31

Given two arrays, one with keys, one with values:

keys = ['foo', 'bar', 'qux']
values = ['1', '2', '3']

How would you convert it to an object, by only using underscore.js methods?

{
   foo: '1', 
   bar: '2',
   qux: '3'
}

I'm not looking for a plain javascript answer (like this).

I'm asking this as a personal exercise. I thought underscore had a method that was doing exactly this, only to find out it doesn't, and that got me wondering if it could be done. I have an answer, but it involves quite a few operations. How would you do it?

Community
  • 1
  • 1
Cristi Mihai
  • 2,505
  • 1
  • 19
  • 20

8 Answers8

51

I know you asked for Underscore.js solutions, but you don't need it for this. Here's a oneliner using ES7 object spread operator and dynamic keys.

keys.reduce((obj, k, i) => ({...obj, [k]: values[i] }), {})
ghirlekar
  • 1,071
  • 1
  • 9
  • 10
40

Using ES6:

let numbers = [1, 2, 3],
    names = ["John", "Mike", "Colin"];

let a = Object.assign({}, ...numbers.map((n, index) => ({[n]: names[index]})))

console.log(a);
Tudor Morar
  • 3,720
  • 2
  • 27
  • 25
15

What you need to use is the _.object method of underscore js. If object method is not present in your version of underscore.js then you will have to manually add this method to it.

keys = ['foo', 'bar', 'qux']
values = ['1', '2', '3']
_.object = function(list, values) {
  if (list == null) return {};
  var result = {};
  for (var i = 0, l = list.length; i < l; i++) {
    if (values) {
      result[list[i]] = values[i];
    } else {
      result[list[i][0]] = list[i][1];
    }
  }
  return result;
};

console.log(_.object(keys, values))
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Ruslan López
  • 4,433
  • 2
  • 26
  • 37
Vikram Deshmukh
  • 12,304
  • 4
  • 36
  • 38
  • 5
    _If object method is not present in your version of underscore.js_ then **update to latest version** :) – Cristi Mihai Jan 18 '13 at 15:22
  • How do you keep duplicates? – rashadb Mar 06 '15 at 04:54
  • @rashadb what do you mean by duplicates? If the key is different then duplicate values in the second array will appear as values in distinct key-value pairs. If there're duplicates in the first array then there's no way you can retain them since an object can not have two or more keys with the same name. Can you provide an example of what you're trying to achieve? – Vikram Deshmukh Mar 06 '15 at 10:06
  • I had two arrays: ["2KE8eiJYBu", "W5gWjvSUCp", "aFqSkbkfgp", "kTC0RwDalJ", "kTC0RwDalJ", "yCDDEWoiwM", "yCDDEWoiwM"] and [Array[16], Array[16], Array[16], Array[16], Array[16], Array[16], Array[16]] where i wanted to make the strings keys for each array in sequence. After too much struggle I used underscore for my solution starting with: "act" [child, child, child, child, child, child, child] – rashadb Mar 06 '15 at 10:53
  • var grouped = _.groupBy(_.zip(_.pluck(_.map(_.map(act, function(n){return _.pluck(n, "user")}), function(a){return _.first(a)}),"id"), _.map(_.map(act, function(n){return _.pluck(n, "aValue")}), function(a){return _.first(a)})), function(n){return n[0]}); where each of the act 'child' has an aValue key which is an array of objects and a user key which is an object made of several key value pairs. – rashadb Mar 06 '15 at 10:57
  • I know, could it be more convoluted, right? I broke this up to several mini functions but it wouldn't fit in the space provided so there it is all together. – rashadb Mar 06 '15 at 10:58
15

Given that this is 4 years old, and Lodash has more or less taken the place of Underscore, I thought I would share this solution using Lodash:

var keys = ['foo', 'bar', 'qux'];
var values = ['1', '2', '3'];
var obj = _.zipObject( keys, values );

Simple and clean.

Kevinleary.net
  • 8,851
  • 3
  • 54
  • 46
  • Simple, clean, and elegant. Lodash has superseded Underscore, like you mentioned, so this is clearly the canonical solution! – Travis Clarke Mar 20 '18 at 23:52
14

How about:

keys = ['foo', 'bar', 'qux'];
values = ['1', '2', '3'];
some = {};
_.each(keys,function(k,i){some[k] = values[i];});

To be complete: another approach could be:

 _.zip(['foo', 'bar', 'qux'],['1', '2', '3'])
  .map(function(v){this[v[0]]=v[1];}, some = {});

For the record, without underscore you could extend Array.prototype:

Array.prototype.toObj = function(values){
   values = values || this.map(function(v){return true;}); 
   var some;
   this .map(function(v){return [v,this.shift()]},values)
        .map(function(v){this[v[0]]=v[1];},some = {});
   return some; 
};
// usage
var some = ['foo', 'bar', 'qux'].toObj(['1', '2', '3']);

See jsfiddle

KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • 1
    Nice, better then what I came up with (`zip` then `reduce` to new object) – Cristi Mihai Aug 30 '12 at 15:05
  • 1
    Hi @Cristi, added a `_.zip` and `.map` alternative – KooiInc Aug 30 '12 at 18:17
  • I have two arrays like keys and values that I want to put together in an object but none of the examples provided herein work. The first only returns the keys array. The second returns undefined. – rashadb Mar 06 '15 at 05:06
  • @rashadb: I'm sorry to hear that. Try fiddling around with your code in the jsfiddle: http://jsfiddle.net/KooiInc/DhzqM/ and tell me what's going wrong – KooiInc Mar 07 '15 at 09:11
7

var toObj = (ks, vs) => ks.reduce((o,k,i)=> {o[k] = vs[i]; return o;}, {});

var keys=['one', 'two', 'three'],
    values = [1, 2, 3];
var obj = toObj(keys, values);
console.log(obj);
capipo
  • 67
  • 1
  • 6
  • I would expect this to be the most efficient solution due to only creating one object instead of an object per key. Someone else beat you by 3 years, but they don't exist anymore, so you get the upvote. – Chinoto Vokro Oct 09 '18 at 06:17
4

Cleanest is

keys = ['foo', 'bar', 'qux']
values = ['1', '2', '3']

function Arr2object(keys, vals) {
  return keys.reduce(
    function(prev, val, i) {
      prev[val] = vals[i];
      return prev;
    }, {}
  );
}

console.log(Arr2object(keys, values));

Or, use _.reduce, but if you're using underscore you already have _.object.

Ruslan López
  • 4,433
  • 2
  • 26
  • 37
  • 1
    Look at @TudorMorar 's answer. It is way cleaner than this. – King Friday May 02 '17 at 14:34
  • @JasonSebring I use that pattern myself and have given it in other answers. On the downside, it is a bit unreadable. –  May 02 '17 at 15:59
  • @torazabura actually I agree. I have an alternate implementation that seems way more readable, minus lodash dependancy. I needed to objectize Promise.all, actually should name it differently, any suggestions? see https://gist.github.com/sebringj/454cadc7773d5fcd13eaa740b077e63f thanks. – King Friday May 02 '17 at 16:01
2

What you are looking for is the zip function.

zip function

Edit: It doesn't create an object but it does combine the array by creating a sub array

There's no function that exactly does what you want. But you can use the result of zip to create your object.

var arr = _.zip(['foo', 'bar', 'qux'], ['1', '2', '3']);
var i, len = arr.length;
var new_obj = [];

for(i=0; i<len; ++i)
   new_obj[arr[i][0]] = arr[i][1];
Richard Heath
  • 349
  • 3
  • 12