Javascript 使用对象对数组项进行分组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31688459/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Group array items using object
提问by Nuno Nogueira
My array is something like this:
我的数组是这样的:
myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
I want to convert this into:
我想将其转换为:
myArray = [
{group: "one", color: ["red", "green", "black"]}
{group: "two", color: ["blue"]}
]
So, basically, group by group
.
所以,基本上,分组group
。
I'm trying:
我想:
for (i in myArray){
var group = myArray[i].group;
//myArray.push(group, {???})
}
I just don't know how to handle the grouping of similar group values.
我只是不知道如何处理相似组值的分组。
采纳答案by neuronaut
First, in JavaScript it's generally not a good idea to iterate over arrays using for ... in
. See Why is using "for...in" with array iteration a bad idea?for details.
首先,在 JavaScript 中,使用for ... in
. 请参阅为什么在数组迭代中使用“for...in”是个坏主意?详情。
So you might try something like this:
所以你可以尝试这样的事情:
var groups = {};
for (var i = 0; i < myArray.length; i++) {
var groupName = myArray[i].group;
if (!groups[groupName]) {
groups[groupName] = [];
}
groups[groupName].push(myArray[i].color);
}
myArray = [];
for (var groupName in groups) {
myArray.push({group: groupName, color: groups[groupName]});
}
Using the intermediary groups
object here helps speed things up because it allows you to avoid nesting loops to search through the arrays. Also, because groups
is an object (rather than an array) iterating over it using for ... in
is appropriate.
在groups
这里使用中间对象有助于加快速度,因为它允许您避免嵌套循环来搜索数组。此外,因为groups
是一个对象(而不是数组)迭代它使用for ... in
是合适的。
Addendum
附录
FWIW, if you want to avoid duplicate color entries in the resulting arrays you could add an if
statement above the line groups[groupName].push(myArray[i].color);
to guard against duplicates. Using jQuery it would look like this;
FWIW,如果您想避免在结果数组中出现重复的颜色条目,您可以if
在该行上方添加一条语句groups[groupName].push(myArray[i].color);
以防止重复。使用 jQuery 它看起来像这样;
if (!$.inArray(myArray[i].color, groups[groupName])) {
groups[groupName].push(myArray[i].color);
}
Without jQuery you may want to add a function that does the same thing as jQuery's inArray
:
如果没有 jQuery,您可能想要添加一个与 jQuery 执行相同操作的函数inArray
:
Array.prototype.contains = function(value) {
for (var i = 0; i < this.length; i++) {
if (this[i] === value)
return true;
}
return false;
}
and then use it like this:
然后像这样使用它:
if (!groups[groupName].contains(myArray[i].color)) {
groups[groupName].push(myArray[i].color);
}
Note that in either case you are going to slow things down a bit due to all the extra iteration, so if you don't need to avoid duplicate color entries in the result arrays I would recommend avoiding this extra code. There
请注意,在任何一种情况下,由于所有额外的迭代,您都会稍微减慢速度,因此如果您不需要避免结果数组中的重复颜色条目,我建议避免使用此额外代码。那里
回答by 1983
Start by creating a mapping of group names to values. Then transform into your desired format.
首先创建组名到值的映射。然后转换成你想要的格式。
var myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
];
var group_to_values = myArray.reduce(function (obj, item) {
obj[item.group] = obj[item.group] || [];
obj[item.group].push(item.color);
return obj;
}, {});
var groups = Object.keys(group_to_values).map(function (key) {
return {group: key, color: group_to_values[key]};
});
var pre = document.createElement("pre");
pre.innerHTML = "groups:\n\n" + JSON.stringify(groups, null, 4);
document.body.appendChild(pre);
Using Array instance methods such as reduceand mapgives you powerful higher-level constructs that can save you a lot of the pain of looping manually.
回答by undefined
One option is:
一种选择是:
var res = myArray.reduce(function(groups, currentValue) {
if ( groups.indexOf(currentValue.group) === -1 ) {
groups.push(currentValue.group);
}
return groups;
}, []).map(function(group) {
return {
group: group,
color: myArray.filter(function(_el) {
return _el.group === group;
}).map(function(_el) { return _el.color; })
}
});
回答by husayt
Use lodash's groupby
method
使用lodash的groupby
方法
Creates an object composed of keys generated from the results of running each element of collection thru iteratee. The order of grouped values is determined by the order they occur in collection. The corresponding value of each key is an array of elements responsible for generating the key. The iteratee is invoked with one argument: (value).
创建一个由通过迭代器运行集合的每个元素的结果生成的键组成的对象。分组值的顺序由它们在集合中出现的顺序决定。每个键对应的值是一个负责生成键的元素数组。迭代器使用一个参数调用:(值)。
So with lodash you can get what you want in a single line. See below
所以使用 lodash 你可以在一行中得到你想要的。见下文
let myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"},
]
let grouppedArray=_.groupBy(myArray,'group')
console.log(grouppedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
回答by Nina Scholz
Beside the given approaches with a two pass approach, you could take a single loop approach by pushing the group if a new group is found.
除了使用两遍方法的给定方法之外,如果找到新组,您可以通过推送该组来采用单循环方法。
var array = [{ group: "one", color: "red" }, { group: "two", color: "blue" }, { group: "one", color: "green" }, { group: "one", color: "black" }],
groups = Object.create(null),
grouped = [];
array.forEach(function (o) {
if (!groups[o.group]) {
groups[o.group] = [];
grouped.push({ group: o.group, color: groups[o.group] });
}
groups[o.group].push(o.color);
});
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
回答by GAURAV
myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
];
let group = myArray.map((item)=> item.group ).filter((item, i, ar) => ar.indexOf(item) === i).sort((a, b)=> a - b).map(item=>{
let new_list = myArray.filter(itm => itm.group == item).map(itm=>itm.color);
return {group:item,color:new_list}
});
console.log(group);
回答by Eddie
Another option is using reduce()
and new Map()
to group the array. Use Spread syntax
to convert set object into an array.
另一种选择是使用reduce()
和new Map()
对数组进行分组。使用Spread syntax
转换集对象到一个数组。
var myArray = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}]
var result = [...myArray.reduce((c, {group,color}) => {
if (!c.has(group)) c.set(group, {group,color: []});
c.get(group).color.push(color);
return c;
}, new Map()).values()];
console.log(result);
回答by Brandon
This version takes advantage that object keys are unique. We process the original array and collect the colors by group in a new object. Then create new objects from that group -> color array map.
此版本利用对象键是唯一的。我们处理原始数组并在新对象中按组收集颜色。然后从该组创建新对象 -> 颜色数组映射。
var myArray = [{
group: "one",
color: "red"
}, {
group: "two",
color: "blue"
}, {
group: "one",
color: "green"
}, {
group: "one",
color: "black"
}];
//new object with keys as group and
//color array as value
var newArray = {};
//iterate through each element of array
myArray.forEach(function(val) {
var curr = newArray[val.group]
//if array key doesnt exist, init with empty array
if (!curr) {
newArray[val.group] = [];
}
//append color to this key
newArray[val.group].push(val.color);
});
//remove elements from previous array
myArray.length = 0;
//replace elements with new objects made of
//key value pairs from our created object
for (var key in newArray) {
myArray.push({
'group': key,
'color': newArray[key]
});
}
Please note that this does not take into account duplicate colors of the same group, so it is possible to have multiple of the same color in the array for a single group.
请注意,这不考虑同一组的重复颜色,因此单个组的数组中可能有多个相同颜色。
回答by om sinha
var array = [{
id: "123",
name: "aaaaaaaa"
}, {
id: "123",
name: "aaaaaaaa"
}, {
id: '456',
name: 'bbbbbbbbbb'
}, {
id: '789',
name: 'ccccccccc'
}, {
id: '789',
name: 'ccccccccc'
}, {
id: '098',
name: 'dddddddddddd'
}];
//if you want to group this array
group(array, key) {
console.log(array);
let finalArray = [];
array.forEach(function(element) {
var newArray = [];
array.forEach(function(element1) {
if (element[key] == element1[key]) {
newArray.push(element)
}
});
if (!(finalArray.some(arrVal => newArray[0][key] == arrVal[0][key]))) {
finalArray.push(newArray);
}
});
return finalArray
}
//and call this function
groupArray(arr, key) {
console.log(this.group(arr, key))
}
回答by Nick Parsons
Using ES6, this can be done quite nicely using .reduce()
with a Map
as the accumulator, and then using Array.from()
with its mapping function to map each grouped map-entry to an object:
使用 ES6,这可以很好地使用.reduce()
aMap
作为累加器,然后使用Array.from()
其映射函数将每个分组的映射条目映射到一个对象:
const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];
const res = Array.from(arr.reduce((m, {group, color}) =>
m.set(group, [...(m.get(group) || []), color]), new Map
), ([group, color]) => ({group, color})
);
console.log(res);
If you have additional properties in your objects other than just group
and color
, you can take a more general approach by setting a grouped object as the map's values like so:
如果您的对象中除了group
and之外还有其他属性color
,您可以通过将分组对象设置为地图的值来采用更通用的方法,如下所示:
const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];
const groupAndMerge = (arr, groupBy, mergeInto) =>
Array.from(arr.reduce((m, o) => {
const curr = m.get(o[groupBy]);
return m.set(o[groupBy], {...o, [mergeInto]: [...(curr && curr[mergeInto] || []), o[mergeInto]]});
}, new Map).values());
console.log(groupAndMerge(arr, 'group', 'color'));
If you can support optional chaining, you can simplify the above method to the following:
如果可以支持optional chaining,则可以将上述方法简化为以下内容:
const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];
const groupAndMerge = (arr, groupBy, mergeWith) =>
Array.from(arr.reduce((m, o) => m.set(o[groupBy], {...o, [mergeWith]: [...(m.get(o[groupBy])?.[mergeWith] || []), o[mergeWith]]}), new Map).values());
console.log(groupAndMerge(arr, 'group', 'color'));