Related
Let's consider multiple objects with overlapping keys, where each key indicates a week of the year and the values are objects of integer test results, like
const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } };
const cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } };
const xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } };
What is an elegant way to merge them to a single object that contains all weeks as keys and the values as merged-objects, such that:
const merged_tests = {
week1: { a: 4, b: 6, x: 1, y: 1 },
week2: { a: 0, b: 9, c: 2, d: 5 },
week3: { c: 6, d: 7 },
week4: { x: 100, y: 123 },
};
Using Array#reduce, iterate over the objects while updating the final one (accumulator)
In each iteration, using Object#entries and Array#forEach, iterate over the pairs of the current object and update the final one
const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } };
const cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } };
const xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } };
const merged = [ab_tests, cd_tests, xy_tests].reduce((merged, current) => {
Object.entries(current).forEach(([key, value]) => {
merged[key] ??= {};
merged[key] = { ...merged[key], ...value };
});
return merged;
}, {});
console.log(merged);
You could loop through the key of each object and update an output object with the same key
const inputs = [ab_tests, cd_tests, xy_tests],
output = { }
for (const o of inputs) {
for (const key in o)
Object.assign(output[key] ??= {}, o[key])
}
Here's a snippet:
const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } },
cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } },
xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } },
inputs = [ab_tests, cd_tests, xy_tests],
output = {}
for (const o of inputs) {
for (const key in o)
Object.assign(output[key] ??= {}, o[key])
}
console.log(output)
flatMap(Object.entries) flattens things so there's only one loop to read, making it a little more readable in my opinion.
function merge(...tests) {
return tests.flatMap(Object.entries).reduce(
(obj, [k, v]) => Object.assign(obj, {[k]: {...obj[k], ...v}}), {});
}
console.log(merge(ab_tests, cd_tests, xy_tests));
You could reduce the array of objects by grouping with the keys of the outer objects.
const
merge = array => array.reduce((r, o) => Object
.entries(o)
.reduce((t, [k, q]) => {
Object.assign(t[k] ??= {}, q);
return t;
}, r),
{}),
ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } },
cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } },
xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } },
result = merge([ab_tests, cd_tests, xy_tests]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 1 year ago.
I am a beginner in javascript and i have a little problem.
i want to change the structure of array for rendering in React Native using section List,
I got this JSON from Web Api
[
{
title: "Test",
c: 1,
d: 2,
},
{
title: "Test",
c: 3,
d: 4,
},
{
title: "Test",
c: 5,
d: 6,
},
{
title: "Test01",
c: 1,
d: 2,
},
{
title: "Test01",
c: 3,
d: 4,
},
{
title: "Test01",
c: 5,
d: 6,
},
{
title: "Test02",
c: 1,
d: 2,
},
{
title: "Test02",
c: 3,
d: 4,
},
{
title: "Test02",
c: 5,
d: 6,
},
];
And I want to change this JSON like this
[
{
title: "Test",
data: [
{ c: 1, d: 2 },
{ c: 3, d: 4 },
{ c: 5, d: 6 },
],
},
{
title: "Test01",
data: [
{ c: 1, d: 2 },
{ c: 3, d: 4 },
{ c: 5, d: 6 },
],
},
{
title: "Test02",
data: [
{ c: 1, d: 2 },
{ c: 3, d: 4 },
{ c: 5, d: 6 },
],
},
];
It would be simpler to key your data to the test name, but you can achieve what you want by mapping your array like this:
let new_array=[];
your_array.forEach(elem => {
let title = elem.title;
let matchingIndex = newArray.findIndex(a => a.title = title);
if (matchingIndex === -1) {
new_array.push({title}
matchingIndex = new_array.length - 1;
}
let dataColumns = ['c', 'd'];
let data = {};
dataColumns.forEach(col => {
data[col] = elem[col];
});
if (!Array.isArray(new_array[matching_index].data)) {
isArray(new_array[matching_index].data = [];
}
new_array[matching_index].data.push(data);
});
You can perform reduce operation in the array and get the desired format.
const items = [
{
title: "Test",
c: 1,
d: 2,
},
{
title: "Test",
c: 3,
d: 4,
},
{
title: "Test",
c: 5,
d: 6,
},
{
title: "Test01",
c: 1,
d: 2,
},
{
title: "Test01",
c: 3,
d: 4,
},
{
title: "Test01",
c: 5,
d: 6,
},
{
title: "Test02",
c: 1,
d: 2,
},
{
title: "Test02",
c: 3,
d: 4,
},
{
title: "Test02",
c: 5,
d: 6,
},
];
const formatted = items.reduce((carry, current) => {
// generating the placeholder format to put the data
if(!carry.hasOwnProperty(current.title)) {
carry[current.title] = {
title: current.title,
data: []
};
}
// Setting the data in unique name
carry[current.title].data.push({ c: current.c, d: current.d });
return carry;
}, []);
// formatted will have key value pair
console.log(Object.values(formatted));
A reduce function to accumulate the result in an array:
const raw = [{
title: 'Test',
c: 1,
d: 2,
},
{
title: 'Test',
c: 3,
d: 4,
},
{
title: 'Test',
c: 5,
d: 6,
},
{
title: 'Test01',
c: 1,
d: 2,
},
{
title: 'Test01',
c: 3,
d: 4,
},
{
title: 'Test01',
c: 5,
d: 6,
},
{
title: 'Test02',
c: 1,
d: 2,
},
{
title: 'Test02',
c: 3,
d: 4,
},
{
title: 'Test02',
c: 5,
d: 6,
},
];
const result = raw.reduce((acc, {
title,
...data
}) => {
const index = acc.findIndex((elem) => title === elem.title);
if (index === -1) acc.push({
title,
data: [data]
});
else acc[index].data.push(data);
return acc;
}, []);
console.log(result);
const input =[
{
title: "Test",
c: 1,
d: 2,
},
{
title: "Test",
c: 3,
d: 4,
},
{
title: "Test",
c: 5,
d: 6,
},
{
title: "Test01",
c: 1,
d: 2,
},
{
title: "Test01",
c: 3,
d: 4,
},
{
title: "Test01",
c: 5,
d: 6,
},
{
title: "Test02",
c: 1,
d: 2,
},
{
title: "Test02",
c: 3,
d: 4,
},
{
title: "Test02",
c: 5,
d: 6,
},
];
let result = {};
input.forEach((e)=>{
if(!result[e.title]){
result[e.title] = {data:[]};
}
result[e.title]['data'].push({ c:e.c, d:e.d});
});
let restructured =[];
Object.keys(result).forEach((key)=>{
restructured.push({
title: key, data:result[key].data
})
});
console.log(restructured)
var data = [
{
title: "Test",
c: 1,
d: 2,
},
{
title: "Test",
c: 3,
d: 4,
},
{
title: "Test",
c: 5,
d: 6,
},
{
title: "Test01",
c: 1,
d: 2,
},
{
title: "Test01",
c: 3,
d: 4,
},
{
title: "Test01",
c: 5,
d: 6,
},
{
title: "Test02",
c: 1,
d: 2,
},
{
title: "Test02",
c: 3,
d: 4,
},
{
title: "Test02",
c: 5,
d: 6,
},
];
console.log('data',data)
function analyse(data){
var tmp = {}
for(let item of data){
if(tmp[item.title]){
tmp[item.title].data.push({c:item.c,d:item.d})
}else{
tmp[item.title] ={
data: [{c:item.c,d:item.d}]
}
}
}
console.log('tmp',tmp)
var results = []
for(let key in tmp){
results.push({title:key,data:tmp[key].data})
}
console.log('results',JSON.stringify(results))
return results
}
analyse(data)
I know this has been asked before but I can't seem to find the answer, i want to get no_anggota, nama_lahir and tgl_lahir from array isiuser, and i want to insert to ZZZ .
isiseat = [
[{ x: 0, y: 0, val: 1 },
{ x: 1, y: 0, val: 1 },
{ x: 2, y: 0, val: 1 }]
[{ x: 0, y: 1, val: 1 },
{ x: 1, y: 1, val: 1 },
{ x: 2, y: 1, val: 1 }]
[{ x: 0, y: 2, val: 1 },
{ x: 1, y: 2, val: 1 },
{ x: 2, y: 2, val: 1 }]
];
isiuser = [ { no_anggota: '12345', nama_lahir: ' Agus dwi', tgl_lahir: '04-09-2018'},
{ no_anggota: '12345', nama_lahir: 'Septano', tgl_lahir: '15-08-2018'},
{ no_anggota: '12345', nama_lahir: 'Septian putra', tgl_lahir: '15-08-2018'}
];
isiseat.forEach((item1) => {
item1.forEach((item2) => {
var detaildata = {};
detaildata.x = item2.x
detaildata.y = item2.y
detaildata.val = item2.val
detaildata.no_anggota = 'ZZZ'
detaildata.nama_lahir = 'ZZZ'
detaildata.tgl_lahir = 'ZZZ'
detaildata.jadwal = req.body.jadwal
detaildata.section = req.body.section
var savedata = new DetailBooking(detaildata);
savedata.save()
})
});
You can use bulk insert bulk insert
var dataArray = [];
var isiseat = [
[
{ x: 0, y: 0, val: 1 },
{ x: 1, y: 0, val: 1 },
{ x: 2, y: 0, val: 1 }
],
[
{ x: 0, y: 1, val: 1 },
{ x: 1, y: 1, val: 1 },
{ x: 2, y: 1, val: 1 }
],
[
{ x: 0, y: 2, val: 1 },
{ x: 1, y: 2, val: 1 },
{ x: 2, y: 2, val: 1 }
]
]
var bulk = db.items.initializeUnorderedBulkOp();
isiseat.forEach((item1) => {
item1.forEach((item2) => {
var detaildata = {};
detaildata.x = item2.x
detaildata.y = item2.y
detaildata.val = item2.val
detaildata.no_anggota = 'ZZZ'
detaildata.nama_lahir = 'ZZZ'
detaildata.tgl_lahir = 'ZZZ'
detaildata.jadwal = 'sssss'
detaildata.section = 'sssssss'
bulk.insert(detaildata);
})
});
bulk.execute();
my saved data look like this
{
"_id" : ObjectId("5b61aab063727f69dc5ad1b8"),
"x" : 0.0,
"y" : 0.0,
"val" : 1.0,
"no_anggota" : "ZZZ",
"nama_lahir" : "ZZZ",
"tgl_lahir" : "ZZZ",
"jadwal" : "sssss",
"section" : "sssssss"
}
{
"_id" : ObjectId("5b61aab063727f69dc5ad1b9"),
"x" : 1.0,
"y" : 0.0,
"val" : 1.0,
"no_anggota" : "ZZZ",
"nama_lahir" : "ZZZ",
"tgl_lahir" : "ZZZ",
"jadwal" : "sssss",
"section" : "sssssss"
}
If both isiseat and isiuser arrays are of the same length, you can pass the index parameter to the forEach function. (See "Loop through an two arrays simultaneously")
Therefore you could pass the index to your first forEach function:
// Index passed here
isiseat.forEach((item1, index) => {
item1.forEach((item2) => {
var detaildata = {
x: item2.x,
y: item2.y,
val: item2.val,
// ...And then access the `isiuser` array accordingly
no_anggota: isiuser[index].no_anggota,
nama_lahir: isiuser[index].nama_lahir,
tgl_lahir: isiuser[index].tgl_lahir,
jadwal: req.body.jadwal,
section: req.body.section
};
// console.log to see results
console.log(detaildata)
var savedata = new DetailBooking(detaildata);
savedata.save()
})
});
const cond = false
const extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
]
const userInfo = [
{
z: 8
},
{
z: 10
},
...(cond && extraInfo)
]
When cond is true, I want both extra and user info.
When cond is false, only userInfo is needed.
The issue is when cond is false, I get
TypeError: (intermediate value)(intermediate value)(intermediate value)[Symbol.iterator] is not a function
My understanding is that I am not allowed to use a boolean as a spread element, in this case ...false.
But ...( cond ? extraInfo : {} ) doesn't seem to work either.
What is going on?
Just make it
...(cond ? extraInfo : [])
Demo with true
var cond = true;
var extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
];
var userInfo = [
{
z: 8
},
{
z: 10
},
...(cond ? extraInfo : [])
];
console.log( userInfo );
Demo with false
var cond = false;
var extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
];
var userInfo = [
{
z: 8
},
{
z: 10
},
...(cond ? extraInfo : [])
];
console.log( userInfo );
Conditionally spread an entity to Object
console.log(
{
name: 'Alex',
age: 19,
...(true && { city: 'Kyiv' }),
...(false && { country: 'Ukraine' })
}
)
// { name: 'Alex', age: 19, city: 'Kyiv' }
Conditionally spread an entity to Array
console.log(
[
'Dan',
'Alex',
...(true ? ['Robin'] : [])
]
)
// [ 'Dan', 'Alex', 'Robin' ]
const extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
];
const userInfo = [
{
z: 8
},
{
z: 10
},
];
const cond = true;
let getMyValue = cond ? [].concat(extraInfo, userInfo) : userInfo;
console.log(getMyValue)
Another way:
cond is true:
var extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
]
var cond = true;
var userInfo = [
{
z: 8
},
{
z: 10
},
...(cond && extraInfo || [])
]
console.log(userInfo);
cond is false:
var extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
]
var cond = false;
var userInfo = [
{
z: 8
},
{
z: 10
},
...(cond && extraInfo || [])
]
console.log(userInfo);
let getMyValue = cond ? [].concat(extraInfo, userInfo) : userInfo;
let check this
const extraInfo = [
{
a: 11,
b: 25
},
{
a: 12,
b: 34
},
{
a: 1,
c: 99
}
];
const userInfo = [
{
z: 8
},
{
z: 10
},
];
const cond = false;
let getMyValue = cond ? [].concat(extraInfo, userInfo) : userInfo;
console.log(getMyValue)
Following is an object array that has a value v, its input i and output o.
var data = [
{
v: 1,
i: [],
o: [1, 2, 3]
},
{
v: 2,
i: [2],
o: [4, 5, 6]
]
},
{
v: 3,
i: [1, 4],
o: [7, 8]
},
{
v: 4,
i: [],
o: [3]
}
]
The final JSON structure is created by checking the input and outputs of each v, i.e. the parent child relations...
Final JSON structure..
[
{
v: 1,
children: [
{
v: 2
},
{
v: 3
}
]
},
{
v: 4
}
]
I tried by the following code, but it's not transforming the object array properly...
function checkForOutputs(outputs, groupedValueChainEntityLists) {
for (var i = 0; i < outputs.length; i++) {
for (var j = 0; j < groupedValueChainEntityLists[j].inputs.length; j++) {
var val_Chain = groupedValueChainEntityLists[j].inputs.map((item) => {
if (outputs[i].o === item.o) {
return groupedValueChainEntityLists[j];
}
});
return val_Chain;
}
}
}
function constructValueChainRelations(data) {
var valueChainArray = new Array();
var result = data.map((item) => {
if (item.i.length === 0) {
valueChainArray.push(item);
return checkForOutputs(item.o, data);
}
});
console.log(result);
}
I think that you are making this too difficult. Simply map the values.
var data = [{
v: 1,
i: [],
o: [1, 2, 3]
}, {
v: 2,
i: [2],
o: [4, 5, 6]
}, {
v: 3,
i: [1, 4],
o: [7, 8]
}, {
v: 4,
i: [],
o: [3]
}];
function transform(verticies, idProp, childProp) {
return verticies.map(function(vertex) {
return {
v: vertex[idProp],
children: vertex[childProp].filter(function(childVertex) {
return childVertex !== vertex[idProp];
}).map(function(childVertex) {
return {
v: childVertex
};
})
}
});
}
var transformed = transform(data, 'v', 'o');
document.body.innerHTML = '<pre>' + JSON.stringify(transformed, null, 4) + '</pre>';
Result
[{
"v": 1,
"children": [{
"v": 2
}, {
"v": 3
}]
}, {
"v": 2,
"children": [{
"v": 4
}, {
"v": 5
}, {
"v": 6
}]
}, {
"v": 3,
"children": [{
"v": 7
}, {
"v": 8
}]
}, {
"v": 4,
"children": [{
"v": 3
}]
}]
You could use some loops and a look up mechanism with this.
var data = [{ v: 1, i: [], o: [1, 2, 3] }, { v: 2, i: [2], o: [4, 5, 6] }, { v: 3, i: [1, 4], o: [7, 8] }, { v: 4, i: [], o: [3] }],
result = [];
data.forEach(function (a) {
if (!this[a.v]) {
this[a.v] = { v: a.v, children: [] };
result.push(this[a.v]);
}
a.o.forEach(function (b) {
var k = a.v + '|' + b;
if (a.v !== b && !this[k]) {
this[k] = { v: b };
this[a.v].children.push(this[k]);
}
}, this);
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Here's another way that is working..
Sample Code
function populateChildrenRecursively(outputTypeId, valueChainEntities, parentValueChainEntity) {
for (var i = 0; i < valueChainEntities.length; i++) {
if (valueChainEntities[i].valueChainEntity.valueChainEntityId != parentValueChainEntity.valueChainEntity.valueChainEntityId && hasInput(outputTypeId, valueChainEntities[i].inputs)) {
parentValueChainEntity.valueChainEntity.items.push(valueChainEntities[i]);
if (valueChainEntities[i].outputs.length > 0) {
valueChainEntities[i].valueChainEntity.items = [];
for (var j = 0; j < valueChainEntities[i].outputs.length; j++) {
populateChildrenRecursively(valueChainEntities[i].outputs[j].outputTypeId, valueChainEntities, valueChainEntities[i]);
}
}
}
}
}
JSON Conversion