Group and merge similar javascript objects - javascript

I have the following data and I am stuck between a logic
[
{
"user.employeeId": "10081",
"objectives": [
"Improve consultation"
],
"param": "dueByDays"
},
{
"user.employeeId": "10081",
"objectives": [
"Building a strong team"
],
"param": "overdue"
},
]
How can I get the data in the below format, I have tried Map(), ForEach(), etc. but I am getting stuck,
This is the requirement/Output
[
{
'user.employeeId': '10081',
params: [
{
objectives: ['Improve consultation'],
param: 'dueByDays'
},
{
objectives: ['Building a strong team'],
param: 'overdue'
},
{
objectives: null,
param: 'dueToday'
}
]
}];
If suppose param is not available, we still need to set objective as null, please help me out regarding this query. It would be better if anyone can help me do this using lodash
There will be only 3 params as mentioned above

The approach here would be something like this:
create some "storage" object (can be a simple object);
iterate through items;
if this is a new employee, then create new record in the "storage";
if this is a known employee, just update its info.
After this, the storage object has to be converted to an array, as per requirement:
const userEntries = /* initial array */;
const tempStorage = Object.create(null); // or just '{}'
for (const entry of userEntries) {
const id = entry["user.employeeId"];
const param = {
objectives: entry.objectives,
param: entry.param,
};
if (id in tempStorage === false) { // this is a new employee
tempStorage[id] = [ param ];
} else { // this is a known employee, it exists in storage
tempStorage[id].push(param);
}
}
// All the necessary data is in tempStorage.
// Now on to converting.
const output = [];
for (const employeeId in tempStorage) {
output.push({
["user.employeeId"]: employeeId,
params: tempStorage[employeeId],
});
}

Something like this should work:
const employeesData = {};
for (let item of array) {
const id = item["user.employeeId"];
if (!employeesData[id]) {
employeesData[id] = {};
}
employeesData[id][item.param] = item.objectives;
}
const params = ['dueByDays', 'overdue', 'dueToday'];
const result = [];
for (let key of Object.keys(employeesData)) {
result.push({
'user.employeeId': key,
params: params.map(param => ({
param,
objectives: employeesData[key][param] || null,
})),
});
}
We first build an object containing all the data then we iterate through the keys to build a proper array.

I don't test it but it should work.
const usersHash = users.reduce((acc, curr) => {
if(acc[curr["user.employeeId"]])
acc[curr["user.employeeId"]].params.push(curr.param ? {...curr.param} : {})
else
acc[curr["user.employeeId"]] = { "user.employeeId": curr["user.employeeId"], params: [{...curr.param}] }
return acc;
},{})
const mergedUsers = Object.values(usersHash);

You can use .reduce() method. The following code should work.
const arr = [
{
"user.employeeId": "10081",
"objectives": [
"Improve consultation"
],
"param": "dueByDays"
},
{
"user.employeeId": "10081",
"objectives": [
"Building a strong team"
],
"param": "overdue"
},
{
"user.employeeId": "10081",
"objectives": [
"A team"
],
},
]
const result = arr.reduce((accumulator, currentValue) => {
const element = accumulator.find(item => item['user.employeeId'] === currentValue['user.employeeId'])
if (element) {
element.params.push({objectives: currentValue.objectives, param: currentValue.param || null})
} else {
accumulator.push({'user.employeeId': currentValue['user.employeeId'], params: [{objectives: currentValue.objectives, param: currentValue.param || null}]})
}
return accumulator
},[])
console.log(result)

Related

Facing issue with Javascript array

