将对象数组转换为对象

3

我有一个分支数组,大致长这样:

let branches = [
  {
    id: 21,
    name: "Branch 1",
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  {
    id: 22,
    name "Branch 2"
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  // .. etc
]

但我希望将其转换为以名称作为每个键的对象。

期望输出:

branches = {
  "Branch 1": {
    id: 21,
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  "Branch 2": {
    id: 22,
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  }
}

尝试过:

let newBranches = branches.map(branch => (
  {
    [branch.name]: {
      id: branch.id,
      days: branch.opening_times
    }
  }
));
console.log(newBranches)

当然,映射函数会给我一个数组输出:
[
  0: {Branch 1: {…}}
  1: {Branch 2: {…}}
]

有人能帮我指向正确的方向,以获取一个新对象,其中name键本身是一个对象吗?

7个回答

6
通过简单的reduce()操作和对象解构:

const branches = [{
    id: 21,
    name: "Branch 1",
    opening_times: []
  },
  {
    id: 22,
    name: "Branch 2",
    opening_times: []
  }
];

const result = branches.reduce((a, {name, ...v}) => (a[name] = v, a), {});

console.log(result);


为什么不直接使用 a[name] = v 呢? - adiga
@adiga OP 表示 name 属性不应该在值对象中。 - Robby Cornelissen
我是指 (a[name] = v, a) 而不是 (a[name] = {...v}, a) - adiga
@adiga 我知道你的意思。那样做会将 name 属性包含在值对象中。 - Robby Cornelissen
但是解构已经照顾到了这个问题,对吗? v 已经是一个没有 name 属性的新对象。{...v} 只是克隆了 v - adiga
@adiga 哇,抱歉,我想我的大脑可能短路了。非常感谢你。我已经更新了我的答案。 - Robby Cornelissen

5

我会使用简单的for-of循环。你可能会得到reduce的答案,但在这里使用reduce只会增加复杂性。

const result = {};
for (const {name, id, opening_times} of branches) {
  result[name] = {id, opening_times};
}

现场示例:

let branches = [
  {
    id: 21,
    name: "Branch 1",
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  {
    id: 22,
    name: "Branch 2",
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  // .. etc
];
const result = {};
for (const {name, id, opening_times} of branches) {
  result[name] = {id, opening_times};
}
console.log(result);
.as-console-wrapper {
    max-height: 100% !important;
}


加入Code Maniac的建议,使用rest操作符:

const result = {};
for (const {name, ...entry} of branches) {
  result[name] = entry;
}

实时示例:

let branches = [
  {
    id: 21,
    name: "Branch 1",
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  {
    id: 22,
    name: "Branch 2",
    opening_times: [ {}, {}, {} ] // Array of objects (Monday, Tuesday etc)
  },
  // .. etc
];
const result = {};
for (const {name, ...entry} of branches) {
  result[name] = entry;
}
console.log(result);
.as-console-wrapper {
    max-height: 100% !important;
}

这两种写法略有不同,第一种方法明确地只使用了结果中的 idopening_times 属性,但是另一种则使用除了 name 以外的所有属性。当然,它们在可读性上也存在差异(显式 vs. 隐式),但是我会根据不同的情况选择其中之一。


2
喜欢可读性,相比于reduce方法更容易理解。不过你有没有想过哪种方法更快呢? - Eli Nathan
4
顺便提一句:我想我们可以在这里使用“剩余参数(rest parameter)”来代替把所有属性取出来再重新创建对象,即 for (const {name, ...rest} of branches) - Code Maniac
2
@EliNathan - 在现代JavaScript引擎上,我认为这是一个平衡的问题。for-of创建并使用迭代器(受优化的影响,JavaScript引擎绝对可以做到),这涉及函数调用。reduce不使用迭代器,但具有对回调的调用。所以... - T.J. Crowder
当然,你必须在“branches”中至少有数万个条目才会有影响(或者你必须在一个紧密的循环中一遍又一遍地执行此操作)。 - T.J. Crowder
1
实际上,我将在这里使用显式版本,因为我为了这个问题简化了branches数组。它有很多我不需要添加到我的新对象中的东西。谢谢! - Eli Nathan

4

ES 2019 草案提供了 Object.fromEntries 来实现这一目的:

result = Object.fromEntries(branches.map(({name,...rest}) => [name, rest]))

大多数浏览器已经实现了它,但是填充程序很容易:

Object.fromEntries = iter =>
    Object.assign({},
        ...[...iter].map(
            ([k, v]) => ({[k]: v})
        ));

你需要移除name属性的值,以匹配预期输出。 - Code Maniac

4
你可以通过将带有所需name键和对象的其余部分的新对象扩展到所有对象上来进行分配。

let branches = [{ id: 21, name: "Branch 1", opening_times: [{}, {}, {}] }, { id: 22, name: "Branch 2", opening_times: [{}, {}, {}] }],
    newBranches = Object.assign({}, ...branches.map(({ name, ...o }) => ({ [name]: o })));

console.log(newBranches);
.as-console-wrapper { max-height: 100% !important; top: 0; }

使用即将到来的Object.fromEntries方法

let branches = [{ id: 21, name: "Branch 1", opening_times: [{}, {}, {}] }, { id: 22, name: "Branch 2", opening_times: [{}, {}, {}] }],
    newBranches = Object.fromEntries(branches.map(({ name, ...o }) => [name, o]));

console.log(newBranches);
.as-console-wrapper { max-height: 100% !important; top: 0; }


1
为什么你的每个答案都这么复杂?一个刚理解for循环的新用户甚至不会有一点点想法,你能不能再简单些。 - Shubham Dixit
@Shubh 我已经看到 Nina Scholz 的答案有几年了,它们一直是最出色的,并且通常是针对任何给定问题的最具创意的解决方案。Stack Overflow 不仅旨在成为初学者的知识库。 - Robby Cornelissen
@RobbyCornelissen,我也是这样认为,但我想应该有一些更详细的解释或一些新用户的帮助链接,这样可以扩大理解的范围。 - Shubham Dixit

2
你可以使用reduce。最初的回答。

let branches = [{id:21,name:"Branch 1",opening_times:[{},{},{}]},{id:22,name:"Branch 2" ,opening_times:[{},{},{}]}];
const res = branches.reduce((acc, { name, ...rest }) => (acc[name] = { ...rest }, acc), {});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }

ES5 syntax:

var branches = [{id:21,name:"Branch 1",opening_times:[{},{},{}]},{id:22,name:"Branch 2" ,opening_times:[{},{},{}]}];
var res = branches.reduce(function(acc, curr) {
  acc[curr.name] = { id: curr.id, opening_times: curr.opening_times };
  return acc;
}, {});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }


1

let branches = [{
    id: 21,
    name: "Branch 1",
    opening_times: [{}, {}, {}] // Array of objects (Monday, Tuesday etc)
  },
  {
    id: 22,
    name: "Branch 2",
    opening_times: [{}, {}, {}] // Array of objects (Monday, Tuesday etc)
  }
]

let newBranches = {};

branches.forEach((el) => {
  newBranches[el.name] = {
    id: el.id,
    opening_times: el.opening_times
  };
});

console.log(newBranches)


我猜,定义一个全局变量并在循环中改变它的值从来都不是一个好主意,如果我们有一个复杂的代码结构,那么这可能会导致错误的输出。 - Shubham Dixit

1
您可以尝试这个(ES6)。
Object.assign({}, ...array.map(item => ({ [item.name]: item })));

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