【问题标题】:Javascript - Group by data from object arraysJavascript - 按对象数组中的数据分组
【发布时间】:2023-04-05 09:30:02
【问题描述】:

我有如下对象数组:

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

我想要的是按id, name 分组并列出属于下面的rating

const users = [
  {
    id: 1,
    name: "Spider-man",
    rating: {top: 1, middle: 4.5, bottom: 5}
  },
  {
    id: 2,
    name: "Iron man",
    rating: {top: 3, middle: 3.5, bottom: 2}
  },
  {
    id: 3,
    name: "Hulk",
    rating: {top: 5, middle: 1.5, bottom: 4}
  }
];

我尝试过这种方法,但它似乎可以通过更多方式实现,例如.reduce、for...of,更优雅,对吧?

const obj = {
  top: [
    { id: 1, name: "Spider-man", rating: 1 },
    { id: 2, name: "Iron man", rating: 3 },
    { id: 3, name: "Hulk", rating: 5 }
  ],
  middle: [
    { id: 1, name: "Spider-man", rating: 4.5 },
    { id: 2, name: "Iron man", rating: 3.5 },
    { id: 3, name: "Hulk", rating: 1.5 }
  ],
  bottom: [
    { id: 1, name: "Spider-man", rating: 5 },
    { id: 2, name: "Iron man", rating: 2 },
    { id: 3, name: "Hulk", rating: 4 }
  ]
};

var result = obj.top.map(x => ({
  id: x.id,
  name: x.name,
  rating: {
    top: obj.top.find(t => t.id === x.id).rating,
    middle: obj.middle.find(t => t.id === x.id).rating,
    bottom: obj.bottom.find(t => t.id === x.id).rating,
  }
}));
                          
console.log(result);

还有其他方法可以实现吗?提前致谢。

