如何将数组转换为对象?

826
什么是最好的转换方式:
['a','b','c']

到:

{
  0: 'a',
  1: 'b',
  2: 'c'
}

2
如果其他人正在寻找Lodash解决方案,请考虑使用_.keyBy(以前称为_.indexBy):https://lodash.com/docs#keyBy - 2540625
1
这有点令人困惑,因为数组已经是对象了,但我猜问题的重点是将数组 异类对象转换为普通对象 - Oriol
3
使用 Lodash 来简单实现这个操作的方法是 _.toPlainObject。例如:var myObj = _.toPlainObject(myArr) - julian soro
46个回答

1003

ECMAScript 6 引入了易于填充的 Object.assign 方法:

Object.assign() 方法用于将一个或多个源对象的所有可枚举自有属性的值复制到目标对象中。它将返回目标对象。

Object.assign({}, ['a','b','c']); // {0:"a", 1:"b", 2:"c"}

数组的length属性不可枚举,因此不会被复制。

此外,您可以在对象上使用ES8的扩展语法来实现相同的结果:

{ ...['a', 'b', 'c'] }

对于自定义键,您可以使用reduce

['a', 'b', 'c'].reduce((a, v) => ({ ...a, [v]: v}), {}) 
// { a: "a", b: "b", c: "c" }

4
请注意 - 如果您已经有了一个原始对象排序后的属性数组,使用展开运算符会将该数组直接转换为一个新对象: { ...[sortedArray]} - HappyHands31
22
Oriol,有没有一种方法可以设置固定的密钥,而不是0和1? - Menai Ala Eddine - Aladdin
5
通常这不是您想要的。通常您想使用数组元素中的属性作为键。因此,reduce 就是您想要的。 - Thomas Beauvais
2
如果我想要的键不是0、1、2,而是与值相同怎么办?例如:{a: 'a'} 我们该如何实现? - Gel
9
使用reduce函数来实现: arr.reduce((a, v) => ({ ...a, [v]: v}), {}),即可将数组转化为对象,对象的键值和原数组相同。 - David Hellsing
显示剩余5条评论

557

使用以下这个函数:

function toObject(arr) {
  var rv = {};
  for (var i = 0; i < arr.length; ++i)
    rv[i] = arr[i];
  return rv;
}

你的数组已经基本上是一个对象了,但是数组在处理整数命名属性时有一些有趣的特殊行为。上面的代码将会给你一个纯对象。

补充,你可能还需要处理数组中的“空洞”:

function toObject(arr) {
  var rv = {};
  for (var i = 0; i < arr.length; ++i)
    if (arr[i] !== undefined) rv[i] = arr[i];
  return rv;
}

在现代JavaScript运行时中,您可以使用.reduce()方法:

var obj = arr.reduce(function(acc, cur, i) {
  acc[i] = cur;
  return acc;
}, {});

这也避免了数组中的“空洞”,因为这就是.reduce()的工作原理。


22
避免参数重新赋值的更简洁方式是:const obj = arr.reduce((acc, cur, i) => ({ ...acc, [i]: cur }), {}); - huygn
6
箭头函数 + 解构语法,无需显式返回:const obj = arr.reduce((obj, cur, i) => ({ ...obj, [i]: cur }), {}); - Marc Scheib
2
正如@VivekN所说,每次都创建新对象是必要的吗?那arr.reduce((obj, cur, i) => (obj[i]=cur,obj), {});呢? - miro
3
@eugene_sunic 当然,但当我在2010年回答此问题时,那种方法还未出现 :) - Pointy
3
正如@miro所指出的那样,每次创建一个新对象都是不必要的,事实上比操作最初创建的对象要慢得多。在1000个元素的数组上运行JSPerf测试,每次创建一个新对象比改变现有对象要慢1,000倍。https://jsperf.com/array-prototype-reduce-mutate-vs-intermediate-objects - romellem
显示剩余11条评论

362

您可以使用一个称为“累加器”的reduce

['a','b','c'].reduce(function(result, item, index, array) {
  result[index] = item; //a, b, c
  return result;
}, {}) //watch out the empty {}, which is passed as "result"

将一个空对象 {} 作为初始状态;然后逐步“增强”该对象。 在迭代结束时,result 将会是 {"0": "a", "1": "b", "2": "c"}

如果你的数组是一组键值对对象:

[{ a: 1},{ b: 2},{ c: 3}].reduce(function(result, item) {
  var key = Object.keys(item)[0]; //first property: a, b, c
  result[key] = item[key];
  return result;
}, {});

将会输出:{a: 1, b: 2, c: 3}

为了完整起见,reduceRight允许您以相反的顺序遍历数组:

[{ a: 1},{ b: 2},{ c: 3}].reduceRight(/* same implementation as above */)

将产生:{c:3,b:2,a:1}

您的累加器可以是任何类型,以满足特定目的。例如,为了在数组中交换对象的键和值,请传递[]

[{ a: 1},{ b: 2},{ c: 3}].reduce(function(result, item, index) {
  var key = Object.keys(item)[0]; //first property: a, b, c
  var value = item[key];
  var obj = {};
  obj[value] = key;
  result.push(obj);
  return result;
}, []); //an empty array

将产生:[{1: "a"}, {2: "b"}, {3: "c"}]

map不同,reduce可能无法用作1对1映射。 您可以完全控制要包含或排除的项目。 因此,reduce允许您实现filter的功能,这使得reduce非常灵活:

[{ a: 1},{ b: 2},{ c: 3}].reduce(function(result, item, index) {
  if(index !== 0) { //skip the first item
    result.push(item);
  }
  return result;
}, []); //an empty array

将产生:[{2: "b"}, {3: "c"}]

