Merge arrays of object into single array of object - javascript

I am looking for merge arrays of object into single array of object and append key of object to each key from inner object's key
I have object like
var myObj = {
"Details": [{
"car": "Audi",
"price": 40000,
"color": "blue"
},
{
"car": "BMW",
"price": 35000,
"color": "black"
}
],
"Accounts": [{
"Total": 2000
},
{
"Total": 3000
}
]
}
and Keys and objects length is not known, I want to merge it like
[
{
"Detailscar": "Audi",
"Detailsprice": 40000,
"Detailscolor": "blue",
"AccountsTotal": 2000
},
{
"Detailscar": "BMW",
"Detailsprice": 35000,
"Detailscolor": "black",
"AccountsTotal": 3000
}
]
I have tried with Ramda mergeAll but it is not working in my case as it only merge objects
here is what I tried
var mergedArray = []
R.mapObjIndexed((instance, instanceName) => {
mergedArray.push(R.map((innerObj) => {
var customObject = {};
R.forEach((key) => {
customObject[`${instanceName}${key}`] = innerObj[key]
}, Object.keys(innerObj))
return customObject;
}, instance))
}, myObj)
I am trying add to each modified object to the mergerArray array but it adding for each iteration and finally, it is creating 2 arrays
mergedArray is still creating two different arrays with the key of the object to be appended to the properties of the object but I want it to be merged in the single array of object.
I am missing something. What should I do to resolve this issue?
Suggest some help.

Use Array.prototype.map and index as second parameter passsed to its callback to get element from Account object
const data = {
"Details": [
{
"car": "Audi",
"price": 40000,
"color": "blue"
},
{
"car": "BMW",
"price": 35000,
"color": "black"
},
{
"car": "Porsche",
"price": 60000,
"color": "green"
}
],
"Accounts": [
{
"Total": 2000
},
{
"Total": 3000
},
{
"Total": 3000
}
]
};
const mergeCarData = ({ Details, Accounts} = {}) => {
return Details.length === Accounts.length ? Details.map(({ car, price, color}, idx) => ({
Detailscar: car,
Detailsprice: price,
Detailscolor: color,
AccountsTotal: Accounts[idx].Total
})) : [];
};
console.log(mergeCarData(data));