I am getting an array of object data from service as below:
outputs
array= [
{id:1,Name:GHI,Gender:Male,DOB:12/1/2020},
{id:1,Name:GHI,Gender:Female,DOB:10/1/2020},
{id:2,Name:ABC,Gender:Male,DOB:02/02/2020},
{id:2,Name:ABC,Gender:Female,DOB:03/04/2019},
{id:3,Name:EFG,Gender:Male,DOB:09/09/2010},
{id:3,Name:EFG,Gender:Female,DOB:08/07/2021}
]
I have applied group by function and mapping by using the key
let key="id";
const groupBy=(array,key)=>{
return Object.entries(array.reduce((result,currentvalue)=>{
(result[currentValues[key]]=result[currentValue[key]]||[]).push(
currentvalue
);
return result;
},{})).map(v=>({[key]:v[0],data:v[1]})));
};
I am able to get data like this
[
{
Id:1,
data:
[
{id:1,Name:GHI,Gender:Male,DOB:12/1/2020},
{id:1,Name:GHI,Gender:Female,DOB:10/1/2020}
]
},
{
Id:2,
data:
[
{id:2,Name:ABC,Gender:Male,DOB:02/02/2020},
{id:2,Name:ABC,Gender:Female,DOB:03/04/3019}
]
}...
]
But I need the out put some thing like this I need to group by Id but need the Name of Id something like this
[
{
Name:GHI,
data:[
{id:1,Name:GHI,Gender:Male,DOB:12/1/2020},
{id:1,Name:GHI,Gender:Female,DOB:10/01/2020}
]
},
{
Name:ABC,
data:[
{id:2,Name:ABC,Gender:Male,DOB:02/02/2020},
{id:2,Name:ABC,Gender:Female,DOB:03/04/2019}
]
},
{
Name:EFG,
data:[
{id:3,Name:EFG,Gender:Male,DOB:09/09/2010},
{id:3,Name:EFG,Gender:Male,DOB:08/07/2021}
]
}
]
Please let me know how to retrieve data in expected format to display on UI.
try with the below code.
var arr = [
{id:1,Name:"GHI",Gender:"Male",DOB:"12/1/2020"},
{id:1,Name:"GHI",Gender:"Female",DOB:"10/1/2020"},
{id:2,Name:"ABC",Gender:"Male",DOB:"02/02/2020"},
{id:2,Name:"ABC",Gender:"Female",DOB:"03/04/2019"},
{id:3,Name:"EFG",Gender:"Male",DOB:"09/09/2010"},
{id:3,Name:"EFG",Gender:"Female",DOB:"08/07/2021"}
]
let temp = {}
arr.forEach((obj) => {
temp[obj.Name] = temp[obj.Name] || [];
temp[obj.Name].push(obj);
});
let output = Object.entries(temp).map(obj => {
return {"Name": obj[0], "data": obj[1]}
});
console.log(JSON.stringify(output, null, 2));
You may go simpler and more readable:
let result = {};
for (const data of array) {
const { Name } = data;
if (!result[Name]) {
result[Name] = { Name, data: [data] };
} else {
result[Name].data.push(data);
}
}

How to filter array and create new array with specific object keys

I have an array with objects and in each object there is an "items" array. My goal is to combine these "items" into one array.
Goal / Expected Output
[
{ id: 'SUV' },
{ id: 'Compact' },
{ id: 'Gasoline' },
{ id: 'Hybrid' }
]
Sample Array
[
{
"id":"carType",
"items":[
{
"id":"SUV"
},
{
"id":"Compact"
}
]
},
{
"id":"fuelType",
"items":[
{
"id":"Gasoline"
},
{
"id":"Hybrid"
}
]
}
]
You could use Array#flatMap.
const data = [{"id":"carType","items":[{"id":"SUV"},{"id":"Compact"}]},{"id":"fuelType","items":[{"id":"Gasoline"},{"id":"Hybrid"}]}];
const r = data.flatMap(({ items }) => items);
console.log(r);
A one-liner in vanilla JS (earlier than EMCAScript 2019, flatMap is not available, so in case of that...)
[].concat(...arr.map(elt => elt.items))
Something like this?
newArr = []
for (let i = 0;i<arr.length;i++) {
for (let ii = 0;ii<arr[i].items.length;ii++) {
newArr.push(arr[i].items[ii].id)
}
}
console.log(newArr)

Merge items from two arrays based on matching ID

