【问题标题】:How to group array object by properties?如何按属性对数组对象进行分组?
【发布时间】:2021-11-25 09:04:17
【问题描述】:

我们如何按属性对数组对象进行分组。例如下面我想按oldStockBooks.name 对样本数据进行分组(见底部的预期结果)?

去过以下这些链接,但我无法在我的场景stack-link-1stack-link-2 中应用它。

我已经尝试了下面的这些代码,但它没有按预期工作。

var counter = {};
oldStockBooks.forEach(function(obj) {
    var key = JSON.stringify(obj)
    counter[key] = (counter[key] || 0) + 1
});

样本数据:

const oldStockBooks = [
    {
        name: 'english book',
        author: 'cupello',
        version: 1,
        //... more props here
    },
    {
        name: 'biology book',
        author: 'nagazumi',
        version: 4,
    },
    {
        name: 'english book',
        author: 'cupello',
        version: 2,
    },
];

预期结果:仅显示 nameauthortotal 道具。而total props 将是按书名重复的次数。

const output = [
    {
        name: 'english book',
        author: 'cupello',
        total: 2,
    },
    {
        name: 'biology book',
        author: 'nagazumi',
        total: 1,
    },
];

【问题讨论】:

    标签: javascript


    【解决方案1】:

    您可以在oldStockBooks 上使用reduce 来构建Map 对象。由于您想按name 分组,因此 Map 对象中的键可以是对象中的name 值。在构建您的地图时,如果您遇到地图中已经存在的名称,您可以从存储在该键的对象中获取总数,并创建一个具有更新总数的新对象。否则,如果您还没有看到该对象,您可以将总数设置为 0(由 destructuring 完成,默认为:total = 0)。拥有 Map 后,您可以从中获取值对象并将其转换为带有 Array.from() 的数组:

    const oldStockBooks = [{ name: 'english book', author: 'cupello', version: 1, }, { name: 'biology book', author: 'nagazumi', version: 4, }, { name: 'english book', author: 'cupello', version: 2, }, ];
    
    const res = Array.from(oldStockBooks.reduce((acc, obj) => {
      // Grab name, author and total keys from the seen object. If the object hasn't already been seen, use the current object to grab the name and author, and default the total to 0
      const {name, author, total=0} = acc.get(obj.name) || obj;
      return acc.set(obj.name, {name, author, total: total+1}); // update the total
    }, new Map).values());
    
    console.log(res);

    【讨论】:

      【解决方案2】:

      您可以使用Map 有效地实现结果

      const oldStockBooks = [
        {
          name: "english book",
          author: "cupello",
          version: 1,
        },
        {
          name: "biology book",
          author: "nagazumi",
          version: 4,
        },
        {
          name: "english book",
          author: "cupello",
          version: 2,
        },
      ];
      
      const map = new Map();
      oldStockBooks.forEach(({ name, author }) =>
        map.has(name)
          ? (map.get(name).total += 1)
          : map.set(name, { name, author, total: 1 })
      );
      
      const result = [...map.values()];
      console.log(result);

      【讨论】:

        【解决方案3】:

        你已经接近答案了:

        var response = {}; // here we put the itens mapped by the key (the 'name' field)
        oldStockBooks.forEach(function(obj) {
            if( !response[ obj.name ] ) { // if it is the first item of this 'name'
                response[ obj.name ] = {
                    name: obj.name,
                    author: obj.author,
                    total: 1,
                };
            } else { // else, we have one already, so lets only increment the total count
                response[ obj.name ].total += 1;
            }
        });
        
        // if need a list/array
        var myBooks = [];
        for(var key in response) myBooks.push( response[key] );
        

        【讨论】:

          【解决方案4】:

          注意:这假设您不在乎两本书是否具有相同的标题但作者不同。

          const oldStockBooks = [
              {
                  name: 'english book',
                  author: 'cupello',
                  version: 1,
              },
              {
                  name: 'biology book',
                  author: 'nagazumi',
                  version: 4,
              },
              {
                  name: 'english book',
                  author: 'cupello',
                  version: 2,
              },
          ];
          
          var counter = {}
          oldStockBooks.forEach((obj) => {
              var key = obj.name
              if (!counter[key]) {
                counter[key] = {
                  ...obj,
                  total: 1,
                }
              } else {
                counter[key].total = counter[key].total + 1
              }
          });
          
          const result = Object.keys(counter).map(key => {
            const {name, author, total} = counter[key]
            return {name, author, total}
          })
          
          console.log(result);

          Codepen

          【讨论】:

          • 它应该只有两个数据输出,就像上面的预期输出一样。并且应该只有 3 个道具。
          • 嗯,好的。错过了预期的输出。我已经更新了答案
          【解决方案5】:

          这是一个更简单的解决方案。它循环通过oldStockBooks,将对象推入counter。如果对象已经在计数器中,它只会增加total++

          const oldStockBooks = [
              {
                  name: 'english book',
                  author: 'cupello',
                  version: 1,
                  //... more props here
              },
              {
                  name: 'biology book',
                  author: 'nagazumi',
                  version: 4,
              },
              {
                  name: 'english book',
                  author: 'cupello',
                  version: 2,
              },
          ];
          
          var counter = [];
          oldStockBooks.forEach(function(obj) {
              var foundIncounter = counter.find(el => el.name === obj.name)
              if(!foundIncounter){
                  // first encounter
                  // removing 'version' field 
                  var versioRemoved = {author: obj.author, name: obj.name, total: 1}
                  counter.push(versioRemoved)
              } else {
                  // not first encounter
                  foundIncounter.total++
              }
          });
          
          console.log(counter)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-12-31
            • 1970-01-01
            • 2022-03-17
            • 1970-01-01
            • 1970-01-01
            • 2014-11-28
            • 2022-01-21
            相关资源
            最近更新 更多