【问题标题】:Best way of grouping array objects by multiple values in javascript.ES6在 javascript.ES6 中按多个值对数组对象进行分组的最佳方法
【发布时间】:2021-12-27 22:55:11
【问题描述】:

开发人员好日子我想知道如何将一组具有不同值的对象分组到特定的子组中,在每个子组中我根据查询的键包含具有特定值的对象。

我的数组会是这样的

const cars = 
  [ { make: 'audi', model: 'r8',      year: '2012' } 
  , { make: 'audi', model: 'rs5',     year: '2013' } 
  , { make: 'ford', model: 'mustang', year: '2012' } 
  , { make: 'ford', model: 'fusion',  year: '2015' } 
  , { make: 'kia',  model: 'optima',  year: '2012' } 
  ] 

我想通过键 make 在名称 2nd_class 的子组中收集键 make 中具有值 kiaford 的所有对象,并将其他对象聚集在一个组中1rst_class 结果是这样的对象:

const expected = 
  [ '2nd_class': 
    [ { make: 'ford', model: 'mustang', year: '2012' } 
    , { make: 'ford', model: 'fusion',  year: '2015' } 
    , { make: 'kia',  model: 'optima',  year: '2012' } 
    ] 
  , '1rst_class' : 
    [ { make: 'audi', model: 'r8',  year: '2012' } 
    , { make: 'audi', model: 'rs5', year: '2013' } 
    ] 
  ]

网络上的所有示例总是指按键和一个特定值进行分组.... 任何帮助都会很棒。

【问题讨论】:

  • 向我们展示您的尝试。 SO 不是免费的代码编写服务。此处的目的是让您发布解决自己问题的尝试,并在其他人无法按预期工作时提供帮助。见How to Askminimal reproducible example
  • 你绝对不会将它们命名为1rst_class2nd_class
  • 你的expected JS结构无效...

标签: javascript arrays ecmascript-6 javascript-objects


【解决方案1】:

或者你可以简单地这样做:

const cars = [ 
 { make: 'audi', model: 'r8',      year: '2012' }, 
 { make: 'audi', model: 'rs5',     year: '2013' }, 
 { make: 'ford', model: 'mustang', year: '2012' },
 { make: 'ford', model: 'fusion',  year: '2015' },
 { make: 'kia',  model: 'optima',  year: '2012' } 
];

const cars_in_classes=cars.reduce((a,c)=>{
  const cls=(c.make==="audi"?"1st":"2nd")+"_class";
  (a[cls]=a[cls]||[]).push(c);
  return a;}, {} );

console.log(cars_in_classes);

(a[cls]=a[cls]||[]).push(c); 行检查对象属性 a[cls] 是否已经存在,如果不存在,则在将当前元素推入它之前将其创建为一个空数组。

如果您将多个品牌视为“1st_class”,您可以将第 2 行更改为:

const cls=(["audi","mercedes"].indexOf(c.make)>-1?"1st":"2nd")+"_class";

