在 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
中具有值 kia
或ford
,将其他人聚集在一起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”桶来捕获映射中不特定的所有其他内容的数据。这显然不是您的特定问题所必需的,但可能使它在其他情况下更普遍有用。
函数解释:
创建供内部使用的 Map 对象,以跟踪遇到的组,其中组名是键,该组中的对象数组是条目的值。
遍历对象数组。
获取对象的 属性 感兴趣值。
如果 属性 不存在,尝试使用默认映射
如果 属性 确实存在,请在映射中查找它以获得其分类。
如果没有找到分类,尝试使用默认映射
在我们的临时输出地图中查找分类
如果没有找到,则为该分类创建空数组。
将项目添加到分类数组
完成数组迭代后,将内部 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";
开发者好,我想知道如何将一组具有不同值的对象分组到特定的子组中,在每个子组中,我根据查询的键包含具有特定值的对象。
我的数组应该是这样的
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
中具有值 kia
或ford
,将其他人聚集在一起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”桶来捕获映射中不特定的所有其他内容的数据。这显然不是您的特定问题所必需的,但可能使它在其他情况下更普遍有用。
函数解释:
创建供内部使用的 Map 对象,以跟踪遇到的组,其中组名是键,该组中的对象数组是条目的值。
遍历对象数组。
获取对象的 属性 感兴趣值。
如果 属性 不存在,尝试使用默认映射
如果 属性 确实存在,请在映射中查找它以获得其分类。
如果没有找到分类,尝试使用默认映射
在我们的临时输出地图中查找分类
如果没有找到,则为该分类创建空数组。
将项目添加到分类数组
完成数组迭代后,将内部 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";