Grouping objects inside nested javascript arrays - javascript

I have the following array, which has an object holding nested arrays that in turn have objects of their own:
var list = [{
one: [{key: 1, value: 'eng1'}, {key: 2, value: 'eng2'}],
two: [{key: 1, value: 'esp1'}, {key: 2, value: 'esp2'}]
}];
I want to group the data above by the key property inside the objects of the nested arrays. The following is the desired structure:
var grouped = [
{
key: 1,
group: {
one: {
value: 'eng1'
},
two: {
value: 'esp1'
}
}
},
{
key: 2,
group: {
one: {
value: 'eng2'
},
two: {
value: 'esp2'
}
}
}
]
I have tried so many ways to achieve the above structure but to no avail. I have tried using reduce but that was mainly applicable if the objects were not deeply nested. My problem is the fact that the arrays are nested inside the objects and the key is embedded in those objects. Your help would be greatly appreciated.
The requirement is to not use any library such as underscore or lodash. I have to get this working with plain JS.

You can use multiple forEach loops because of your data structure and add to new array.
var list = [{
one: [{key: 1, value: 'eng1'}, {key: 2, value: 'eng2'}],
two: [{key: 1, value: 'esp1'}, {key: 2, value: 'esp2'}]
}];
var grouped = [];
list.forEach(function(e) {
Object.keys(e).forEach(function(k) {
var that = this;
e[k].forEach(function(a) {
if(!that[a.key]) grouped.push(that[a.key] = {key: a.key, group: {}})
Object.assign(that[a.key].group, {[k]: {value: a.value}})
})
}, {})
})
console.log(grouped)

You may try for this:
var list = [{
one: [{key: 1, value: 'eng1'}, {key: 2, value: 'eng2'}],
two: [{key: 1, value: 'esp1'}, {key: 2, value: 'esp2'}]
}];
var grouped = [];
list.forEach(function(e) {
Object.keys(e).forEach(function(k) {
var that = this;
e[k].forEach(function(a) {
if(!that[a.key]) grouped.push(that[a.key] = {key: a.key, group: {}})
Object.assign(that[a.key].group, {[k]: {value: a.value}})
})
}, {})
})
console.log(grouped)

Related

How to make {value,array[i]} object pairs in javascript

