JavaScript: Update an attribute of object and remove duplicate in array [duplicate] - javascript
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);
Related
How to manipulate a 2 levels array of objects and transform it in another structure in Javascript? [duplicate]
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);
how to create 2 arrays by running once on an array containing objects with the arrays matching the fields?
for example - lets say I have the array - const array = [{name: "first", val: 1}, {name: "second", val: 2}] I want to run once on that array and at the end of that run to have two arrays - const arrayOne = ["first", "second"]; const arrayTwo = [1,2]; to get the first one is easy, but getting both at once? I remember there was a way to do it but couldn't find it.. I'd appreciate any help!
Any looping logic will help Array.reduce implementation will be like below const array = [{ name: "first", val: 1 }, { name: "second", val: 2 }]; const [arrayOne, arrayTwo] = array.reduce((acc, curr) => { const { name, val } = curr; acc[0].push(name); acc[1].push(val); return acc; }, [[], []]); console.log(arrayOne, arrayTwo);
The function extractArrays is general-purpose and can be used in other cases as well. function extractArrays(arr) { const result = {}; for (obj of arr) { for (key in obj) { result[key] = (result[key] || []).concat([obj[key]]); } } return result; } const array = [{name: "first", val: 1}, {name: "second", val: 2}]; const result = extractArrays(array); const arrayOne = result.name; const arrayTwo = result.val; console.log(`arrayOne=${arrayOne}`); console.log(`arrayTwo=${arrayTwo}`);
You can use Array.reduce to achieve this: const array = [{name: "first", val: 1}, {name: "second", val: 2}] const result = array.reduce((res, item) => { res[0].push(item.name) res[1].push(item.val) return res }, [[], []]) console.log(result)
thanks everyone! but I think that the easiest, most readable code would be something like - const itemArray = [], valArray = []; data.map(({name, val})=> { if(name) nameArray.push(name); if(val) valArray.push(val); }) because basically in 4 lines of code it's finished thanks again everyone!
const array = [{name: "first", val: 1}, {name: "second", val: 2}] const keys = []; const values = []; array.forEach(item=>{ keys.push(item.name); values.push(item.val); }) console.log(keys, values)
Use the Array.map function: const array = [ { name: 'first', val: 1 }, { name: 'second', val: 2 } ] let names = array.map(item => item.name) let vals = array.map(item => item.val) console.log(names) console.log(vals) The map function calls a callback function you provide on each element and constructs a new array from the results of that function. If you are not familiar with arrow functions like: item => item.name ... it is a short form for: function (item) { return item.name } You could even do it in one line: let [ names, vals ] = [ array.map(item => item.name), array.map(item => item.val) ]
JavaScript Objects, how to not have duplicate values
Trying to create a function that does not allow an object to crate a new key if the value of the key has already been used. So the object cannot have name = crystal and favoriteRock = crystal Once value has been declared, no other key within the object can have that value. Not sure why I keep getting undefined let keyAdderUniqueVal = function (object, key, value) { let allvalues = Object.values(object) if (!allvalues.includes(value)) { object[key] = value } else { return object } } This is what a working code is suppose to do let horse = { name: 'Spirit', color: 'brown' }; keyAdderUniqueVal(horse, "food", "carrot"); // => {name: "Spirit", color: "brown", food: "carrot"} keyAdderUniqueVal(horse, "hair", "brown"); // => {name: "Spirit", color: "brown", food: "carrot"} console.log(horse); // { name: "Spirit", color: "brown", food: "carrot" }
The problem is that in the first part of your if statement you are adding the key but you are not returning the object, u are only doing that in the else part. Also, consider creating a new object instead of modifying it in place, for example: const keyAdderUniqueVal = (object, key, value) => { const newObject = { ...object } const allvalues = Object.values(newObject) if (!allvalues.includes(value)) newObject[key] = value return newObject } let horse = { name: 'Spirit', color: 'brown' } horse = keyAdderUniqueVal(horse, "food", "carrot"); console.log(horse) >>> { color:brown,food:carrot,name:Spirit } horse = keyAdderUniqueVal(horse, "hair", "brown"); console.log(horse) >>> { color:brown,food:carrot,name:Spirit }
you can try this const keyAdderUniqueVal = (object, key, value) => !Object.values(object).includes(value) && (object[key] = value) let horse = { name: 'Spirit', color: 'brown' }; keyAdderUniqueVal(horse, "food", "carrot"); // => {name: "Spirit", color: "brown", food: "carrot"} keyAdderUniqueVal(horse, "hair", "brown"); // => {name: "Spirit", color: "brown", food: "carrot"} keyAdderUniqueVal(horse, "spirit", "brown"); keyAdderUniqueVal(horse, "some", "brown"); keyAdderUniqueVal(horse, "star", "brown"); console.log(horse); // { name: "Spirit", color: "brown", food: "carrot" }
Javascript - Concat/update arrays of dictionaries based on key-value
I have two arrays of dictionaries, each having a varied number of objects Array 1 [ { id: "1", color: "orange" }, { id: "2", color: "red" }, ] Array 2 [ { id: "1", color: "pink" }, { id: "3", color: "yellow" }, { id: "4", color: "blue" }, ] I want the two to be combined so that objects with new ids from Array2 are added to Array1 (e.g. id=3,4) and objects with existing keys are replaced (e.g. id=1). So the final output would be [ { id: "1", color: "pink" }, { id: "2", color: "red" }, { id: "3", color: "yellow" }, { id: "4", color: "blue" }, ] The only thing I can think of doing is running nested for loops - which is probably the worst way to approach this. What would be the most effective way to achieve this? Thank you.
You can merge those array then filter out the records having duplicate ids and then sort it. I suppose this is what you need: var arr2=[{"id":"1","color":"pink"}, {"id":"3","color":"yellow"},{"id":"4","color":"blue"}]; var arr1=[{"id":"1","color":"orange"}, {"id":"2","color":"red"}]; var result = [...arr2,...arr1].filter((val, i, self)=>self.findIndex(k=>k.id==val.id)==i).sort((a,b)=>a.id-b.id); console.log(result); One approach could be this as well(by using Map): var arr2=[{"id":"1","color":"pink"}, {"id":"3","color":"yellow"},{"id":"4","color":"blue"}]; var arr1=[{"id":"1","color":"orange"}, {"id":"2","color":"red"}]; var result = [...new Map([...arr1,...arr2].map(k=>[k.id,k])).values()]; console.log(result);
concat, reduce, and values var a = [ { id: "1", color: "orange" }, { id: "2", color: "red" }, ] var b = [ { id: "1", color: "pink" }, { id: "3", color: "yellow" }, { id: "4", color: "blue" }, ] var result = Object.values(a.concat(b).reduce((o, item) => (o[item.id] = item, o), {})) console.log(result)
I'd say a simple for..loop is much better for this. var arr1 = [ { id: "1", color: "orange" }, { id: "2", color: "red" }, ] var arr2 = [ { id: "1", color: "pink" }, { id: "3", color: "yellow" }, { id: "4", color: "blue" }, ] var ids = arr2.map(i => i.id); for(let i of arr1){ if(!ids.includes(i.id)){ arr2.push(i) } } console.log(arr2)
Push elements from array, then filter by id and sort by id for(let obj of arr1){ arr2.push(obj); } var newArr = arr2.filter((x, index, self) => index === self.findIndex((t) => ( t.id === x.id))); //Filter by id function compare(a, b) { if ( a.id < b.id ){ return -1; } if ( a.id > b.id ){ return 1; } return 0; } var output = newArr.sort(compare); // Sort by id console.log(output);
Needs to group by multi selected value array
This my array & i needs to group by dept. Here dept is an array so i need to group by its dept value. myArray = [ {name: "one", dept: ["red", "blue"]}, {name: "two", dept: ["green", "blue"]} ] I tried with this code but i can't get the what i need. let deptObj = {}; _.map(myArray , el => { const mySubArray= []; _.filter(el.dept, (depts, i) => { mySubArray.push(el.member); deptObj[depts] = mySubArray; }) } }); I need result like this, anybody can help me? deptObj={ red:[ {name: "one", dept:["red", "blue"]} ], blue:[ {name: "one", dept:["red", "blue"]}, {name: "two", dept:["green", "blue"]} ], green:[ {name: "two", dept:["green", "blue"]} ] }
This solution does what you need: let myArray = [ { name: "one", dept: ["red", "blue"] }, { name: "two", dept: ["green", "blue"] } ]; let deptObj = {}; myArray.forEach(item => { item.dept.forEach(dept => { if (!deptObj[dept]) { deptObj[dept] = [item]; } else { deptObj[dept].push(item); } }); }); console.log(deptObj);
You can use Array.reduce(): var arr = [ {name: "one", dept: ["red", "blue"]}, {name: "two", dept: ["green", "blue"]} ]; var result = arr.reduce((a,curr)=>{ curr.dept.forEach((e)=>{ (a[e] = (a[e] || [])).push(curr)}); return a; },{}); console.log(JSON.parse(JSON.stringify(result)));
Interesting problem. Here is a another solution which is "one liner" and uses .reduce/.map/_.concat: const arr = [{name: "one", dept: ["red", "blue"]},{name: "two", dept: ["green", "blue"]}] var result = arr.reduce((r, {dept}, i, a) => dept.map(x => r[x] = _.concat(r[x] || [], [a[i]])) && r, {}) console.log(result)