I have an array of object, which has be combined based on a unique attribute.
For example:
[
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
I want the resultant array of object as the following:
[
{ "macId": "123", "input": 30, "power": 100},
{ "macId": "567", "input": 40 "power": 250}
]
Is this possible with lodash? If not, how can I write a short code for this to do so?
Using lodash I would try groupBy using macId, then using merge to "combine" the objects in the resulting array.
const after =
_(before)
.groupBy('macId')
.map(group => _.merge(...group))
.value();
I would suggest using an ES6 Map in combination with Object.assign:
const arr = [
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
const result = [...arr.reduce((acc, obj) =>
acc.set(obj.macId, Object.assign(acc.get(obj.macId) || {}, obj)), new Map).values()];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do the following, with Object#assign and Array#find :
var myMergedArray = arr.reduce((acc = [], val) => {
if (acc.filter(obj => obj.macId === val.macId).length !== 0) {
Object.assign(acc.find((el) => el.macId === val.macId), val)
} else {
acc.push(val);
}
return acc;
}, [])
Snippet :
var arr = [
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
var myMergedArray = arr.reduce((acc = [], val) => {
if (acc.filter(obj => obj.macId === val.macId).length !== 0) {
Object.assign(acc.find((el) => el.macId === val.macId), val)
} else {
acc.push(val);
}
return acc;
}, [])
console.log(myMergedArray);
Since you have tagged lodash you can easily group it and map it back (merge using reduce or _.merge) easily.
var arr = [
{ "macId": "123", "input": 30},
{ "macId": "567", "input": 40},
{ "macId": "123", "power": 100},
{ "macId": "567", "power": 250}
]
var unique = _.chain(arr).groupBy('macId').map(v => _.merge(...v)).value();
console.log(unique)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Related
I have a JSON array
{
"data": [
{
"id": 659,
"source_id": 1,
"created_at": "2023-01-13T06:35:08.000000Z",
"products": [
{
"name": "532",
"properties": [
{
"name": "color",
"value": "blue"
},
{
"name": "size",
"value": "1"
}
],
}
]
},
{
"id": 658,
"source_id": 2,
"created_at": "2023-01-12T21:36:06.000000Z",
"products": [
{
"name": "532",
"properties": [
{
"name": "color",
"value": "khaki"
},
{
"name": "size",
"value": "2"
}
],
}
]
},
},
],
}
All code I have so far:
var rows = [], sortOrder = ['fabric', 'color', 'size'], orderSource = [{"Insta" : 1, "Retail" : 2}];
dataSet.forEach((e) => {
e.products.forEach((product) => {
product.properties = product.properties.sort((a, b) => {
return sortOrder.indexOf(a.name) - sortOrder.indexOf(b.name);
});
product.properties = sortOrder.map((i => name => product.properties[i].name === name ?
product.properties[i++] : {name, value : ''})(0));
rows.push([e.id, e.source_id, new Date(e.created_at).toLocaleDateString('uk-UK'),
product.name].concat(product.properties.map(p => p.value)).concat([product.quantity,
product.comment]));
console.log(rows);
});
});
Console output looks like this
[ 659, 1, '13.01.2023', '532', 'blue', '1' ],
[ 658, 2, '12.01.2023', '532', 'khaki', '2' ]
I need the data from the array orderSource = [{"Insta" : 1, "Retail" : 2}] to be reassigned to the resulting array so that it looks like this
[ 659, 'Insta', '13.01.2023', '532', 'blue', '1' ],
[ 658, 'Retail', '12.01.2023', '532', 'khaki', '2' ]
This is necessary in order to then write the array to the Google spreadsheet
I am new to programming, so I do not consider it necessary to list here all my attempts to reassign values in the resulting array))
Any of your help is welcome
Use the array method Array.find() on the object's keys to find the relevant key (e.g. Insta, Retail).
const rows = [],
sortOrder = ['fabric', 'color', 'size'],
orderSource = [{
"Insta": 1,
"Retail": 2
}];
dataSet.forEach((e) => {
e.products.forEach((product) => {
product.properties = product.properties.sort((a, b) => {
return sortOrder.indexOf(a.name) - sortOrder.indexOf(b.name);
});
product.properties = sortOrder.map((i => name => product.properties[i].name === name ?
product.properties[i++] : {
name,
value: ''
})(0));
// Use the Array.find() method on the object's keys
const key = Object.keys(orderSource[0]).find(key => orderSource[0][key] === e.source_id);
rows.push([e.id, key, new Date(e.created_at).toLocaleDateString('uk-UK'),
product.name
].concat(product.properties.map(p => p.value)).concat([product.quantity,
product.comment
]));
});
});
console.log(rows);
I have below json array I want to group by it based on sourceno and destno combine key.
Present Code:
let eddarr=[
{
"sourceno": "112",
"destno": "321",
"edno": "123",
"eppno": "322"
},
{
"sourceno": "112",
"destno": "321",
"edno": "123",
"eppno": "324"
},
{
"sourceno": "114",
"destno": "335",
"edno": "100",
"eppno": "355"
},
{
"sourceno": "114",
"destno": "335",
"edno": "222",
"eppno": "999"
}
]
let pobj={}
let p_data=[]
let count=0;
for(key in eddarr)
{
let resultarr=eddarr[key]
let pkey = eddarr[key].sourceno+'-'+eddarr[key].destno;
let obj = {};
let isNew = true;
if(pdata.length > 0){
for(let j=0;j<pdata.length;j++){
if(pdata[j].hasOwnProperty(pkey)){
pdata[j][pkey][pdata[j][pkey].length] = resultarr;
isNew = false;
break;
}
}
}
if(isNew){
obj[pkey] = new Array();
obj[pkey][0] = resultarr;
pdata.push(obj);
}
console.log(pdata)
Above code is working fine and i am getting below result but its running very slow its creating below result for 37K records after 15 min. How can i optimise this code or any other logic need to build. I want to process 1.5 million records but loop is taking too much time.
Final result after processing should be like this:
[
{
"112-321": [
{
"edno": "123",
"eppno": "322"
},
{
"edno": "123",
"eppno": "324"
}
]
},
{
"114-335": [
{
"edno": "100",
"eppno": "355"
},
{
"edno": "222",
"eppno": "999"
}
]
}
]
You could destructure the object to omit keys and build a single object with grouping keys. From this build an array of objects with a sinlge property.
const
data = [{ sourceno: "112", destno: "321", edno: "123", eppno: "322" }, { sourceno: "112", destno: "321", edno: "123", eppno: "324" }, { sourceno: "114", destno: "335", edno: "100", eppno: "355" }, { sourceno: "114", destno: "335", edno: "222", eppno: "999" }],
keys = ['sourceno', 'destno'],
result = Object
.entries(data.reduce((r, o) => {
const key = keys.map(k => {
let v;
({ [k]: v, ...o } = o);
return v;
}).join('-');
(r[key] ??= []).push(o);
return r;
}, {}))
.map(pair => Object.fromEntries([pair]));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const values = {};
eddarr = [{ sourceno: "112", destno: "321", edno: "123", eppno: "322" }, { sourceno: "112", destno: "321", edno: "123", eppno: "324" }, { sourceno: "114", destno: "335", edno: "100", eppno: "355" }, { sourceno: "114", destno: "335", edno: "222", eppno: "999" }];
eddarr.map((entry) => {
var key = entry.sourceno + '-' + entry.destno;
if (!values[ key ]) { values[ key ] = []; }
values[ key ].push({
edno: entry.edno,
eppno: entry.eppno
})
});
console.log(Object.keys(values).map(key => { return { [ key ]: values[key] } }));
[EDIT: Although this solution works, there is a faster way of achieving the same result, traversing the input array only once; please make sure to have a look at it here]
One way of doing it could be using Array.prototype.reduce() (and Array.prototype.find()); something like this:
const input = [{
"sourceno": "112",
"destno": "321",
"edno": "123",
"eppno": "322"
},
{
"sourceno": "112",
"destno": "321",
"edno": "123",
"eppno": "324"
},
{
"sourceno": "114",
"destno": "335",
"edno": "100",
"eppno": "355"
},
{
"sourceno": "114",
"destno": "335",
"edno": "222",
"eppno": "999"
}
];
const output = input.reduce((acc, item) => {
const key = `${item.sourceno}-${item.destno}`; // create the object key
const { edno, eppno } = item; // destructure the attributes to be grouped
const accItem = acc.find(_item => Object.keys(_item)[0] === key); // check whether there is an element with the same object key
if (accItem) {
// in the item is found, update it by adding the attributes to the array
accItem[key].push({ edno, eppno });
} else {
// otherwise create a new item and add it to the accumulator
acc.push({ [key]: [{ edno, eppno }] });
}
return acc;
}, []);
console.log(output)
A faster solution, compared to the one I proposed before, would be using an object to keep track of the items inserted in pdata; in this way you can achieve your goal traversing eddarr only once.
Please notice in the following code snippet I am using an IIFE to avoid "polluting" the global scope with the itemsListObj variable but that is not strictly necessary.
const eddarr = [{
"sourceno": "112",
"destno": "321",
"edno": "123",
"eppno": "322"
},
{
"sourceno": "112",
"destno": "321",
"edno": "123",
"eppno": "324"
},
{
"sourceno": "114",
"destno": "335",
"edno": "100",
"eppno": "355"
},
{
"sourceno": "114",
"destno": "335",
"edno": "222",
"eppno": "999"
}
];
const pdata = (function() {
const itemsListObj = {};
return eddarr.reduce((acc, { sourceno, destno, edno, eppno }) => {
const key = `${sourceno}-${destno}`;
const insertIndex = itemsListObj[key];
if (insertIndex != null) {
acc[insertIndex][key].push({ edno, eppno });
} else {
acc.push({ [key]: [{ edno, eppno }] });
itemsListObj[key] = acc.length - 1;
}
return acc;
}, []);
})()
console.log(pdata);
I am working in small react project & I am facing issue in grouping the data. Requirement is to group the id & its feature into a single row if same id is there in before & after object.
Json Data:
{
"before":{
"device": [
{
id:"1234",
price:"10,
features:[
{name:"samsung",price:"10"},
{name:"Apple",price:"20"}
]
},
{id:"2154",
price:"20,
features:[
{name:"samsung",price:"30"},
{name:"Moto",price:"40"}
]
]
},
"after":{
"device": [
{
id:"1234",
price:"50,
features:[
{name:"samsung",price:"20"},
{name:"Lenovo",price:"30"}
]
},
{id:"2158",
price:"40,
features:[
{name:"samsung",price:"30"}
]
]
}
}
Expected grouping to be shown in UI is shared in image.
I tried to get unique ids in one array and lopping through after array and comparing unique array id I am getting unique id to show but issue i am facing while grouping their related feature.
Can anyone please help me to get a best approach to handle this requirement.
Thanks
There are 3 things i'd suggest you:
1.) Please verify the data your'e posting is correct and in proper format, people won't be able to help if the data is incorrect.
2.) The UI display requirement should be simple enough.
Now, if you still want to achieve this requirement i believe the correct JSON and the merged output json will look something like below:
//Correct input data that you have:
var input = {
"before": {
"device": [
{
"id": "1234",
"price": "10",
"features": [
{
"name": "samsung",
"price": "10"
},
{
"name": "Apple",
"price": "20"
}
]
},
{
"id": "2154",
"price": "20",
"features": [
{
"name": "samsung",
"price": "30"
},
{
"name": "Moto",
"price": "40"
}
]
}
]
},
"after": {
"device": [
{
"id": "1234",
"price": "50",
"features": [
{
"name": "samsung",
"price": "20"
},
{
"name": "Lenovo",
"price": "30"
}
]
},
{
"id": "2158",
"price": "40",
"features": [
{
"name": "samsung",
"price": "30"
}
]
}
]
}
};
// Output JSON which you should need to show the desired output.
var output = {
"devices": [
{
"id": 1234,
"feature": [
{
"name": "1234",
"price": {
"before": 10,
"after": 50
}
},
{
"name": "samsung",
"price": {
"before": 10,
"after": 20
}
},
{
"name": "apple",
"price": {
"before": 10,
"after": 0
}
},
{
"name": "lenovo",
"price": {
"before": 0,
"after": 30
}
}
]
}
]
};
3.) Please try to get the desired output from input yourself as this will help you learn a lot of things in between, as suggested by some please use map, filter, forEach for your requirement.
Hope this helps. Thanks!
You could take a nested approach for grouping.
var data = { before: { device: [{ id: "1234", price: "10", features: [{ name: "samsung", price: "10" }, { name: "Apple", price: "20" }] }, { id: "2154", price: "20", features: [{ name: "samsung", price: "30" }, { name: "Moto", price: "40" }] }] }, after: { device: [{ id: "1234", price: "50", features: [{ name: "samsung", price: "20" }, { name: "Lenovo", price: "30" }] }, { id: "2158", price: "40", features: [{ name: "samsung", price: "30" }] }] } },
cols = Object.fromEntries(Object.keys(data).map(k => [k, 0])),
result = Object.values(Object.entries(data).reduce((r, [col, { device }]) => {
device.forEach(({ id, price, features }) => {
r[id] = r[id] || [{ id, ...cols }];
r[id][0][col] = price;
features.forEach(({ name, price }) => {
let temp = r[id].find(q => q.name === name);
if (!temp) r[id].push(temp = { name, ...cols });
temp[col] = price;
});
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use lodash library for grouping
https://lodash.com/docs/3.10.1#groupBy
Comparing 2 objects, and output equivalent values
var has = {"before":[{
name: 'Nokia',
os: 'Andriod',
features: {
camera: "200 MPixel Camera",
battery: "24 hours battery backup",
}
}],
"after":[{
name: 'Samsung',
os: 'Andriod',
features: {
camera: "200 MPixel Camera",
battery: "30 hours battery backup",
}
}]
};
function compare(Pro1, Pro2) {
var Val1 = Object.values(Pro1);
var Val2 = Object.values(Pro2);
var equivalent = [];
var keys = Object.keys(Pro1);
keys.forEach(k => {
if (Pro1.hasOwnProperty(k) && Pro2.hasOwnProperty(k)) {
if (typeof Pro1[k] === 'object') {
let recursiveResult = compare(Pro1[k], Pro2[k]);
equivalent.push(...recursiveResult);
} else if (Pro1[k] === Pro2[k]) {
equivalent.push(Pro1[k]);
}
}
});
return equivalent;
}
let equiv = compare(has["before"], has["after"]);
console.log(equiv);
I am trying to figure out the most performant Javascript way to convert an array of objects, into an object with unique keys and an array full of objects as the value.
For Example:
const array = [
{ "name": "greg", "year": "2000" },
{ "name": "john", "year": "2002" },
{ "name": "bob", "year": "2005" },
{ "name": "ned", "year": "2000" },
{ "name": "pam", "year": "2000" },
];
I would like this converted to:
{
"2000": [
{ "name": "greg", "year": "2000" },
{ "name": "ned", "year": "2000" },
{ "name": "pam", "year": "2000" }
],
"2002": [ { "name": "john", "year": "2002" } ],
"2005": [ { "name": "bob", "year": "2005" } ],
}
As of now, this is what I've done so far:
let yearsObj = {};
for (let i=0; i<array.length; i++) {
if (!yearsObj[array[i].year]) {
yearsObj[array[i].year] = [];
}
yearsObj[array[i].year].push(array[i]);
}
you can use a more elegant way to do it by using array's reduce function
// # impl
const group = key => array =>
array.reduce(
(objectsByKeyValue, obj) => ({
...objectsByKeyValue,
[obj[key]]: (objectsByKeyValue[obj[key]] || []).concat(obj)
}),
{}
);
// # usage
console.log(
JSON.stringify({
byYear: group(array),
}, null, 1)
);
// output
VM278:1 {
"carsByBrand": {
"2000": [
{
"name": "greg",
"year": "2000"
},
{
"name": "ned",
"year": "2000"
},
{
"name": "pam",
"year": "2000"
}
],
"2002": [
{
"name": "john",
"year": "2002"
}
],
"2005": [
{
"name": "bob",
"year": "2005"
}
]
}
}
It could be as simple as that Object.fromEntries(array.map(obj => [obj.year,obj])) even it is not exactly what you need, but talking about performance it is way slower than all proposed, so i'm giving it as an bad example of showing how the short statement is not always the fastest.
Your way seems to be the fastest taking about performance.
Run the snippet below to see the actual timing.
// common
let array = [
{ "name": "greg", "year": "2000" },
{ "name": "john", "year": "2002" },
{ "name": "bob", "year": "2005" },
{ "name": "ned", "year": "2000" },
{ "name": "pam", "year": "2000" },
];
// simple as a statement way
console.time();
console.log(Object.fromEntries(array.map(obj => [obj.year,obj])));
console.timeEnd();
// using .reduce way
console.time();
const result = array.reduce((prev, curr) => {
const { year } = curr;
if (prev[year]) {
prev[year].push(curr);
} else {
prev[year] = [curr];
}
return prev;
}, {});
console.log(result);
console.timeEnd();
// your way
console.time();
let yearsObj = {};
for (let i=0; i<array.length; i++) {
if (!yearsObj[array[i].year]) {
yearsObj[array[i].year] = [];
}
yearsObj[array[i].year].push(array[i]);
}
console.log(yearsObj);
console.timeEnd();
A for loop (imperative style) like you have is likely to be the fastest in most situations. However, in this case you are not likely to see much of a difference. One thing you could do to improve the code in your example is to get the array length before the for loop and assign it to the variable, so that it's not calculated every iteration of the loop.
const yearsObj = {};
const arrayLength = array.length; // Only calculate array length once
for (let i=0; i<arrayLength; i++) {
if (!yearsObj[array[i].year]) {
yearsObj[array[i].year] = [];
}
yearsObj[array[i].year].push(array[i]);
}
In this situation, my preference would be to use Array.reduce(). It is more readable and the performance difference will be negligible.
const arr = [
{ name: 'greg', year: '2000' },
{ name: 'john', year: '2002' },
{ name: 'bob', year: '2005' },
{ name: 'ned', year: '2000' },
{ name: 'pam', year: '2000' },
];
const result = arr.reduce((prev, curr) => {
const { year } = curr;
if (prev[year]) {
prev[year].push(curr);
} else {
prev[year] = [curr];
}
return prev;
}, {});
/* Result:
{ '2000':
[ { name: 'greg', year: '2000' },
{ name: 'ned', year: '2000' },
{ name: 'pam', year: '2000' } ],
'2002': [ { name: 'john', year: '2002' } ],
'2005': [ { name: 'bob', year: '2005' } ] }
*/
I have two arrays of objects:
1)
[
{
"userId": 9
},
{
"userId": 14
}
]
2)
[{"role": "1", "group": "3"}, {"role": "1", "group": "2"}]
I would like to merge two arrays as follows:
[
{"userId":9,"role":"1","group":"2"},
{"userId":14,"role":"1","group":"2"}
{"userId":9,"role":"1","group":"3"},
{"userId":14,"role":"1","group":"3"}
]
I tried to use let however I couldn't find the way to manipulate switch the subarray :
let arr1 = [{"userId": 9}, {"userId": 14}];
let arr2 = [{"role": "1", "group": "3"}, {"role": "1", "group": "2"}];
let result = arr1.map(o => Object.assign(o, ...arr2));
console.log(result);
return result;
The result I got with the previous implementation is like this :
[{"userId":9,"role":"1","group":"2"},{"userId":14,"role":"1","group":"2"}]
However, I would like to get the result as follows :
[
{"userId":9,"role":"1","group":"2"},
{"userId":14,"role":"1","group":"2"}
{"userId":9,"role":"1","group":"3"},
{"userId":14,"role":"1","group":"3"}
]
var a = [{
"userId": 9
},
{
"userId": 14
}
]
var b = [{
"role": "1",
"group": "3"
}, {
"role": "1",
"group": "2"
}]
console.log(
b.map(z=>a.map(x=>({...x, ...z}))).flat()
)
Another solution using for loop
let arr1 = [{ "userId": 9 }, { "userId": 14 }]
let arr2 = [{"role": "1","group": "3"}, {"role": "1","group": "2" }]
let result = [];
for (let group of arr2) {
for (let user of arr1) [
result.push(Object.assign({}, group, user))
]
}
console.log(JSON.stringify(result))
//output is:
// [
// {"role":"1","group":"3","userId":9},
// {"role":"1","group":"3","userId":14},
// {"role":"1","group":"2","userId":9},
// {"role":"1","group":"2","userId":14}
// ]
Stackblitz example
To achieve expected result, use below option of looping through two arrays
var x = [
{
"userId": 9
},
{
"userId": 14
}
]
var y = [{"role": "1", "group": "3"}, {"role": "1", "group": "2"}]
let result = []
y.forEach((v, i) =>{
x.forEach((y,i) => result.push({...v, ...y}))
})
console.log(result);
codepen - https://codepen.io/nagasai/pen/pxvzOG?editors=1010
You could iterate over the two arrays like this and push the merged values into result:
arr1.forEach(e => {
arr2.forEach(e2 => {
result.push(Object.assign({}, e, e2));
});
});
Which could also be written in ONE LINE:
arr1.forEach(e => arr2.forEach(e2 => result.push(Object.assign({}, e, e2))));
const arr1 = [{
"userId": 9
},
{
"userId": 14
}
];
const arr2 = [{
"role": "1",
"group": "3"
}, {
"role": "1",
"group": "2"
}];
const result = [];
arr1.forEach(e => {
arr2.forEach(e2 => {
result.push(Object.assign({}, e, e2));
});
});
console.log(result);
You can use a mix a reduce and map to boil it down to a single array of objects.
let data = [{
"userId": 9
},
{
"userId": 14
}
]
let metaData = [{
"role": "1",
"group": "3"
}, {
"role": "1",
"group": "2"
}];
let dataReducer = data.reduce((acc, curr) => {
let metadataReducer = metaData.map((val) => {
return {
...curr,
...val
};
}, []);
return [...acc, ...metadataReducer];
}, []);
console.log(dataReducer)
Here a short approach for an arbitrary count of arrays of objects.
var array1 = [{ userId: 9 }, { userId: 14 }],
array2 = [{ role: "1", group: "3" }, { role: "1", group: "2" }],
result = [array1, array2]
.reduce((a, b) =>
a.reduce((r, v) =>
r.concat(b.map(w => Object.assign({}, v, w))), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're basically asking for a Cartesian product of the two arrays. flatMap is a useful function here. For each element of one array, merge it with all objects from the other array, then return a flat result.
const a = [{userId: 9}, {userId: 14}];
const b = [{role: "1", group: "3"}, {role: "1", group: "2"}];
const merged = b.flatMap(ae => a.map(be => ({...be, ...ae})));
console.log(merged);