使用reduce,如何创建一个对象目录,其中每个键都是唯一值并将重复值存储为子对象?

Using reduce, how to make an object directory where every key is a unique value and stores repeated values as sub-objects?

如何使用 reduce 创建一个函数,该函数将接收对象数组和 return 具有唯一键但允许重复值的对象目录?

  1. 每个键都是人名
  2. 每个重复的键都有一个带有数字目录的嵌套数组
  3. 如果没有重复的键,则键应该只包含一个对象

我希望输出如下所示:

{
  Bob: {
    '0': { name: 'Bob', age: 30, voted: true },
    '1': { name: 'Bob', age: 31, voted: true },
    '2': { name: 'Bob', age: 32, voted: true },
       },
  Jake: {
    '0': { name: 'Jake', age: 20, voted: false },
    '1': { name: 'Jake', age: 32, voted: true }
       },
  Phil: { name: 'Phil', age: 21, voted: true },
  Ed: { name: 'Ed', age: 55, voted: true },
  Tami: { name: 'Tami', age: 54, voted: true },
  Mary: { name: 'Mary', age: 31, voted: false },
  Becky: { name: 'Becky', age: 43, voted: false },
  Joey: { name: 'Joey', age: 41, voted: true },
  Jeff: { name: 'Jeff', age: 30, voted: true },
  Zack: { name: 'Zack', age: 19, voted: false }
}

这是我的代码,如果出现两个重复的名字,结果会相互吞噬,它会将第一个对象扩展到键中,而不向其添加数字键。

let count = 0
const toNamedObj = (arr) => {
  return arr.reduce((acc, cur) => {
    let key = Object.values(cur)[0]
    if(acc.hasOwnProperty(key)){
      count ++
      console.log(count)
      return {[key]: {...acc[key], [count]: cur} }
    }
    return {...acc, [key]: cur}
  } ,{})
}

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters)); 

使用您当前的方法,您必须能够在迭代时检测名称是否已经存在,嵌套在其中的对象是否是普通选民对象(在这种情况下,它必须是删除并替换为 array-like 对象),或者如果它是 array-like 对象。虽然那是 可能 ,但它会很复杂。更好的方法是无条件地分组为按名称索引的数组,然后 然后 将每个数组转换为单个选民对象,或者之后转换为 {0: .. 1: } 对象。

const toNamedObj = (arr) => {
  const votersByName = {};
  for (const voter of voters) {
    votersByName[voter.name] ??= [];
    votersByName[voter.name].push(voter);
  }
  for (const [name, voters] of Object.entries(votersByName)) {
    votersByName[name] = voters.length === 1
      ? voters[0]
      : { ...voters };
  }
  return votersByName;
}

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters));

或者通过映射条目

const toNamedObj = (arr) => {
  const votersByName = {};
  for (const voter of voters) {
    votersByName[voter.name] ??= [];
    votersByName[voter.name].push(voter);
  }
  return Object.fromEntries(
    Object.entries(votersByName)
      .map(([name, voters]) => ([
        name,
        voters.length === 1 ? voters[0] : { ...voters }
      ]))
  );
}

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters));

recommend against 使用 .reduce 当累加器在每次迭代中都是相同的值时 - 普通的 for 循环更易于编写和理解。

下面介绍的是实现所需 objective.

的一种可能方法

代码段

// method to transform arr to desired format
const toNamedObj = arr => (
  arr.reduce(
    (acc, {name, voted, ...rest}, idx) => (
      (acc[name] ??= []).push({ name, voted, ...rest }),
      (idx === arr.length - 1 && Object.entries(acc).forEach(
          ([k, v]) => (acc[k] = (v.length === 1) ? v[0] : {...v})
      )),
      acc
    ),
    {}
  )
);
/* with explanation
// method to transform arr to desired format
const toNamedObj = arr => (
  // iterate using ".reduce()"
  arr.reduce(
    (acc, {name, voted, ...rest}, idx) => {
      // if "name" not present in "acc", set it as empty array
      acc[name] ??= [];
      // push elt into array
      acc[name].push({ name, voted, ...rest });
      // when iterating over the last item, special processing
      if (idx === arr.length - 1) {    // processed last elt in arr
        // review each value in "acc" and convert to object
        Object.entries(acc)
        .forEach(([k, v]) => {
          if (v.length === 1) {       // only one elt in value-array
            // place the elt as-is
            acc[k] = v[0];
          } else {                    // multiple elts in val-arr
            // convert val-arr to object
            acc[k] = {...v};
          }
        });
      };
      // always return the accumulator "acc" in ".reduce()" callback
      return acc;
    },
    {}        // "acc" is set as an empty object initially
  )
);
*/

const voters = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Bob' , age: 31, voted: true},
  {name:'Bob' , age: 32, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Jake' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
console.log(toNamedObj(voters));
.as-console-wrapper { max-height: 100% !important; top: 0 }

说明

在上面的代码段中添加了内联评论。