【讨论】:

    【解决方案2】:

    你需要这样做:

    const cars = [
      {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
      }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
      }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
      }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
      }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
      },
    ];
    
    // Used car make
    const usedMake = [];
    // Return object
    const formattedObject = {
      '1st_class': [],
      '2nd_class': []
    };
    
    // Iterate through your car array
    cars.forEach(car => {
      // Check if this is the first time we see this make and process
      if (usedMake.indexOf(car.make) === -1) {
        // Retrieve the cars with the same make as our iterated car
        const filteredCars = cars.filter(c => c.make === car.make);
        
        if (['kia', 'ford'].includes(car.make)) {
          // push in our 2nd class - we push the retrieved objects
          formattedObject['2nd_class'].push(...filteredCars)
        } else {
          // push in our 1st class - we push the retrieved objects
          formattedObject['1st_class'].push(...filteredCars)
        }
        
        // Store the used car make so we don't reuse it later
        usedMake.push(car.make);
      }
    });
    
    console.log(formattedObject)

    迭代检查未使用的值未使用时的处理存储以防止重用的类型是一个基本的编程算法。

    【讨论】:

      【解决方案3】:

      这样:

      const cars = 
        [ { make: 'audi', model: 'r8',      year: '2012' } 
        , { make: 'audi', model: 'rs5',     year: '2013' } 
        , { make: 'ford', model: 'mustang', year: '2012' } 
        , { make: 'ford', model: 'fusion',  year: '2015' } 
        , { make: 'kia',  model: 'optima',  year: '2012' } 
        ] 
      
      const Class2 = [ 'ford', 'kia' ]
      
      const expected = cars.reduce( (r,c) =>
        {
        let cls = Class2.includes(c.make) ? '2nd_class':'1rst_class'
        r[cls].push({...c})
        return r
        } , {'2nd_class':[],'1rst_class':[] }  ) 
      
      console.log( expected )
      .as-console-wrapper {max-height: 100%!important;top:0 }

      【讨论】:

        【解决方案4】:

        我决定尝试创建一个通用分组函数,而不是只为您的特定情况编写一次性解决方案。因为您要尝试分组,而不仅仅是按单个值(实用程序对事物进行分组的常用方式),所以这种类型的分组函数需要更多输入。

        所以,我创建了函数:

        groupBy(arr, propToGroup, mapping)
        

        它需要一组对象进行分组,需要检查这些对象中的属性以进行分组,以及一个映射对象,该对象告诉您该属性的哪些值属于哪个组名称。

        这是你可以在 sn-p 中运行的版本:

        function groupBy(arr, propToGroup, mapping, defaultMapping) {
            let output = new Map();
            for (let item of arr) {
                // get value of our property of interest
                let val = item[propToGroup];
                if (val === undefined) {
                    if (defaultMapping) {
                        val = defaultMapping;
                    } else {
                        throw new Error(`No value for property .${propToGroup} and no defaultMapping`);
                    }
                }
                let classification = mapping.get(val);
                if (!classification) {
                    if (!defaultMapping) {
                        throw new Error(`Property value ${val} is not present in mapping and no defaultMapping`);
                    }
                    classification = defaultMapping;
                }
                let classificationArray = output.get(classification);
                // if classification not found yet, then initialize as empty array
                if (!classificationArray) {
                    classificationArray = [];
                    output.set(classification, classificationArray);
                }
                classificationArray.push(item);
            }
            // convert to output format
            let result = [];
            for (let [key, val] of output) {
                result.push({
                    [key]: val
                });
            }
            return result;
        }
        
        const cars = [
            { make: 'audi', model: 'r8', year: '2012' },
            { make: 'audi', model: 'rs5', year: '2013' },
            { make: 'ford', model: 'mustang', year: '2012' },
            { make: 'ford', model: 'fusion', year: '2015' },
            { make: 'kia', model: 'optima', year: '2012' },
            { make: 'vw', model: 'bug', year: '1960' },
        ];
        
        const mapping = new Map([
            ['audi', '1rst_class'],
            ['ford', '2nd_class'],
            ['kia', '2nd_class']
        ]);
        
        let result = groupBy(cars, "make", mapping, "other");
        console.log(result);

        这个想法是你也可以在其他情况下重用这个groupBy() 函数。如果在映射中找不到给定的属性值并且传递了 defaultMapping,那么它将被放入 defaultMapping 存储桶中。如果没有传递 defaultMapping 并且它不在映射中,则会抛出异常。

        请注意,defaultMapping 添加了许多代码行,但会尝试处理意外数据或数据的情况,您需要一个“catchall”存储桶来捕获映射中未指定的所有其他内容。这显然不是您的特定问题所必需的,但可能会使这对其他情况更普遍有用。


        功能说明:

        1. 创建一个 Map 对象供内部使用,以跟踪遇到的组,其中组名是键,该组中的对象数组是条目的值。

        2. 遍历对象数组。

        3. 获取对象感兴趣的属性值。

        4. 如果该属性不存在,请尝试使用默认映射

        5. 如果该属性确实存在,请在映射中查找以获取其分类。

        6. 如果没有找到分类,尝试使用defaultMapping

        7. 在我们的临时输出 Map 中查找分类

        8. 如果没有找到,则为此分类创建空数组。

        9. 将项目添加到分类数组中

        10. 完成数组迭代后,将内部 Map 对象转换为所需的最终数组结构并返回。

        【讨论】:

          猜你喜欢
          • 2021-08-28
          • 2018-10-06
          • 2022-01-18
          相关资源
          最近更新 更多