I'm receiving from backend side object which is formulated like:
[
{value: 'FIRST', AvailableValues[{code: "one"},{code: "two"}]},
{value: 'SECOND', AvailableValues[{code: "one"},{code: "two"}]
]
My question is how to map through this object to create pairs like
[{value: "FIRST", code:"one"},
{value: "FIRST", code:"two"},
{value: "SECOND", code:"one"},
{value: "SECOND", code:"two"}]
Thanks
I tried combination of javascript predefined methods like double map, keyed search and so on, but it resulted with errors
use reduce function
Also, your initial data should be an array because if it's a object, then it should contains key with values
const list = [
{value: 'FIRST', AvailableValues:[{code: "one"},{code: "two"}]},
{value: 'SECOND', AvailableValues:[{code: "one"},{code: "two"}]}
]
const result = list.reduce((acc,item) => {
const res = item.AvailableValues.map(i => ({value: item.value, code: i.code}))
return [...acc,...res]
}, [])
console.log(result)
Try something like this, Assuming that you receiving array of object from backend
let result = [];
yourObject.forEach((x)=>{
x.AvailableValues.map((innerItem)=>{
result.push({value:x.value, code : innerItem.code})
});
})
console.log(result)
The other answers here gets the job done. For completion here is an approach using flatMap combined with an inner map
const data =[
{value: 'FIRST', AvailableValues:[{code: "one"},{code: "two"}]},
{value: 'SECOND', AvailableValues:[{code: "one"},{code: "two"}]}
]
const res = data.flatMap(({value,AvailableValues}) => AvailableValues.map(({code}) => ({value,code})))
console.log(res)
var response = [{
value: 'FIRST', AvailableValues: [{ code: "one" }, { code: "two" }]},{
value: 'SECOND', AvailableValues: [{ code: "one" }, { code: "two" }]}
];
var output = [];
response.forEach(function(o) {
o.AvailableValues.forEach(function(oo) {
var t = {
value: o.value,
code: oo.code
};
output.push(t);
})
});
console.log(output);

Immutable.js algorithm: List.update_or_add(item)

I want to concatenate 2 lists in immutable.js.
Both lists have this structure: { id, value }
The algorithm concatenate should do this:
If an ID exists in both list1 and list2 take the value from list2.
let list1 = [
{ id: 1, value: 'foo' },
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
]
let list2 = [
{ id: 1, value: 'quux' }, // id 1 exists in list1
{ id: 4, value: 'asd' },
]
let result = [
{ id: 1, value: 'quux' }, // from list 2
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
{ id: 4, value: 'asd' },
]
If Immutable.js has this functionality with another type (eg. Dictionary), I could also use that.
Algorithms for union
First you have to maintain two map with key as id and value as object then check for length of array which is of bigger size and pass the bigger size array with small size map to merged function there you can iterate over the array and check if it's exists in the map if yes then update the object and delete that row from map otherwise add the object into output. After the for loop complete check if map has element present then push all the values from map into output array and return;
index.js
const old = [
{ id: 1, value: 'foo' },
{ id: 3, value: 'bar' },
{ id: 2, value: 'baz' },
];
const newa = [
{ id: 1, value: 'quux' }, // update
{ id: 4, value: 'asd' }, // push
];
function merged(input,filterMap){
var output = [];
input.forEach(function(eachRow){
if(filterMap.hasOwnProperty(eachRow.id)){
output.push(Object.assign(eachRow,filterMap[eachRow.id]));
delete filterMap[eachRow.id];
}else{
output.push(eachRow);
}
});
if(Object.keys(filterMap).length > 0){
output = output.concat(Object.values(filterMap));
}
return output;
}
function parseData(first,second){
var mapFirst = {},
mapSecond = {};
var output = [];
first.forEach(function(eachRow){
mapFirst[eachRow.id] = eachRow;
});
second.forEach(function(eachRow){
mapSecond[eachRow.id] = eachRow;
});
if(first.length > second.length){
return merged(first,mapSecond);
}else{
return merged(second,mapFirst);
}
}
console.log(parseData(old,newa));
Working jsFiddle demo - https://jsfiddle.net/qz25hnmf/

Grouping elements in javascript

I've created this list by grouping elements from another list (with d3.nest)
array = [ {key: "6S", values: [{Id: "1234a", ECTS: 3},
{Id: "1234b", ECTS: 3}]},
{key: "7S", values: [{Id: "1534a", ECTS: 5},
{Id: "154b", ECTS: 4},]} ]
From this list I want to create something like this:
array = [{key: "6S", values: { 3: [{Id: "1234a"}, {Id: "1234b"}]}},
{key: "7S", values: { 5: [{Id: "1534a"}], 4: [{Id:"1534a"}]}}]
Actually I want to group the data for each key (6S, 7S) by ECTS.
I've tried with _.groupBy.... but is not working. The problem is that the elements that I want to group are objects, already grouped once.
Any idea about how I could group the items?
You can try following
var array = [ {key: "6S", values: [{Id: "1234a", ECTS: 3}, {Id: "1234b", ECTS: 3}]}, {key: "7S", values: [{Id: "1534a", ECTS: 5}, {Id: "154b", ECTS: 4},]} ];
array.forEach((obj) => {
var values = {};
// Iterate over array and create the updated value
obj.values.forEach((item) => {
values[item.ECTS] = values[item.ECTS] || [];
values[item.ECTS].push({"Id" : item.Id});
});
// Set the updated value in object
obj.values = values;
});
console.log(array);
var array = [{
key: "6S",
values: [{
Id: "1234a",
ECTS: 3
},
{
Id: "1234b",
ECTS: 3
}
]
},
{
key: "7S",
values: [{
Id: "1534a",
ECTS: 5
},
{
Id: "154b",
ECTS: 4
},
]
}
]
array = array.map(function(v1) {
var updatedVal = v1.values.reduce(function(obj, v2) {
obj[v2.ECTS] = obj[v2.ECTS] || [];
obj[v2.ECTS].push({
Id: v2.Id
});
return obj;
}, {});
v1.values = updatedVal;
return v1;
});
console.log(array);

Creating new object from another object without persisting changes

I'm using an Array as a container for some basic boilerplate objects which can be copied and added to another Array and then modified. The problem is when I assign the new array ownership of the object any changes persist down to the original object (which shouldn't change).
An example:
var originals = [{ name: "One", value: 1 }, { name: "Two", value: 2 }, { name: "Three", value: 3 }];
var notOriginal = [];
notOriginal.push(originals[0]);
// THIS GIVES ME - notOriginal = [{ name: "One", value: 1 }];
notOriginal[0].name = "Uno";
// THIS RESULTS IN - originals = [{ name: "Uno", value: 1 },...];
I'm trying to keep the "originals" variable the same - it shouldn't change.
I've googled quite a bit and tried some things but not sure where to find a solution.
Specifically this is happening in VueJS whereas the object is in my data()
Make a deep copy of it using JSON.parse & JSON.stringify
var originals = [{
name: "One",
value: 1
}, {
name: "Two",
value: 2
}, {
name: "Three",
value: 3
}];
var notOriginal = [];
notOriginal.push(JSON.parse(JSON.stringify(originals[0])));
notOriginal[0].name = "Uno";
console.log(originals)
You can use Object.assign to make a copy of the object.
var originals = [{
name: "One",
value: 1
}, {
name: "Two",
value: 2
}, {
name: "Three",
value: 3
}];
var notOriginal = [];
notOriginal.push(Object.assign({}, originals[0]));
notOriginal[0].name = "Uno";
console.log(originals);
Objects are passed by reference.
In order to copy an object you can use Object.assign({}, obj) which will return a new object with the duplicated properties within obj.
var originals = [{ name: "One", value: 1 }, { name: "Two", value: 2 }, { name: "Three", value: 3 }];
var notOriginal = originals.map(obj => Object.assign({}, obj));
notOriginal[0].name = "bart";
console.log(originals[0], notOriginal[0]);
I found a solution by using jQuery.extend(true, {}, myObj); but would still like to know what this is called so I can understand this reactivity better.
Thanks