I have a data object like this :
{
"data1": [
[
"ID",
"name",
"Birthday"
],
[
"10",
"thomas",
"1992-03-17"
],
[
"11",
"Emily",
"2000-03-03"
]
],
"data2": [
[
"Balance",
"ID"
],
[
"$4500",
"10"
],
[
"$1500",
"13"
]
]
}
It contains two arrays data1 and data2.
The first row in each array is the name of the columns and the rest of the rows have the data (think of it like a table).
I want to compare the ID field in both arrays and if the IDs match then the final output will contain a column Balance with the balance corresponding to that ID and if the IDs don't match then the Balance will be $0.
Expected output:
{
"output": [
[
"ID",
"name",
"Birthday",
"Balance"
],
[
"10",
"thomas",
"1992-03-17",
"$4500" //ID 10 matched so the balance added here
],
[
"11",
"Emily",
"2000-03-03",
"0" //0 bcoz the ID 11 is not there in data2 array
]
]
}
I find this challenging to accomplish. Think of it like a LEFT-JOIN in MySQL.
I referred to this solution but it doesn't work in my case as I don't have the keys in my response.
EDIT: I also need to join the other fields as well.
You can use Array.prototype.map(), find, filter, slice, reduce, concat, includes and Object.assign().
This solution:
Handles arbitrary ordering of the items. The order is read from the headers.
Appends a Balance field only if there is one present in data2.
Joins all other fields (requested by OP, see comments below).
Takes default values as an input and uses them if the data is not present in data1 and data2.
function merge({ data1, data2 }, defaults) {
// get the final headers, add/move 'Balance' to the end
const headers = [...data1[0].filter(x => x !== 'Balance')]
.concat(data2[0].includes('Balance') ? ['Balance'] : []);
// map the data from data1 to an array of objects, each key is the header name, also merge the default values.
const d1 = data1.slice(1)
.map(x => x.reduce((acc, y, i) => ({ ...defaults, ...acc, [data1[0][i]]: y }), {}));
// map the data from data2 to an array of objects, each key is the header name
const d2 = data2.slice(1)
.map(x => x.reduce((acc, y, i) => ({ ...acc, [data2[0][i]]: y }), {}));
// combine d1 and d2
const output = d1.map((x, i) => { // iterate over d1
// merge values from d2 into this value
const d = Object.assign(x, d2.find(y => y['ID'] === x['ID']));
// return an array ordered according to the header
return headers.map(h => d[h]);
});
return { output: [headers, ...output] };
}
const test0 = {
data1: [[ "ID","name","Birthday","other"],["10","thomas","1992-03-17","empty"],["11","Emily","2000-03-03","empty"]],
data2: [["other", "ID", "Balance", "city"],["hello", "10", "$4500", "New York"],["world", "10","$8","Brazil"]]
};
const test1 = {
data1: [["ID","name","Birthday"],["10","thomas","1992-03-17"],["11","Emily","2000-03-03"]],
data2: [["other","ID"],["x","10"],["y","11"]]
};
console.log(merge(test0, { Balance: '$0' }));
console.log(merge(test1, { Balance: '$0' }));
const KEY_ID = "ID";
var data = {
"data1": [
[ "ID", "name", "Birthday" ],
[ "10", "thomas", "1992-03-17" ],
[ "11", "Emily", "2000-03-03" ]
],
"data2": [
[ "Balance", "ID" ],
[ "$4500", "10" ],
[ "$1500", "13" ]
]
}
var merged = Object.keys(data).map(function (key) {
var tmp = data[key].slice();
var heads = tmp.shift();
return tmp.map(function (item) {
var row = {};
heads.forEach(function (head, i) {
row[head] = item[i];
});
return row;
});
}).flat().reduce(function (acc, row) {
var found = acc.find(function (item) {
return row[KEY_ID] === item[KEY_ID];
})
if (!found) {
found = row;
acc.push(found);
} else {
Object.keys(row).forEach(function (head) {
found[head] = row[head];
});
}
return acc;
}, []);
console.log(merged);
This solution is scalable: if you add properties, it will scale the new format.
let a = { "data1": [ ... ],"data2": [ ...] }
let r = a.data1.reduce((r,u,i)=>{
if(i !== 0)
{
let entry = a.data2.filter((a)=> a[1]===u[0])
r.push([...u,entry.length ? entry[0][0] : 0])
}
return r
},[[
"ID",
"name",
"Birthday",
"Balance"
]])
You could abstract all table operations into a class-like:
function Table(array) {
const [head, ...values] = array;
const Entry =(entry) => ({
get(key) { return entry[ head.indexOf(key) ]; },
set(key, value) { entry[ head.indexOf(key) ] = value; }
});
return {
index(name) {
const result = {};
for(const value of values)
result[ value[ head.indexOf(name) ] ] = Entry(value);
return result;
},
*[Symbol.iterator]() {
for(const row of values)
yield Entry(row);
},
addRow(key) { head.push(key); }
};
}
Usable as:
const users = Table(obj.data1);
const balances = Table(obj.data2);
const balanceByID = balance.index("ID");
users.addRow("Balance");
for(const user of users)
user.set("Balance", balanceByID[ user.get("ID") ].get("Balance"));

read array in javascript

