在 javascript.ES6 中按多个值对数组对象进行分组的最佳方法

Best way of grouping array objects by multiple values in javascript.ES6

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

我的数组应该是这样的

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' } 
  ] 

我想通过名称 2nd_class 的子组中的键 make 收集键 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' } 
    ] 
  ]

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

你需要做这样的事情:

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)

进程的种类迭代检查未使用的值如果未使用则处理存储以防止重用是一种基本的编程算法。

这样:

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 }

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

所以,我创建了函数:

groupBy(arr, propToGroup, mapping)

它使用要分组的对象数组、这些对象中的 属性 来检查分组和一个映射对象,该映射对象告诉您 属性 的哪些值属于哪个组名称。

这是您可以在代码段中 运行 的版本:

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. 如果没有找到分类,尝试使用默认映射

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

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

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

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

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

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";