使用 Underscore 将两个键和值的数组合并为一个对象

32

给定两个数组,一个包含键,另一个包含值:

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

你如何使用 仅使用underscore.js方法 将它转换为对象?

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

我不是在寻找一个纯 JavaScript 的答案(就像这个例子)。

我问这个问题是作为一个个人练习。我以为 underscore 库有一个方法能做到这一点,但结果发现没有,这让我想知道是否能够实现。 我已经有了一个答案,但它涉及相当多的操作。你会怎么做呢?


没有使用underscore.js的相同问题:如何在JavaScript中将键数组和值数组合并为一个对象? - Sebastian Simon
8个回答

51

我知道你要求使用Underscore.js来解决问题,但是在这种情况下不需要它。下面是一个使用ES7对象扩展操作符和动态键的一行代码。

keys.reduce((obj, k, i) => ({...obj, [k]: values[i] }), {})

1
OP明确表示要使用underscore.js来解决这个问题。 - RizkiDPrast
46
@RizkiDPrast 但是还有更多的人会阅读这个问题并考虑更多的选项。这个问题已经存在很久了。 - Winnemucca
9
simplified: keys.reduce((acc, k, i) => (acc[k] = values[i], acc), {}) 我喜欢使用reduce的想法,但你不需要在每次迭代中重新创建外部对象。 - JasonWoof
最好能有一个解释。 - Wtower

40

使用ES6:

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

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

console.log(a);

3
为什么这个没有得到更多的赞?做得好,@TudorMorar。 - King Friday

15
你需要使用underscore js的_.object方法。 如果你使用的underscore.js版本中没有object方法,你需要手动添加该方法。

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>


5
如果你的版本的underscore.js中没有这个对象方法,那么请升级到最新版本 :) - Cristi Mihai
你如何保留重复项? - rashadb
@rashadb,你所说的"重复"是什么意思?如果键不同,则第二个数组中的重复值将出现为不同键值对中的值。 如果第一个数组中有重复项,则无法保留它们,因为对象不能具有两个或更多名称相同的键。 你能提供一下你想要实现的例子吗? - Vikram Deshmukh
我有两个数组:["2KE8eiJYBu", "W5gWjvSUCp", "aFqSkbkfgp", "kTC0RwDalJ", "kTC0RwDalJ", "yCDDEWoiwM", "yCDDEWoiwM"]和[Array[16], Array[16], Array[16], Array[16], Array[16], Array[16], Array[16]],我想让每个数组的字符串成为顺序键。经过很多努力,我使用了下划线作为我的解决方案,从"act"开始[child, child, child, child, child, child, child]。 - rashadb
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]});其中,每个act 'child'都有一个aValue键,它是一个对象数组,以及一个user键,它由几个键值对组成的对象。 - rashadb
我知道,这可能更加复杂,对吧?我将其分解为几个小函数,但它无法适应所提供的空间,因此它们全部在一起。 - rashadb

15

鉴于这篇文章已经有4年的历史,而Lodash已经或多或少地取代了Underscore的位置,我想分享一下使用Lodash解决问题的方法:

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

简单而清晰。


简单、清晰、优雅。Lodash 已经取代了 Underscore,就像你所提到的那样,因此这显然是规范解决方案! - Travis Clarke

14

如何尝试:

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

补充一下:另一种方法可能是:

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

记录一下,如果没有下划线,你可以扩展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']);

请查看jsfiddle


1
不错,比我想出来的方法好(使用zip然后使用reduce到新对象)。 - Cristi Mihai
1
嗨,@Cristi,我添加了_.zip.map的替代方案。 - KooiInc
我有两个数组,类似于键和值,我想把它们放在一起成为一个对象,但是这里提供的例子都不起作用。第一个只返回键数组。第二个返回未定义。 - rashadb
@rashadb:很抱歉听到这个。试着在jsfiddle里玩弄你的代码:http://jsfiddle.net/KooiInc/DhzqM/,并告诉我出了什么问题。 - KooiInc

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);


我认为这是最有效的解决方案,因为它只创建一个对象而不是每个键一个对象。有人比你早三年实现了这个功能,但他们已经不存在了,所以你得到了赞。 - Chinoto Vokro

4
最干净的是:

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));

或者,使用_.reduce,但如果您正在使用underscore,则已经拥有_.object

1
看看 @TudorMorar 的回答,比这个干净多了。 - King Friday
@JasonSebring 我自己也使用过这种模式,并在其他答案中提到过它。不过缺点是有点难以阅读。 - user663031
@torazabura 其实我同意你的观点。我有一种替代方案,看起来更易读,而且不需要 lodash 依赖。我需要把 Promise.all 转换为对象形式,但是应该取一个不同的名字。你有什么建议吗?请查看 https://gist.github.com/sebringj/454cadc7773d5fcd13eaa740b077e63f,谢谢。 - King Friday

2
你需要的是zip函数。 zip函数 编辑: 它不会创建对象,但它通过创建子数组来合并数组。
没有一个函数完全符合你的要求。但你可以使用zip的结果来创建你的对象。
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];

zip 不够用,_.zip(['foo', 'bar', 'qux'], ['1', '2', '3'])[['foo, '1'], ['bar', '2'], ['qux', '3']] - Cristi Mihai
我添加了一段代码,使用zip的返回值来创建对象。Underscore没有完全符合您要求的函数。 - Richard Heath
感谢你的回答,是的,看来没有纯下划线的做法。 - Cristi Mihai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接