In plain Javascript, you could iterate the keys of the given object and iterate the arrays and build a new object out of the inner properties with a new key.
var object = { Details: [{ car: "Audi", price: 40000, color: "blue" }, { car: "BMW", price: 35000, color: "black" }], Accounts: [{ Total: 2000 }, { Total: 3000 }] },
result = Object.keys(object).reduce(function (returnValue, parentKey) {
object[parentKey].forEach(function (currentObj, i) {
returnValue[i] = returnValue[i] || {};
Object.keys(currentObj).forEach(function (childKey) {
returnValue[i][parentKey + childKey] = currentObj[childKey];
});
});
return returnValue;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Well, it's not pretty, but you could do something like this:
const convert = pipe(
mapObjIndexed((val, name) => pipe(
map(toPairs),
map(map(([prop, propVal]) => objOf(name + prop, propVal))),
map(mergeAll),
)(val)),
values,
apply(zipWith(merge))
)
You can see this in action on the Ramda REPL.

Here you go, another option. I pulled a renameBy function from ramda recipe list. You can combine all the stuff on to one line if you want.
// https://github.com/ramda/ramda/wiki/Cookbook#rename-keys-of-an-object
const renameBy = R.curry((fn, obj) => R.pipe(R.toPairs, R.map(R.adjust(fn, 0)), R.fromPairs)(obj));
let convertNames = R.mapObjIndexed((value, key)=> {
return R.map(renameBy(R.concat(key)), value)
})
let mergeUp = R.pipe(R.values, R.reduce(R.mergeDeepLeft,[]), R.values)
let convert = R.pipe(convertNames, mergeUp)
convert(myObj)

Related

Array of object into a nested object for every value in the array

Trying to turn an array of objects into a nested object. Is there a good method for this? and how do I make it depending on the array length?
Working but is not universal:
https://codesandbox.io/s/thirsty-roentgen-3mdcjv?file=/src/App.js
What I have:
sorting: [
{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
},
{
"id": "gender",
"options": [
"male",
"female"
]
}
]
What I want:
value: {
"Region": "Oklahoma",
"HighestDegree": {
"HighSchool": {
"male": null,
"female":null
},
"Undergraduate":{
"male": null,
"female":null
}
//and so on...
}
}
The code beneath works but is hardcoded for only two different options. I want it to be able to nest the length of the array. So lets say another object was age it would be {"HighSchool":{male:{"<25":null,"25-35":null}}} etc..
function testSortingArray() {
let sorting = [
{
id: "HighestDegree",
options: ["HighSchool", "Undergraduate", "Bachelor", "Master", "Doctor"]
},
{
id: "gender",
options: ["male", "female"]
}
];
let GoalArray = {};
if (sorting.length > 0) {
sorting[0].options.map((firstArray) => {
let currObject = {};
sorting[1].options.map((secondOption) => {
currObject[secondOption] = null;
});
GoalArray[firstArray] = currObject;
});
}
return GoalArray;
}
console.log(testSortingArray());
You can do it with a recursive function.
The function below reduces every options array to an object, and then continues populating that object if there are rest elements left from the original sorting array.
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
Besides the reduce() method, the code above makes use of object and array destructuring and spread syntax.
Complete snippet:
const sorting = [{
"id": "HighestDegree",
"options": [
"HighSchool",
"Undergraduate",
"Bachelor",
"Master",
"Doctor"
]
}, {
"id": "gender",
"options": [
"male",
"female"
]
}, {
"id": "age",
"options": [
"<25",
"25-35"
]
}];
const fn = ([{ options }, ...rest]) => options.reduce((a, v) => ({
...a,
[v]: rest.length ? fn(rest): null
}), {});
const result = fn(sorting);
console.log(result);

Insert new JSON objects in nested JS array based on condition

For one of my e-commerce application requirement, I have a nested array of the form (Sample):
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
]
To this array, I need to insert new objects to the array 'labels' if the group value equals 'upper-wear'.
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
]
This is what I tried so far, considering that for now I only need to insert to single label (i.e. 'upper-wear') (in future, there can be multiple labels category 'upper-wear', 'bottom-wear', to be inserted into):
const updatedArray = data.map((datum) => {
if (datum.group === 'upper-wear') {
return {
...datum,
labels: [...datum.labels, ...newDataToInsert]
};
}
});
console.log(updatedArray);
But there seems to be a silly issue that I am missing as the result returns like this:
[
{
id: 1,
group: 'upper-wear',
labels: [ [Object], [Object] ],
popular: true
},
undefined
]
I know there may be better approaches available, but this is what I can think of as the minimum solution for now.
any help to resolve the current or any better solution will be highly appreciated.
Try with this
updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
})
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
];
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
];
const updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
});
console.log(updatedArray)
Explaination
Here while mapping the data, we check for the condition
IF
If it matches then we will first copy the whole object from the variable b return { ...b }
after that we take another variable with the same name lables return { ...d, labels: d.labels.concat(newDataToInsert) },As per the JSON default nature the new variable with the same name will hold the latest value
Here in labels we first take a copy of old data and then merge it with newDataToInsert array labels: d.labels.concat(newDataToInsert), It will merge 2 arrays and store them in JSON with the name labels
Else
In else we just return the current values else { return d; }
You don't actually need to iterate with map over the array. Just find an object in the array and change what you want.
const data=[{id:1,group:"upper-wear",labels:[{type:"shirts",quantity:"20"}],popular:true},{id:2,group:"bottom-wear",lables:[{type:"trousers",quantity:"31"}],popular:true}];
const newDataToInsert=[{type:"blazers",quantity:19}];
data.find(({ group }) => group === 'upper-wear')?.labels.push(...newDataToInsert);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're not returning all objects from your map. you're only returning a result when your criteria is met. This is resulting in your undefined objects...
const data = [
{ "id": 1, "group": "upper-wear", "labels": [ { "type": "shirts", "quantity": "20", }, ], popular: true },
{ "id": 2, "group": "bottom-wear", "lables": [ { "type": "trousers", "quantity": "31", }, ], popular: true },
]
const newDataToInsert = [ { "type": 'blazers',"quantity": 19 }, ]
const updatedArray = data.map(datum => {
if (datum.group === 'upper-wear') datum.labels = [...datum.labels, ...newDataToInsert]
return datum
});
console.log(updatedArray);
You can use Array#find to locate the desired group and then change labels for the group found. There are two options depending on how many items you would like to insert. Use Array#push to add the desired item; use forEach for more than one item:
const searchgroup = "upper-wear";
const target = data.find(({group}) => group === searchgroup);
target.labels.push(...newDataToInsert); //For one item to insert
//newDataToInsert.forEach(label => target.labels.push( label )); //For more than one item
const data = [{"id": 1, "group": "upper-wear", "labels": [{"type": "shirts", "quantity": "20"},],popular: true }, {"id": 2, "group": "bottom-wear", "lables": [{"type": "trousers", "quantity": "31", },],popular: true}];
const newDataToInsert = [{"type": 'blazers', "quantity": 19}];
//group to find
const searchgroup = "upper-wear";
//target element in data
const target = data.find(({group}) => group === searchgroup);
//check if group was found
if( target ) {
//if there's only one product in newDataToInsert us this:
//target.labels.push(...newDataToInsert);
//if you have more than one product to be inserted use this; also works for one
newDataToInsert.forEach(label => target.labels.push( label ));
} else {
console.log( `No such group found: ${searchgroup}!` );
}
console.log( data );