consolidating values in a list of objects based on an attribute which is the same

Bit of background, this comes from a submitted form that I used serializeArray() on
I have a list of objects like so.
[
{name: 0, value: 'waffles'},
{name: 0, value: 'pancakes'},
{name: 0, value: 'french toast'},
{name: 1, value: 'pancakes'}
]
I want to take all things that have the same name attribute and put them together. EG,
[
{name: 0, value: ['waffles', 'pancakes', 'french toast']},
{name: 1, value: ['pancakes']}
]
how would one go about this? All the things I've tried only result in one answer being shown for each name key.
This should do it:
var newlist = _.map(_.groupBy(oldlist, "name"), function(v, n) {
return {name: n, values: _.pluck(v, "value")};
});
Here's the best I could come up with:
http://jsfiddle.net/Z6bdB/
var arr = [
{name: 0, value: 'waffles'},
{name: 0, value: 'pancakes'},
{name: 0, value: 'french toast'},
{name: 1, value: 'pancakes'}
]
var obj1 = {};
$.each(arr, function(idx, item) {
if (obj1[item.name]) {
obj1[item.name].push(item.value);
} else {
obj1[item.name] = [item.value];
}
});
var result = [];
for(var prop in obj1) {
result.push({
name: prop,
value: obj1[prop]
});
}
console.log(result);
This seems to work:
var output = _.chain(input)
.groupBy(function(x){ return x.name; })
.map(function(g, k) { return { name: k, value: _.pluck(g, 'value') }; })
.value();
Demonstration
I am one of those guys that uses native functions:
var food = [
{name: 0, value: 'waffles'},
{name: 0, value: 'pancakes'},
{name: 0, value: 'french toast'},
{name: 1, value: 'pancakes'}
];
var result = food.reduce(function(res,dish){
if (!res.some(function(d){return d.name === dish.name })){
var values = food.filter(function(d){ return d.name === dish.name }).map(function(d){ return d.value; });
res.push({name: dish.name, value : values});
}
return res;
}, []);
console.log(result);
I'd probably do something like this:
function them_into_groups(m, h) {
m[h.name] || (m[h.name] = [ ]);
m[h.name].push(h.value);
return m;
}
function them_back_to_objects(v, k) {
return {
name: +k,
value: v
};
}
var output = _(input).chain()
.reduce(them_into_groups, { })
.map(them_back_to_objects)
.value();
Using _.chain and _.value makes things flow nicely and using named functions make things less messy and clarifies the logic.
Demo: http://jsfiddle.net/ambiguous/G7qwM/2/

Categories