【问题讨论】:

    标签: javascript arrays object ecmascript-6 group-by


    【解决方案1】:

    您需要映射其中一个子数组来查找每个角色的评分,所以我认为您当前的方法非常合理。您可以通过预先制作一组属性(top、middle、bot)来减少重复性,然后遍历它们而不是列出每个不同的属性:

    const obj = {
      top: [
        { id: 1, name: "Spider-man", rating: 1 },
        { id: 2, name: "Iron man", rating: 3 },
        { id: 3, name: "Hulk", rating: 5 }
      ],
      middle: [
        { id: 1, name: "Spider-man", rating: 4.5 },
        { id: 2, name: "Iron man", rating: 3.5 },
        { id: 3, name: "Hulk", rating: 1.5 }
      ],
      bottom: [
        { id: 1, name: "Spider-man", rating: 5 },
        { id: 2, name: "Iron man", rating: 2 },
        { id: 3, name: "Hulk", rating: 4 }
      ]
    };
    
    const props = ['top', 'middle', 'bottom'];
    var result = obj.top.map(x => ({
      id: x.id,
      name: x.name,
      rating: Object.fromEntries(
        props.map(prop =>
          [prop, obj[prop].find(t => t.id === x.id).rating]
        )
      )
    }));      
    console.log(result);

    另一种计算复杂度较低的方法:

    const obj = {
      top: [
        { id: 1, name: "Spider-man", rating: 1 },
        { id: 2, name: "Iron man", rating: 3 },
        { id: 3, name: "Hulk", rating: 5 }
      ],
      middle: [
        { id: 1, name: "Spider-man", rating: 4.5 },
        { id: 2, name: "Iron man", rating: 3.5 },
        { id: 3, name: "Hulk", rating: 1.5 }
      ],
      bottom: [
        { id: 1, name: "Spider-man", rating: 5 },
        { id: 2, name: "Iron man", rating: 2 },
        { id: 3, name: "Hulk", rating: 4 }
      ]
    };
    const byName = {};
    for (const [prop, arr] of Object.entries(obj)) {
      for (const item of arr) {
        byName[item.name] ??= { ...item, rating: {} };
        byName[item.name].rating[prop] = item.rating;
      }
    }
     
    console.log(Object.values(byName));

    【讨论】:

    • 是的,非常喜欢你的方法You can make it a bit less repetitive,+1
    • 第二个解决方案看起来the same to me,但是您的代码更加优雅和简洁,先生。谢谢你的有用。
    • @Phong developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… if (!byName[item.name]) byName[item.name] = { ...item, rating: {} }的快捷方式
    • 还有一个问题:使用dictionary 搜索时的时间复杂度为O(1),第二种解决方案会比第一种解决方案更好。但如您所见,输出仅包含 3 个项目。所以这里的权衡是space & time 复杂性,对吧?
    • @Phong First 代码在时间和空间复杂度方面都更差,因为它必须 .find 并且还必须构造一堆中间数组。但是复杂性是否真的是一个需要担心的问题取决于您真正处理的输入类型。如果只有 3 件,没关系,你可以选择任何“感觉”更好的东西
    【解决方案2】:

    你可以用单行方式做到这一点

    const obj = {
      top: [
        { id: 1, name: "Spider-man", rating: 1 },
        { id: 2, name: "Iron man", rating: 3 },
        { id: 3, name: "Hulk", rating: 5 },
      ],
      middle: [
        { id: 1, name: "Spider-man", rating: 4.5 },
        { id: 2, name: "Iron man", rating: 3.5 },
        { id: 3, name: "Hulk", rating: 1.5 },
      ],
      bottom: [
        { id: 1, name: "Spider-man", rating: 5 },
        { id: 2, name: "Iron man", rating: 2 },
        { id: 3, name: "Hulk", rating: 4 },
      ],
    };
    
    const res = Array.from(
      Object.entries(obj)
        .flatMap(([rater, ratee]) =>
          ratee.map(({ id, name, rating }) => ({
            id,
            name,
            [rater]: rating,
          }))
        )
        .reduce(
          (acc, { id, ...restInfo }) =>
            acc.set(id, { ...(acc.get(id) || {}), ...restInfo }),
          new Map()
        )
    ).map(([id, { name, ...rating }]) => ({ id, name, rating }));
    
    console.log(res);

    【讨论】:

    • 是的,另一个不错的方法是使用.flatMap 和reduce。只是关于复杂性问题的一点点。无论如何,谢谢+1
    【解决方案3】:

    将Dictionary 与Logical nullish assignment (??=) 一起使用

    主要思想包括2个步骤:

    1. 循环对象的所有[key, values]。
    2. 在每个对象的values 内部,我们循环确定user's rating 和key 属于user.id。

    const obj = {
      top: [
        { id: 1, name: "Spider-man", rating: 1 },
        { id: 2, name: "Iron man", rating: 3 },
        { id: 3, name: "Hulk", rating: 5 }
      ],
      middle: [
        { id: 1, name: "Spider-man", rating: 4.5 },
        { id: 2, name: "Iron man", rating: 3.5 },
        { id: 3, name: "Hulk", rating: 1.5 }
      ],
      bottom: [
        { id: 1, name: "Spider-man", rating: 5 },
        { id: 2, name: "Iron man", rating: 2 },
        { id: 3, name: "Hulk", rating: 4 }
      ]
    };
    
    // Refactor code: using Dictionary along with `Logical nullish assignment (??=)` .
    var result = Object.entries(obj).reduce((acc, [key, values]) => {
      values.forEach(v => {
        acc[v.id] ??= {...v, rating: {}};
        acc[v.id].rating[key] = v.rating;
      });
      
      return acc;
    }, {});
    console.log(Object.values(result));
    
    /* Old versions: using Array approach
    
     var result = Object.entries(obj).reduce((acc, [key, values]) => {
      values.forEach(v => {
        var x = acc.find(r => r.id === v.id);
        if(x !== undefined){
          x.rating[key] = v.rating;
        }else{
            x = { id: v.id, name: v.name, rating: {[key]: v.rating} };
            acc.push(x);
          }
      });
      
      return acc;
    }, []);
    */

    更详细的解释:

    逻辑空赋值 (x ??= y) 运算符only assigns if x is nullish (null or undefined)。

    【讨论】:

      【解决方案4】:

      const obj = {
        top: [
          { id: 1, name: "Spider-man", rating: 1 },
          { id: 2, name: "Iron man", rating: 3 },
          { id: 3, name: "Hulk", rating: 5 }
        ],
        middle: [
          { id: 1, name: "Spider-man", rating: 4.5 },
          { id: 2, name: "Iron man", rating: 3.5 },
          { id: 3, name: "Hulk", rating: 1.5 }
        ],
        bottom: [
          { id: 1, name: "Spider-man", rating: 5 },
          { id: 2, name: "Iron man", rating: 2 },
          { id: 3, name: "Hulk", rating: 4 }
        ]
      };
      
      
      const tempArr1 = Object.keys(obj).reduce((arr, key) => {
        obj[key].forEach((item) => {
          arr = [...arr, { ...item, rating: { [key]: item.rating } }];
        });
        return arr;
      }, []);
      
      const result = tempArr1.reduce((arr, el) => {
        let tempObj = { ...el };
        const index = arr.findIndex((tempItem) => tempItem.id === tempObj.id);
        if (~index) {
          arr[index] = {
            ...tempObj,
            rating: {
              ...arr[index].rating,
              ...tempObj.rating
            }
          };
        } else {
          arr = [...arr, tempObj];
        }
        return arr;
      }, []);
      
      console.log(result);

      【讨论】:

        【解决方案5】:

        const obj = {
          top: [
            { id: 1, name: "Spider-man", rating: 1 },
            { id: 2, name: "Iron man", rating: 3 },
            { id: 3, name: "Hulk", rating: 5 }
          ],
          middle: [
            { id: 1, name: "Spider-man", rating: 4.5 },
            { id: 2, name: "Iron man", rating: 3.5 },
            { id: 3, name: "Hulk", rating: 1.5 }
          ],
          bottom: [
            { id: 1, name: "Spider-man", rating: 5 },
            { id: 2, name: "Iron man", rating: 2 },
            { id: 3, name: "Hulk", rating: 4 }
          ]
        };
        
        var result = [];
        for(let [key, values] of Object.entries(obj))
          for(let item of values){
            let x = result.find(r => r.id === item.id);
            if(x !== undefined){
              x.rating[key] = item.rating;
            }else{
                x = { id: item.id, name: item.name, rating: {[key]: item.rating} };
                result.push(x);
              }
          }
        console.log(result);

        【讨论】:

          猜你喜欢
          • 2014-07-05
          • 2021-02-23
          • 1970-01-01
          • 2021-03-29
          • 2017-08-30
          • 1970-01-01
          • 2021-01-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多