Assign to object within map

I'd like to assign to an object within an array map
Heres the array of objects I want to add to
const arr = [
{
"key": "Mike",
"ref": 11800
},
{
"key": "Raph",
"ref": 9339
},
{
"key": "Leo",
"ref": 2560
},
]
I want to add add a new property to the object called slug while I loop over it like below. Possibly map is not the right function to use here because ESLINT complains about assigning within the map.
arr.map((item) => {
...item,
item.slug = `${item.key.toLowerCase();}/${String(item.ref)}`
});
.map() returns a new array containing the results of calling provided function for each element, so you should assign it to the new variable:
const arr = [{
"key": "Mike",
"ref": 11800
},
{
"key": "Raph",
"ref": 9339
},
{
"key": "Leo",
"ref": 2560
},
]
const newArr = arr.map(item => ({
...item,
slug: `${item.key.toLowerCase()}/${String(item.ref)}`
}))
console.dir(newArr)
If you want to add something to existing objects within an array you should use a for loop or .forEach():
const arr = [{
"key": "Mike",
"ref": 11800
},
{
"key": "Raph",
"ref": 9339
},
{
"key": "Leo",
"ref": 2560
},
]
arr.forEach(item => {
item.slug = `${item.key.toLowerCase()}/${String(item.ref)}`
})
console.dir(arr)
When mutating an array, or perform operations with side-effects, you should use a for loop or the Array.prototype.forEach method. If you want to perform pure functional operations over an array, then use Array.prototype.filter, Array.prototype.map, etc.
If you want to set a new property on the existing array elements then do this:
const arr = [ { key: "Mike", ref: 11800 }, /*etc*/ ];
for( const e of arr ) {
e.slug = e.key.toLowerCase() + "/" + e.ref.toString();
}
If you want to generate a new array with new members, then do this:
const arr = [ { key: "Mike", ref: 11800 }, /*etc*/ ];
// Note the parentheses within `map` to avoid ambiguous syntax:
const newArr = arr.map( e => ( { slug: e.key.toLowerCase() + "/" + e.ref.toString() } ) );
console.log( newArr ); // [ { slug: "mike/11800" } ]
Alternatively, to copy over all properties and then add new properties use Object.assign:
const arr = [ { key: "Mike", ref: 11800 }, /*etc*/ ];
const newArr = arr.map( e => Object.assign( {}, e, { slug: e.key.toLowerCase() + "/" + e.ref.toString() } ) );
console.log( newArr ); // [ { key: "Mike", ref: 11800, slug: "mike/11800" } ]

