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
.
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.
This question is related to
javascript
I like to use the Map
constructor callback for creating the groups (map keys). The second step is to populate the values of that map, and finally to extract the map's data in the desired output format:
let myArray = [{group: "one", color: "red"},{group: "two", color: "blue"},
{group: "one", color: "green"},{group: "one", color: "black"}];
let map = new Map(myArray.map(({group}) => [group, { group, color: [] }]));
for (let {group, color} of myArray) map.get(group).color.push(color);
let result = [...map.values()];
console.log(result);
_x000D_
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.
myArray = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"}_x000D_
];_x000D_
_x000D_
_x000D_
let group = myArray.map((item)=> item.group ).filter((item, i, ar) => ar.indexOf(item) === i).sort((a, b)=> a - b).map(item=>{_x000D_
let new_list = myArray.filter(itm => itm.group == item).map(itm=>itm.color);_x000D_
return {group:item,color:new_list}_x000D_
});_x000D_
console.log(group);
_x000D_
this repo offers solutions in lodash and alternatives in native Js, you can find how to implement groupby. https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_groupby
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))
}
Using Array's reduce
and findIndex
methods, this can be achieved.
var myArray = [{_x000D_
group: "one",_x000D_
color: "red"_x000D_
}, {_x000D_
group: "two",_x000D_
color: "blue"_x000D_
}, {_x000D_
group: "one",_x000D_
color: "green"_x000D_
}, {_x000D_
group: "one",_x000D_
color: "black"_x000D_
}];_x000D_
_x000D_
var transformedArray = myArray.reduce((acc, arr) => {_x000D_
var index = acc.findIndex(function(element) {_x000D_
return element.group === arr.group;_x000D_
});_x000D_
if (index === -1) {_x000D_
return acc.push({_x000D_
group: arr.group,_x000D_
color: [arr.color]_x000D_
});_x000D_
}_x000D_
_x000D_
acc[index].color.push(arr.color);_x000D_
return acc;_x000D_
}, []);_x000D_
_x000D_
console.log(transformedArray);
_x000D_
By using reduce
function, array is iterator and the new values are stored in acc (accumulating)
parameter. To check if the object with given group
already exists we can use findIndex
function.
If findIndex()
return -1, the value does not exist, so add the array in the acc
parameter.
If findIndex()
return index, then update the index
with the arr
values.
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:
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);
_x000D_
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:
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'));
_x000D_
If you can support optional chaining and the nullish coalescing operator (??), you can simplify the above method to the following:
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'));
_x000D_
Try (h={})
myArray.forEach(x=> h[x.group]= (h[x.group]||[]).concat(x.color) );
myArray = Object.keys(h).map(k=> ({group:k, color:h[k]}))
let myArray = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"},_x000D_
];_x000D_
_x000D_
let h={};_x000D_
_x000D_
myArray.forEach(x=> h[x.group]= (h[x.group]||[]).concat(x.color) );_x000D_
myArray = Object.keys(h).map(k=> ({group:k, color:h[k]}))_x000D_
_x000D_
console.log(myArray);
_x000D_
Use lodash's groupby
method
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
let myArray = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"},_x000D_
]_x000D_
let grouppedArray=_.groupBy(myArray,'group')_x000D_
console.log(grouppedArray)
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
_x000D_
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; })
}
});
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" }],_x000D_
groups = Object.create(null),_x000D_
grouped = [];_x000D_
_x000D_
array.forEach(function (o) {_x000D_
if (!groups[o.group]) {_x000D_
groups[o.group] = [];_x000D_
grouped.push({ group: o.group, color: groups[o.group] });_x000D_
}_x000D_
groups[o.group].push(o.color);_x000D_
});_x000D_
_x000D_
console.log(grouped);
_x000D_
.as-console-wrapper { max-height: 100% !important; top: 0; }
_x000D_
Start by creating a mapping of group names to values. Then transform into your desired format.
var myArray = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"}_x000D_
];_x000D_
_x000D_
var group_to_values = myArray.reduce(function (obj, item) {_x000D_
obj[item.group] = obj[item.group] || [];_x000D_
obj[item.group].push(item.color);_x000D_
return obj;_x000D_
}, {});_x000D_
_x000D_
var groups = Object.keys(group_to_values).map(function (key) {_x000D_
return {group: key, color: group_to_values[key]};_x000D_
});_x000D_
_x000D_
var pre = document.createElement("pre");_x000D_
pre.innerHTML = "groups:\n\n" + JSON.stringify(groups, null, 4);_x000D_
document.body.appendChild(pre);
_x000D_
Using Array instance methods such as reduce and map gives you powerful higher-level constructs that can save you a lot of the pain of looping manually.
You can do something like this:
function convert(items) {
var result = [];
items.forEach(function (element) {
var existingElement = result.filter(function (item) {
return item.group === element.group;
})[0];
if (existingElement) {
existingElement.color.push(element.color);
} else {
element.color = [element.color];
result.push(element);
}
});
return result;
}
This gives you unique colors, if you do not want duplicate values for color
var arr = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"}_x000D_
]_x000D_
_x000D_
var arra = [...new Set(arr.map(x => x.group))]_x000D_
_x000D_
let reformattedArray = arra.map(obj => {_x000D_
let rObj = {}_x000D_
rObj['color'] = [...new Set(arr.map(x => x.group == obj ? x.color:false ))]_x000D_
.filter(x => x != false)_x000D_
rObj['group'] = obj_x000D_
return rObj_x000D_
})_x000D_
console.log(reformattedArray)
_x000D_
My approach with a reducer:
myArray = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"}_x000D_
]_x000D_
_x000D_
console.log(myArray.reduce( (acc, curr) => {_x000D_
const itemExists = acc.find(item => curr.group === item.group)_x000D_
if(itemExists){_x000D_
itemExists.color = [...itemExists.color, curr.color]_x000D_
}else{_x000D_
acc.push({group: curr.group, color: [curr.color]})_x000D_
}_x000D_
return acc;_x000D_
}, []))
_x000D_
Another option is using reduce()
and new Map()
to group the array. Use Spread syntax
to convert set object into an array.
var myArray = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}]_x000D_
_x000D_
var result = [...myArray.reduce((c, {group,color}) => {_x000D_
if (!c.has(group)) c.set(group, {group,color: []});_x000D_
c.get(group).color.push(color);_x000D_
return c;_x000D_
}, new Map()).values()];_x000D_
_x000D_
console.log(result);
_x000D_
You can extend array functionality with the next:
Array.prototype.groupBy = function(prop) {
var result = this.reduce(function (groups, item) {
const val = item[prop];
groups[val] = groups[val] || [];
groups[val].push(item);
return groups;
}, {});
return Object.keys(result).map(function(key) {
return result[key];
});
};
Usage example:
/* re-usable function */_x000D_
Array.prototype.groupBy = function(prop) {_x000D_
var result = this.reduce(function (groups, item) {_x000D_
const val = item[prop];_x000D_
groups[val] = groups[val] || [];_x000D_
groups[val].push(item);_x000D_
return groups;_x000D_
}, {});_x000D_
return Object.keys(result).map(function(key) {_x000D_
return result[key];_x000D_
});_x000D_
};_x000D_
_x000D_
var myArray = [_x000D_
{group: "one", color: "red"},_x000D_
{group: "two", color: "blue"},_x000D_
{group: "one", color: "green"},_x000D_
{group: "one", color: "black"}_x000D_
]_x000D_
_x000D_
console.log(myArray.groupBy('group'));
_x000D_
Credits: @Wahinya Brian
Source: Stackoverflow.com