Related
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.
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 reduce and map gives you powerful higher-level constructs that can save you a lot of the pain of looping manually.
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.
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.
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;
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:
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
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);
The above as a reusable function:
const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];
const groupAndMerge = (arr, groupBy, mergeInto) => {
return Array.from(arr.reduce((m, obj) =>
m.set(obj[groupBy], [...(m.get(obj[groupBy]) || []), obj[mergeInto]]), new Map
), ([grouped, merged]) => ({[groupBy]: grouped, [mergeInto]: merged})
);
};
console.log(groupAndMerge(arr, "group", "color"));
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'));
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'));
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 = [
{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>
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; })
}
});
http://jsfiddle.net/dvgwodxq/
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; }
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);
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.
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"}]
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);
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);
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))
}
My approach with a reducer:
myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
console.log(myArray.reduce( (acc, curr) => {
const itemExists = acc.find(item => curr.group === item.group)
if(itemExists){
itemExists.color = [...itemExists.color, curr.color]
}else{
acc.push({group: curr.group, color: [curr.color]})
}
return acc;
}, []))
This gives you unique colors, if you do not want duplicate values for color
var arr = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
var arra = [...new Set(arr.map(x => x.group))]
let reformattedArray = arra.map(obj => {
let rObj = {}
rObj['color'] = [...new Set(arr.map(x => x.group == obj ? x.color:false ))]
.filter(x => x != false)
rObj['group'] = obj
return rObj
})
console.log(reformattedArray)
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
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;
}
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 */
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];
});
};
var myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
console.log(myArray.groupBy('group'));
Credits: #Wahinya Brian
Since the group field is used to group in the reduce step, it creates an object in the form of
{
one: {
color: ["red", "green", "black"],
group: "one"
},
two: {
color: ["blue"],
group: "two"
}
}
So to get the values array in the desired format can use Object.values on the reduce result
let myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
let res = Object.values(myArray.reduce((acc,{group,color}) => {
acc[group] = acc[group] || {group,color:[]}
acc[group].color.push(color)
return acc
},{}))
console.log(res)
/*
//If need to overrite myArray
myArray = Object.values(myArray.reduce((acc,{group,color}......
*/
Using Array's reduce and findIndex methods, this can be achieved.
var myArray = [{
group: "one",
color: "red"
}, {
group: "two",
color: "blue"
}, {
group: "one",
color: "green"
}, {
group: "one",
color: "black"
}];
var transformedArray = myArray.reduce((acc, arr) => {
var index = acc.findIndex(function(element) {
return element.group === arr.group;
});
if (index === -1) {
return acc.push({
group: arr.group,
color: [arr.color]
});
}
acc[index].color.push(arr.color);
return acc;
}, []);
console.log(transformedArray);
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.
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 = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"},
];
let h={};
myArray.forEach(x=> h[x.group]= (h[x.group]||[]).concat(x.color) );
myArray = Object.keys(h).map(k=> ({group:k, color:h[k]}))
console.log(myArray);
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.
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 reduce and map gives you powerful higher-level constructs that can save you a lot of the pain of looping manually.
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.
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.
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;
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:
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
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);
The above as a reusable function:
const arr = [{"group":"one","color":"red"},{"group":"two","color":"blue"},{"group":"one","color":"green"},{"group":"one","color":"black"}];
const groupAndMerge = (arr, groupBy, mergeInto) => {
return Array.from(arr.reduce((m, obj) =>
m.set(obj[groupBy], [...(m.get(obj[groupBy]) || []), obj[mergeInto]]), new Map
), ([grouped, merged]) => ({[groupBy]: grouped, [mergeInto]: merged})
);
};
console.log(groupAndMerge(arr, "group", "color"));
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'));
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'));
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 = [
{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>
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; })
}
});
http://jsfiddle.net/dvgwodxq/
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; }
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);
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.
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"}]
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);
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);
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))
}
My approach with a reducer:
myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
console.log(myArray.reduce( (acc, curr) => {
const itemExists = acc.find(item => curr.group === item.group)
if(itemExists){
itemExists.color = [...itemExists.color, curr.color]
}else{
acc.push({group: curr.group, color: [curr.color]})
}
return acc;
}, []))
This gives you unique colors, if you do not want duplicate values for color
var arr = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
var arra = [...new Set(arr.map(x => x.group))]
let reformattedArray = arra.map(obj => {
let rObj = {}
rObj['color'] = [...new Set(arr.map(x => x.group == obj ? x.color:false ))]
.filter(x => x != false)
rObj['group'] = obj
return rObj
})
console.log(reformattedArray)
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
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;
}
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 */
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];
});
};
var myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
console.log(myArray.groupBy('group'));
Credits: #Wahinya Brian
Since the group field is used to group in the reduce step, it creates an object in the form of
{
one: {
color: ["red", "green", "black"],
group: "one"
},
two: {
color: ["blue"],
group: "two"
}
}
So to get the values array in the desired format can use Object.values on the reduce result
let myArray = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"}
]
let res = Object.values(myArray.reduce((acc,{group,color}) => {
acc[group] = acc[group] || {group,color:[]}
acc[group].color.push(color)
return acc
},{}))
console.log(res)
/*
//If need to overrite myArray
myArray = Object.values(myArray.reduce((acc,{group,color}......
*/
Using Array's reduce and findIndex methods, this can be achieved.
var myArray = [{
group: "one",
color: "red"
}, {
group: "two",
color: "blue"
}, {
group: "one",
color: "green"
}, {
group: "one",
color: "black"
}];
var transformedArray = myArray.reduce((acc, arr) => {
var index = acc.findIndex(function(element) {
return element.group === arr.group;
});
if (index === -1) {
return acc.push({
group: arr.group,
color: [arr.color]
});
}
acc[index].color.push(arr.color);
return acc;
}, []);
console.log(transformedArray);
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.
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 = [
{group: "one", color: "red"},
{group: "two", color: "blue"},
{group: "one", color: "green"},
{group: "one", color: "black"},
];
let h={};
myArray.forEach(x=> h[x.group]= (h[x.group]||[]).concat(x.color) );
myArray = Object.keys(h).map(k=> ({group:k, color:h[k]}))
console.log(myArray);
I got an array of id's. I also have another array of objects. I would like to remove those objects which match with the array of id's. Below is the pseudo code for the same. Can someone help me with the best approch?
const ids = ['1', '2'];
const objs = [
{
id: "1",
name : "one",
},
{
id: "1",
name : "two"
},
{
id: "3",
name : "three",
},
{
id: "4",
name : "four"
},
];
ids.forEach(id => {
const x = objs.filter(obj => obj.id !== id )
console.log('x ==', x);
});
Use filter and includes method
const ids = ["1", "2"];
const objs = [
{
id: "1",
name: "one",
},
{
id: "1",
name: "two",
},
{
id: "3",
name: "three",
},
{
id: "4",
name: "four",
},
];
const res = objs.filter(({ id }) => !ids.includes(id));
console.log(res);
You can put the ids in a Set and use .filter to iterate over the array of objects and .has to check if the id is in this set:
const ids = ['1', '2'];
const objs = [
{ id: "1", name : "one" },
{ id: "1", name : "two" },
{ id: "3", name : "three" },
{ id: "4", name : "four" },
];
const set = new Set(ids);
const arr = objs.filter(obj => !set.has(obj.id));
console.log(arr);
1st requirement -> you have to check for all elements in id array
way to do that using array's built in method is array.includes() or indexof methods
2nd Requirement -> pick out elements not matching with ur 1st requirement which means filter the array.
Combile two
arr = arr.filter(x => !ids.includes(x.id))
Cool es6 destructung syntax
arr = arr.filter(({id}) => !ids.includes(id))
const ids = ['1', '2'];
const objs = [
{
id: "1",
name : "one",
},
{
id: "1",
name : "two"
},
{
id: "3",
name : "three",
},
{
id: "4",
name : "four"
},
];
let arr = objs.filter(function(i){
return ids.indexOf(i.id) === -1;
});
console.log(arr)
I have an Array like this
[
{
name: "A",
job: ["1", "2"]
},
{
name: "B",
job: ["2"]
},
{
name: "C",
job: []
}
]
How do i flatten it into something like this using lodash.
[
{
name: "A",
job: ["1"]
},
{
name: "A",
job: ["2"]
},
{
name: "B",
job: ["2"]
},
{
name: "C",
job: []
}
]
The only solution coming to my mind is to iterate recursively.
Thanks.
You don't need to use recursion if your object isn't multiple levels deep.
Instead, you can achieve this without lodash and recursion. You could use .flatMap to get each job array, and then create an individual object for each item within that job array using .map.
See example below:
const arr = [{name: "A", job: ["1", "2"]}, {name: "B", job: ["2"]}, {name: "C", job: []}];
const spreadArrObj = arr => {
return arr.flatMap(({name, job}) => {
if(job.length < 2) return {name, job}; // if the object is empty or has one one object then return the object
return job.map(elem => ({ // map each element in the job array to its own object
name,
job: [elem]
}));
});
}
console.log(spreadArrObj(arr));
See browser compatibility for .flatMap here.
let arr = [{
name: "A",
job: ["1", "2"]
},
{
name: "B",
job: ["2"]
},
{
name: "C",
job: []
}
];
let newArr = [];
for (i = 0; i < arr.length; i++) {
var job = arr[i].job
if (job.length > 0) {
for (j = 0; j < job.length; j++) {
obj = {};
obj.name = arr[i].name;
obj.job = [job[j]];
newArr.push(obj);
}
} else {
obj = {};
obj.name = arr[i].name;
obj.job = [];
newArr.push(obj);
}
}
console.log(newArr);
You can use array reduce and check length of job array. If it is more than 1 then iterate the job array and create a new object and push it to the accumulator
let data = [{
name: "A",
job: ["1", "2"]
},
{
name: "B",
job: ["2"]
},
{
name: "C",
job: []
}
]
let newData = data.reduce(function(acc, curr) {
if (curr.job.length > 1) {
curr.job.forEach(function(item) {
acc.push({
name: curr.name,
job: [item]
})
})
} else {
acc.push(curr)
}
return acc;
}, []);
console.log(newData)
I have three arrays.
One of them contains values I will be testing. The two others are arrays of object which might include the values of my first array under the name key.
const myArray = ["foo", "bar"];
const testArray1 = [
{name: "foo"},
{name: "bar"},
{name: "something else"}
]
const testArray2 = [
{name: "foo"},
{name: "rab"},
{name: "something else"}
]
I am trying to write a condition which would return true only if the tested array contains all of the values of my first array.
With the same example it would give me something like this :
if (testArray1.containsAll(myArray)) // true
if (testArray2.containsAll(myArray)) // false
What is the best way to resolve this ?
Thanks, any help much appreciated
With array.prototype.every and array.prototype.find, it should be:
const myArray = ["foo", "bar"];
const testArray1 = [
{name: "foo"},
{name: "bar"},
{name: "something else"}
];
const testArray2 = [
{name: "foo"},
{name: "rab"},
{name: "something"}
];
console.log(myArray.every(s => testArray1.find(o => o.name === s)));
console.log(myArray.every(s => testArray2.find(o => o.name === s)));
can be use, every and some. these are return only true/false
const myArray = ["foo", "bar"];
const testArray1 = [
{name: "foo"},
{name: "bar"},
{name: "something else"}
]
const testArray2 = [
{name: "foo"},
{name: "rab"},
{name: "something else"}
]
let result1 = testArray1.every(item => myArray.some(array => item.name == array))
let result2 = testArray2.every(item => myArray.some(array => item.name == array))
console.log('result1', result1)
console.log('result2', result2)
Check this out. May not be the best way but works perfectly fine.
const myArray = ["foo", "bar"];
const testArray1 = [
{name: "foo"},
{name: "bar"},
{name: "something else"}
]
const testArray2 = [
{name: "foo"},
{name: "rab"},
{name: "something else"}
]
let aFlag = testArray1.filter( a => myArray.includes(a.name)).length === myArray.length;
let bFlag = testArray2.filter( a => myArray.includes(a.name)).length === myArray.length;
console.log(aFlag, bFlag)