Add fields from array of JSON objects based on specific field

I have an array of objects ...
[
{
"matchID":"-LP0LKl_nR4VQf6Gxwz8",
"playerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points":"11",
"id":"-LP1WvT4eN1L7BLbyhJt"
},
{
"matchID":"-LP0LKl_nR4VQf6Gxwz8",
"playerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points":"23",
"id":"-TP1WvT4eN1L7GeYyhJt"
},
{
"matchID":"-DF0LKl_nR4VQf6Gxwz7",
"playerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points":"12",
"id":"-GH1WvT4eN1L7GeYyhJt"
},
{
"matchID":"-DF0LKl_nR4VQf6Gxwz7",
"playerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points":"6",
"id":"-XZ1WvR2eN1L7GeYyhJt"
}
]
I want to loop through this array and create a new array that is a basically just each playerId and their total points for all matches.
So after cycling through the above, the array would look like ..
[{"palyerId": "YabcVY1gsZSzI7ZQzyDTZbSwdLF6", "points": "35"},{"palyerId": "YvtwVY1gsZSzI7ZQzyDTZbSwdLF3", "points": "17"}]
You can use reduce() to loop thru the array. Use new Map() to group the array. And use spread operator to convert the map object into an array.
var arr = [{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"11","id":"-LP1WvT4eN1L7BLbyhJt"},{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"23","id":"-TP1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"12","id":"-GH1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"6","id":"-XZ1WvR2eN1L7GeYyhJt"}]
var result = [...arr.reduce((c, v) => {
if (!c.has(v.palyerId)) c.set(v.palyerId, {"palyerId": v.palyerId,"points": 0});
c.get(v.palyerId).points += +v.points;
return c;
}, new Map()).values()];
console.log(result);
Or you can reduce() the array into an object using the palyerId as the key. Use Object.values() to convert the object into an array.
var arr = [{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"11","id":"-LP1WvT4eN1L7BLbyhJt"},{"matchID":"-LP0LKl_nR4VQf6Gxwz8","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"23","id":"-TP1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YabcVY1gsZSzI7ZQzyDTZbSwdLF6","points":"12","id":"-GH1WvT4eN1L7GeYyhJt"},{"matchID":"-DF0LKl_nR4VQf6Gxwz7","palyerId":"YvtwVY1gsZSzI7ZQzyDTZbSwdLF3","points":"6","id":"-XZ1WvR2eN1L7GeYyhJt"}]
var result = Object.values(arr.reduce((c, {palyerId,points}) => {
c[palyerId] = c[palyerId] || {palyerId,points: 0};
c[palyerId].points += +points;
return c;
}, {}));
console.log(result);
A non reduce version with a for loop looks like the following (again, the central idea is to group by playerId):
let data = [{
"matchID": "-LP0LKl_nR4VQf6Gxwz8",
"playerId": "YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points": "11",
"id": "-LP1WvT4eN1L7BLbyhJt"
},
{
"matchID": "-LP0LKl_nR4VQf6Gxwz8",
"playerId": "YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points": "23",
"id": "-TP1WvT4eN1L7GeYyhJt"
},
{
"matchID": "-DF0LKl_nR4VQf6Gxwz7",
"playerId": "YabcVY1gsZSzI7ZQzyDTZbSwdLF6",
"points": "12",
"id": "-GH1WvT4eN1L7GeYyhJt"
},
{
"matchID": "-DF0LKl_nR4VQf6Gxwz7",
"playerId": "YvtwVY1gsZSzI7ZQzyDTZbSwdLF3",
"points": "6",
"id": "-XZ1WvR2eN1L7GeYyhJt"
}
];
var res = {};
for (let d of data) {
if (d.playerId in res) res[d.playerId].points += parseInt(d.points);
else res[d.playerId] = {playerId: d.playerId, points: parseInt(d.points)};
}
console.log(Object.values(res));

Build array from another array if some key are identical using JavaScript

I have an array of data. Some of the key in the array are same. I would like to create a new array based on the key and add the other data.
This is my array
var myObjOne = [
{
"name":"John",
"id":1,
"car":"maruti"
},
{
"name":"John",
"id":2,
"car":"wolks"
},
{
"name":"John",
"id":3,
"car":"bmw"
},
{
"name":"Peter",
"id":4,
"car":"alto"
},
{
"name":"Peter",
"id":5,
"car":"swift"
}
];
I would like to convert the array in to the below format.
var myObj = [
{
"name":"John",
"items": [
{ "id":1, "car":"maruti" },
{ "id":2, "car":"wolks" },
{ "id":3, "car":"bmw" }
]},
{
"name":"Peter",
"items": [
{ "id":4, "car":"alto" },
{ "id":5, "car":"swift" },
]
}
];
I am working on a node environment.
You can create an object using Array#reduce first which maps name with items, and then create the final array by looping over the intermediate map using a for...of loop:
var source = [{"name":"John","id":1,"car":"maruti"},{"name":"John","id":2,"car":"wolks"},{"name":"John","id":3,"car":"bmw"},{"name":"Peter","id":4,"cars":"alto"},{"name":"Peter","id":5,"cars":"swift"}];
const map = source.reduce((acc, {name, ...obj}) => {
if (!acc[name]) {
acc[name] = [];
}
acc[name].push(obj);
return acc;
}, {});
const result = [];
for (let[name, items] of Object.entries(map)) {
result.push({name, items});
}
console.log(result);
Array.reduce is at rescue.This method accepts an accumulator and current
item. Check in the accumulator if there exist an object where the value of name property is John or Peter
var myObjOne = [{
"name": "John",
"id": 1,
"car": "maruti"
},
{
"name": "John",
"id": 2,
"car": "wolks"
},
{
"name": "John",
"id": 3,
"car": "bmw"
},
{
"name": "Peter",
"id": 4,
"car": "alto"
},
{
"name": "Peter",
"id": 5,
"car": "swift"
}
];
var newObj = myObjOne.reduce(function(acc, curr, currIndex) {
// using findIndex to check if there exist an object
// where the value of the name property is John, Peter
// if it exist it will return the index else it will return -1
let ifNameExist = acc.findIndex(function(item) {
return item.name === curr.name;
})
// if -1 then create a object with name and item property and push
// it to the accumulator
if (ifNameExist === -1) {
let nameObj = {};
nameObj.name = curr.name;
nameObj.items = [];
nameObj.items.push({
id: curr.id,
car: curr.car
})
acc.push(nameObj)
} else {
// if such an object already exist then just update the item array
acc[ifNameExist].items.push({
id: curr.id,
car: curr.car
})
}
return acc;
}, []);
console.log(newObj)
Use .reduce to group by name, and use .find inside the reducer to find if the matching name has already been added:
const input=[{"name":"John","id":1,"car":"maruti"},{"name":"John","id":2,"car":"wolks"},{"name":"John","id":3,"car":"bmw"},{"name":"Peter","id":4,"cars":"alto"},{"name":"Peter","id":5,"cars":"swift"}]
const output = input.reduce((a, { name, ...item }) => {
const foundNameObj = a.find(nameObj => nameObj.name === name);
if (foundNameObj) foundNameObj.items.push(item);
else a.push({ name, items: [item] });
return a;
}, []);
console.log(output);

Categories