I am new in javascript and using nodejs and mongoose query to get a result which I would like to loop through and get some values in another array.
The resultset looks like
[ { _id: 5366e9049cfc825b32966852,
companies:
[ 5ab20bb69cb2754e949a09dc,
5ac5d53983d45353bc6a9c54,
5ac62eeca5421e4cb9abf63e]
},
{ _id: 5b9251f8ae8db624755f4b90,
companies:
[ 5b461c892bb9c81bd3ed4a25,
5b5086196947782fbc873d28,
5b76a6c79dc71a4a12564cc5 ]
}]
The final array should look like --
[ 5ab20bb69cb2754e949a09dc,
5ac5d53983d45353bc6a9c54,
5ac62eeca5421e4cb9abf63e,
5b461c892bb9c81bd3ed4a25,
5b5086196947782fbc873d28,
5b76a6c79dc71a4a12564cc5]
My code--
Model.find().exec(function(err,gdoc){
if(err)
{
callback({err:err,message:"Error looking up company"});
}else
{
for (var i = 0, len = gdoc.length; i < len; i++) {
console.log(gdoc.companies);
}
}
});
I am getting undefined values.
Any help is highly appreciated. Thanks in advance.
The reason why you are getting undefined is that you try to access the non-existing companies property on gdoc. Instead of gdoc.companies, you have to use gdoc[i].companies.
Instead of your loop, you can use Array.prototype.concat together with the spread syntax (...) and Array.prototype.map as follows:
const gdoc = [
{
_id: '5366e9049cfc825b32966852',
companies: [
'5ab20bb69cb2754e949a09dc',
'5ac5d53983d45353bc6a9c54',
'5ac62eeca5421e4cb9abf63e',
],
},
{
_id: '5b9251f8ae8db624755f4b90',
companies: [
'5b461c892bb9c81bd3ed4a25',
'5b5086196947782fbc873d28',
'5b76a6c79dc71a4a12564cc5',
],
},
];
const companies = [].concat(...gdoc.map(doc => doc.companies));
console.log(companies);
Basically you're trying to concatenate all the company fields of the results array. As long as you know that the key is companies, this is an easy reduction.
Arrays have a couple of builtin methods that make transforming them very easy, compared to having to use a for-loop.
Array.forEach(), Array.map(), Array.reduce() and Array.find() are the most frequently used ones.
// Since these id's contain letters as well, they need to be strings.
const data = [
{
_id: "5366e9049cfc825b32966852",
companies:[
"5ab20bb69cb2754e949a09dc",
"5ac5d53983d45353bc6a9c54",
"5ac62eeca5421e4cb9abf63e"
]
},
{
_id: "5b9251f8ae8db624755f4b90",
companies:[
"5b461c892bb9c81bd3ed4a25",
"5b5086196947782fbc873d28",
"5b76a6c79dc71a4a12564cc5"
]
}
];
const companies = data.reduce(( result, item ) => result.concat( item.companies ), []);
console.log( companies );
You can use the following method:
const map = [...[].concat(...arr.map((o) => o.companies))]
var arr = [ { _id: "5366e9049cfc825b32966852",
companies:
[ "5ab20bb69cb2754e949a09dc",
"5ac5d53983d45353bc6a9c54",
"5ac62eeca5421e4cb9abf63e"]
},
{ _id: "5b9251f8ae8db624755f4b90",
companies:
[ "5b461c892bb9c81bd3ed4a25",
"5b5086196947782fbc873d28",
"5b76a6c79dc71a4a12564cc5" ]
}];
const map = [...[].concat(...arr.map((o) => o.companies))]
console.log(map);
How about running a reduce over the array.
const model = [{
_id: "5366e9049cfc825b32966852",
companies: [
"5ab20bb69cb2754e949a09dc",
"5ac5d53983d45353bc6a9c54",
"5ac62eeca5421e4cb9abf63e"
]
},
{
_id: "5b9251f8ae8db624755f4b90",
companies: [
"5b461c892bb9c81bd3ed4a25",
"5b5086196947782fbc873d28",
"5b76a6c79dc71a4a12564cc5"
]
}
];
const reducer = (acc, value) => {
acc.push(...value.companies);
return acc;
};
console.log(model.reduce(reducer, []));
You can do some thing like that!
var docs= [ { _id: "5366e9049cfc825b32966852",
companies:
[ "5ab20bb69cb2754e949a09dc",
"5ac5d53983d45353bc6a9c54",
"5ac62eeca5421e4cb9abf63e"]
},
{ _id: "5b9251f8ae8db624755f4b90",
companies:
[ "5b461c892bb9c81bd3ed4a25",
"5b5086196947782fbc873d28",
"5b76a6c79dc71a4a12564cc5" ]
}];
// your companies will be put in the folowing variable
var companies = [];
for ( var doc of docs){
for (var item of doc.companies){
companies.push(item)
}
}
console.log(companies)
const data = [ { _id: '5366e9049cfc825b32966852',
companies:
[ '5ab20bb69cb2754e949a09dc',
'5ac5d53983d45353bc6a9c54',
'5ac62eeca5421e4cb9abf63e']
},
{ _id: '5b9251f8ae8db624755f4b90',
companies:
[ '5b461c892bb9c81bd3ed4a25',
'5b5086196947782fbc873d28',
'5b76a6c79dc71a4a12564cc5' ]
}]
let outputArr = []
data.map(d => outputArr.push(d.companies));
//Flatten
console.log([].concat.apply([],outputArr))
Both flatMap and flat which are currently an 'experimental technology' will allow a cleaner approach to this problem in the future.
See:
Array.prototype.flat(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
Array.prototype.flatMap(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap
You want something like this:
var k = [{
_id: "5366e9049cfc825b32966852",
companies: ["5ab20bb69cb2754e949a09dc",
"5ac5d53983d45353bc6a9c54",
"5ac62eeca5421e4cb9abf63e"
]
},
{
_id: "5b9251f8ae8db624755f4b90",
companies: ["5b461c892bb9c81bd3ed4a25",
"5b5086196947782fbc873d28",
"5b76a6c79dc71a4a12564cc5"
]
}
];
var res = [];
k.forEach(function(id) {
res.push(...id.companies)
});
console.log(res);

Destructuring array of objects in es6

In es6, how can i simplify the following lines using destructuring?:
const array0 = someArray[0].data;
const array1 = someArray[1].data;
const array2 = someArray[2].data;
Whether using destructuring would actually be a simplification is debatable but this is how it can be done:
const [
{ data: array0 },
{ data: array1 },
{ data: array2 }
] = someArray
Live Example:
const someArray = [
{ data: 1 },
{ data: 2 },
{ data: 3 }
];
const [
{ data: array0 },
{ data: array1 },
{ data: array2 }
] = someArray
console.log(array0, array1, array2);
What is happening is that you're first extracting each object from someArray then destructuring each object by extracting the data property and renaming it:
// these 2 destructuring steps
const [ obj1, obj2, obj3 ] = someArray // step 1
const { data: array0 } = obj1 // step 2
const { data: array1 } = obj2 // step 2
const { data: array2 } = obj3 // step 2
// written together give
const [
{ data: array0 },
{ data: array1 },
{ data: array2 }
] = someArray
Maybe combine destructuring with mapping for (potentially) more readable code:
const [array0, array1, array2] = someArray.map(item => item.data)
Live Example:
const someArray = [
{ data: 1 },
{ data: 2 },
{ data: 3 }
];
const [array0, array1, array2] = someArray.map(item => item.data)
console.log(array0, array1, array2);
I believe what you actually want is
const array = someArray.map(x => x.data)
If you really want three variables (Hint: you shouldn't), you can combine that mapping with destructuring:
const [array0, array1, array2] = someArray.map(x => x.data)
If you want to do with this pure JS then follow this code snippet. It will help you.
let myArray = [
{
"_id": "1",
"subdata": [
{
"subid": "11",
"name": "A"
},
{
"subid": "12",
"name": "B"
}
]
},
{
"_id": "2",
"subdata": [
{
"subid": "12",
"name": "B"
},
{
"subid": "33",
"name": "E"
}
]
}
]
const array = myArray.map(x => x.subdata).flat(1)
const isExist = (key,value, a) => {
return a.find(item => item[key] == value)
}
let a = array.reduce((acc, curr) => {
if(!isExist('subid', curr.subid, acc)) {
acc.push(curr)
}
return acc
}, [])
console.log(a)
const myInfo = someArray.map((item) => {
const {itemval1, itemval2} = item;
return(
//return data how you want it eg:
<p>{itemval1}</p>
<p>{itemval2}</p>)
})
This is how I did it in react, correct me if m wrong, I'm still new to this world
#Daniel, I presume you were looking to destructure a nested Object in an array of Objects. Following #nem035 was able to extract the nested Object's property using his pattern.
What is happening is that you're first extracting each object from addresses array then destructuring each object by extracting its properties and renaming it including the nested Object:
addresses = [
{
locality:"Sarjapura, Bangalore",
coordinates:{latitude:"12.901160", longitude:"77.711680"}
},
{
locality:"Vadakara, Kozhikode",
coordinates:{latitude:"11.588980", longitude:"75.596450"}
}
]
const [
{locality:loc1, coordinates:{latitude:lat1, longitude:ltd1}},
{locality:loc2, coordinates:{latitude:lat2, longitude:ltd2}}
] = addresses
console.log(`Latitude of Vadakara :: ${lat2}`)

Categories