注意reduceObject.keyECMA 5th edition的一部分; 您应该为不支持它们的浏览器提供polyfill(特别是IE8)。

请参见Mozilla的默认实现:Mozilla


2
当您使用对象映射并且需要删除其中一个键时,有助于维护redux存储。 - dhruvpatel

106

如果您正在使用jQuery:

$.extend({}, ['a', 'b', 'c']);

4
如果我输入一个数组变量,它会把每个字符放在一个独立的对象中:0:"a",1:",",2:"b"......所以它忽略引号但包括逗号......这很奇怪。 - TrySpace
@Max 我认为没有这样的行为。返回的对象是 {0: 'a', 1: 'b', 2: 'c'},这是一个预期的结果。 - ivkremer
3
是否有一种方法可以在使用$.extend的同时以非数字方式通知键,即对数组项进行计算? - Davi Lima
超级棒的短测试 - Faisal Mehmood Awan

75
为了完整性,ECMAScript 2015(ES6)使用扩展运算符需要一个转译器(Babel)或至少支持ES6的环境。

console.log(
   { ...['a', 'b', 'c'] }
)


2
实际上,这在ES2015中也不是本地可用的。然而,它非常优雅。 - Aluan Haddad
如何使用展开运算符获取结果 - { "a": "a", "b":"b", "c":"c" }? - Kanish
1
在ES2018中可用 - panzerpower

59

如果您想使用迭代对象的属性作为键,例如:

// from:
const arr = [
    {
        sid: 123,
        name: 'aaa'
    },
    {
        sid: 456,
        name: 'bbb'
    },
    {
        sid: 789,
        name: 'ccc'
    }
];
// to:
{
  '123': { sid: 123, name: 'aaa' },
  '456': { sid: 456, name: 'bbb' },
  '789': { sid: 789, name: 'ccc' }
}

使用:

const result = arr.reduce((obj, cur) => ({...obj, [cur.sid]: cur}), {})

我在想这个reduce的速度有多快。每次迭代中,对象都会使用扩展运算符被克隆一遍又一遍? - Balazs Zsoldos

48

这里很少有人评论Object.fromEntries,我非常喜欢它,因为它更加简洁,而且与TypeScript易于集成,无需过多关注泛型和其他问题。如果需要,它还可以使用map来自定义键。缺点是:如果你想要自定义键,则需要额外的map。例如:

const tags = [
  { name: 'AgeGroup', value: ageGroup },
  { name: 'ApparelTypes', value: apparelTypes },
  { name: 'Brand', value: brand },
  // ...
]

const objectTags = Object.fromEntries(tags.map((t) => [t.name, t.value]))

/*
{
  AgeGroup: 'Adult',
  Apparel: 'Creeper, Jacket'
  Brand: '',
  // ...
}
*/

48

我可能会这样写(因为很少情况下我手头没有underscorejs库):

var _ = require('underscore');

var a = [ 'a', 'b', 'c' ];
var obj = _.extend({}, a);
console.log(obj);
// prints { '0': 'a', '1': 'b', '2': 'c' }

9
你给我的回答投了反对票,但没有留下任何评论?我的答案是正确的并经过测试的。您有什么异议吗? - Dave Dopson
16
这个问题没有下划线标签,而且你还假定了使用node.js或require库。 - David Hellsing
11
“underscore” 在客户端和服务端都很常见。它是 Backbone.js 的默认依赖库,也可能是最常用的实用库。话虽如此,我包含了 require 行以明确表明我正在使用一个库。对于在浏览器环境中添加“underscore.js”并没有简单的一句话描述,需要进行一些翻译工作。 - Dave Dopson
6
如果您正在使用下划线,简单的 obj=_.extend({},a); 就可以完成任务。此外,如果您正在迭代数组,我建议使用 _.each 而不是 _.map。总的来说,这个答案在多个层面上都不好。 - David Hellsing
3
@CharlieMartin的比喻完全有缺陷,因为lodash、underscore和jQuery不是“标准库”,在许多JavaScript用例中,直接添加库会直接影响最终用户,为了琐碎的原因而这样做是不应该的。 - morewry
显示剩余3条评论

30
我们可以使用 Object.assignarray.reduce 函数将一个数组转换为对象。
var arr = [{a:{b:1}},{c:{d:2}}] 
var newObj = arr.reduce((a, b) => Object.assign(a, b), {})

console.log(newObj)


1
这正是我所需要的。我不需要索引,我需要将数组转换为保留键值对。 - Mike K
加一分给功夫,我需要它来将 arr = [{ b: 1, d: 2 }] 转换为对象。 - Timo

27

这里只是为了完整性而提供的一个O(1)的ES2015方法。

var arr = [1, 2, 3, 4, 5]; // array, already an object
Object.setPrototypeOf(arr, Object.prototype); // now no longer an array, still an object

(1)根据MDN,改变对象的[[Prototype]]是一个非常缓慢的操作。(2) 这不会移除自身的length属性。(3)该对象仍然是数组,Array.isArray(arr)===true。(4)不会移除特殊的数组行为,例如arr.length=0可移除所有索引。(5)因此,我认为Object.assign更好 - Oriol
1
@Oriol mdn是错误的,在V8(以及其他引擎)中,这在特定情况下是一种非常快速的操作 - 因为对象已经有了数字存储,所以基本上只是更改了两个指针。 - Benjamin Gruenbaum
这也可以用于事实上它显然是在所有快速解决方案中独一无二的,即保留数组上的任何非索引(“自有”)属性(即非正整数属性)... - Brett Zamir
但是,嗯,Array.isArray 对于这里的“object”返回true,即使 instanceof Array 不返回... - Brett Zamir
@BrettZamir 听起来像是一个 bug :D - Benjamin Gruenbaum

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