Group by using same key and join value with comma - javascript

I have the following sample data with dynamic key /value pair, i need to group them with same key , joining mutiple value by comma. I was able to extract unique keys on array and then stuck
input
[
{
"c1": "USA"
},
{
"c2": "Korea"
},
{
"c4": "japan"
},
{
"c3": "india"
},
{
"c1": "australia"
},
{
"c2": "france"
}
]
output
[
{
"c1": "USA,australia",
"c2": "Korea,france",
"c4": "japan",
"c3": "india"
}
]
let data=[
{
"c1": "USA"
},
{
"c2": "Korea"
},
{
"c4": "japan"
},
{
"c3": "india"
},
{
"c1": "australia"
},
{
"c2": "france"
}
]
var output = Object.keys(data).map(element => {
var ret = Object.keys(data[element]);
return ret;
})
let c=[...new Set(output.flat())];
console.log(c);
How to join the values by comma from the unique array i got, i think i am halfway

you'll need to output a new organized object with the help of your map() function.
didn't check the code - but it should look something like this.
let dataNew = {};
var output = Object.keys(data).map(element => {
if (typeof( dataNew[element] ) === 'undefined') {
dataNew[element] = data[element]
} else {
dataNew[element] += `,${data[element]}`;
}
return dataNew;
})
like this you are creating a new object with comas in unified values.

You could try Array.reduce prototype
let data=[
{
"c1": "USA"
},
{
"c2": "Korea"
},
{
"c4": "japan"
},
{
"c3": "india"
},
{
"c1": "australia"
},
{
"c2": "france"
}
]
const combinedData = data.reduce((memory, current) => {
const currentKeys = Object.keys(current);
const found = memory.find(m =>
Object.keys(m).some(key => currentKeys.includes(key))
);
if (found) {
const key = Object.keys(found).pop();
found[key] = `${found[key]},${current[key]}`;
} else {
memory.push(current);
}
return memory;
}, []);
console.log(combinedData);

Take the key value pairs, flatten them (as it does not matter wether the keys where in the same object or not), then merge using another object:
const result = input
.map(Object.entries)
.flat()
.reduce((obj, [k, v]) => ((obj[k] = obj[k] ? (obj[k] + ", " + v) : v), obj), {});

Sorry i went back and de-duped the final Array.
const data = [
{
"c1": "USA"
},
{
"c2": "Korea"
},
{
"c4": "japan"
},
{
"c3": "india"
},
{
"c1": "australia"
},
{
"c2": "france"
}
]
let endData = [{}]
data.forEach(d => {
const [dataKey] = Object.keys(d)
const relatedValues = data.map(d => dataKey in d ? d[dataKey] : null).filter(Boolean)
const combinedValues = [d[dataKey], ...relatedValues]
const unique = endData.find(d => Object.keys(d)[0] === dataKey) === undefined
if (unique) endData[0][dataKey] = `${[...new Set(combinedValues)]}`
})
console.log(endData)

Without changing your code:
iterate over each value of Set c, then filter the data items that have the same key, then merge them with join (',').
let data=[
{
"c1": "USA"
},
{
"c2": "Korea"
},
{
"c4": "japan"
},
{
"c3": "india"
},
{
"c1": "australia"
},
{
"c2": "france"
}
]
var output = Object.keys(data).map(element => {
var ret = Object.keys(data[element]);
return ret;
})
let c=[...new Set(output.flat())];
/* Join equals */
const n = [];
c.forEach( (it, idx) => {
n.push({});
n[idx][it] = data.filter(its => it === Object.keys(its)[0])
.map( its => Object.values(its)[0])
.join(',');
})
console.log(n);

Related

Group array of objects with a specific key value pushed first

Given the following Array of Objects:
[
{
"teamFK": 8650,
"code": "yellow_cards",
"typeId": 554,
"value": "5",
"side": "home"
},
{
"teamFK": 8650,
"code": "goals",
"typeId": 554,
"value": "1",
"side": "home"
},
{
"teamFK": 8990,
"code": "yellow_cards",
"typeId": 555,
"value": "2",
"side": "away"
},
{
"teamFK": 8990,
"code": "goals",
"typeId": 555,
"value": "0",
"side": "away"
}
]
I would like to group this data by code and get this result:
{
"stats": [
{
"name": "yellow_cards",
"stats": ["5","2"]
},
{
"name": "goals",
"stats": ["2","0"]
}
]
}
What I've done is the following which works but I want to make sure that the alway the stat with "side":"home" always pushed first into the array "stats": []:
const groupedStats = Object.entries(
query.reduce((acc, { typeId, value, code, side }) => {
if (!acc[code]) {
acc[code] = [];
}
acc[code].push(value);
return acc;
}, {}),
).map(([name, stats]) => ({ name, stats }));
My approach is sort it first by side using Array.sort() and then looping through the objects and adding it to stats
i created a const match to find if there is a match already so i dont have to add the name and value again basically if its not a match i'll add it to the stats array and if its a match then i'll just update the current index
const objs = [
{
teamFK: 8650,
code: "yellow_cards",
typeId: 554,
value: "5",
side: "home",
},
{
teamFK: 8650,
code: "goals",
typeId: 554,
value: "1",
side: "away",
},
{
teamFK: 8990,
code: "yellow_cards",
typeId: 555,
value: "2",
side: "away",
},
{
teamFK: 8990,
code: "goals",
typeId: 555,
value: "0",
side: "home",
},
];
let stats = [];
const transformedObj = objs
.sort((a, b) => {
if (a.side > b.side) {
return -1;
}
if (a.side < b.side) {
return 1;
}
return 0;
})
.forEach((obj) => {
const match = stats.find((stat) => stat.name === obj.code);
const statsIndex = stats.findIndex((stat) => stat.name === obj.code);
if (!match) {
stats = [...stats, { name: obj.code, value: [obj.value] }];
} else {
stats[statsIndex] = {
name: stats[statsIndex].name,
value: [...stats[statsIndex].value, obj.value],
};
}
});
console.log(stats);
You can sort array and use key grouping approach:
const data = [{"teamFK": 8650,"code": "yellow_cards","typeId": 554,"value": "5","side": "home"},{"teamFK": 8650,"code": "goals","typeId": 554,"value": "1","side": "home"},{"teamFK": 8990,"code": "yellow_cards","typeId": 555,"value": "2","side": "away"},{"teamFK": 8990,"code": "goals","typeId": 555,"value": "0","side": "away"}];
const groups = data
.sort(({ side: a }, { side: b }) => b.localeCompare(a))
.reduce((acc, { code, value }) => {
acc[code] ??= { name: code, stats: [] };
acc[code]['stats'].push(value);
return acc;
}, {});
const result = { stats: Object.values(groups) };
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0 }

moving a key value pair out of an array

I am trying to move everything in the Array Results outside and into the original object
this is the object
{
"Name": "John",
"Results": [
{
"Type": "DB",
"Immediate_Action": "No",
}
]
}
It should look like this
{
"Name": "John",
"Type": "DB",
"Immediate_Action": "No",
}
What I have so far is this
const mapOscarResults = ({ data }) => {
return data.map(entry => {
let mapped = {...entry};
entry.Results.forEach(key => {
let Type = mapped[key.Type]
if (mapped[key]) {
mapped[key].push(entry.Results[key]);
} else {
mapped[key] = [entry.Results[key]];
}
});
return mapped;
});
};
You can simply spread the Results array into an Object.assign() call.
const input = { "Name": "John", "Results": [{ "Type": "DB", "Immediate_Action": "No", }, { "Another": "value" }] };
const { Results, ...refactored } = input;
Object.assign(refactored, ...Results);
console.log(refactored)
This code works for your example:
const { Results: results, ...rest } = {
"Name": "John",
"Results": [
{
"Type": "DB",
"Immediate_Action": "No",
}
]
}
const res = {...rest, ...results.reduce((prev, curr) => ({
...prev,
...curr
}), {})}
console.log(res)
But I don't know what you expect when the Results array has more than one element.
In that condition, if this code does not fill your needs, ask me to change it.
however, it will join first Result with index 0, you can expand it
const data = {
"Name": "John",
"Results": [
{
"Type": "DB",
"Immediate_Action": "No",
}
]
}
const mapOscarResults = (data) => {
for (let i in Object.keys(data)){
if (Array.isArray(data[Object.keys(data)[i]])){
newKey = data[Object.keys(data)[i]][0]
data = {... data, ...newKey}
delete data[Object.keys(data)[i]]
}
}
return data
};
console.log(mapOscarResults(data))

Renaming keys in an object using reduce method in JS

Following is the object in which I want to replace countryID with value, countryName with label.
In the same object I am having localLanguages Array in which I am trying to rename language with label and languageCode with value.
array -
var obj = [{
"countryID": "CON1010",
"countryName": "Poland",
"countryCode": "pl",
"localLanguages": [{
"language": "English",
"languageCode": "en"
},
{
"language": "Polish",
"languageCode": "en"
}
]
},
{
"countryID": "CON1011",
"countryName": "UK",
"countryCode": "uk",
"localLanguages": [{
"language": "English",
"languageCode": "en"
}]
}
];
Transformed to -
var obj = [{
"value": "CON1010",
"label": "Poland",
"countryCode": "pl",
"localLanguages": [{
"label": "English",
"value": "en"
},
{
"label": "Polish",
"value": "en"
}
]
},
{
"value": "CON1011",
"label": "UK",
"countryCode": "uk",
"localLanguages": [{
"label": "English",
"value": "en"
}]
}
];
Code -
arr.map(x => {
var newObj = Object.keys(x).reduce((obj, key) => {
if (key !== 'countryID') {
obj[key] = x[key]
}
if (key === 'countryID') {
obj.value = x.countryID;
}
}, {})
console.log(newObj);
return newObj;
})
Here is a solution with es6 Destructuring and map:
const arr = [{"countryID":"CON1010","countryName":"Poland","countryCode":"pl","localLanguages":[{"language":"English","languageCode":"en"},{"language":"Polish","languageCode":"en"}]},{"countryID":"CON1011","countryName":"UK","countryCode":"uk","localLanguages":[{"language":"English","languageCode":"en"}]}];
const result = arr.map(item => {
let localLanguages = item.localLanguages.map(i => {
const { language: label, languageCode: value, ...rest } = i;
return { label, value, ...rest };
});
const { countryID: value, countryName: label, ...rest } = item;
return { value, label, ...rest, localLanguages };
});
console.log(result)
Use Array.map() to convert the outer objects, and another map to convert the localLanguages:
const arr = [{"countryID":"CON1010","countryName":"Poland","countryCode":"pl","localLanguages":[{"language":"English","languageCode":"en"},{"language":"Polish","languageCode":"en"}]},{"countryID":"CON1011","countryName":"UK","countryCode":"uk","localLanguages":[{"language":"English","languageCode":"en"}]}];
const result = arr.map(o => ({
value: o.countryID,
label: o.countryName,
countryCode: o.countryCode,
localLanguages: o.localLanguages.map(l => ({
value: l.languageCode,
label: l.language
}))
}));
console.log(result)
You have forgotten to return obj value in the reduce function
var newObj = Object.keys(x).reduce( (obj, key) => {
if(key !== 'countryID') {
obj[key] = x[key]
}
if(key === 'countryID') {
obj.value = x.countryID;
}
}, {})
Here the function to change the keys. Use it with every element of you arrayrecursively, but check the type of every element
function changeKeys(obj) {
const rename = {
'countryID': 'value',
'countryName': 'label',
'language': 'label',
'languageCode': 'value'
}
return Object.keys(obj)
.reduce(
(acc, rec) => {
if (typeof rename[rec] !== 'undefined') {
return {...acc, [rename[rec]]: obj[rec]}
}
return {...acc, [rec]: obj[rec]}
}, {}
)
}

merge duplicate objects in an array and combine sub array of each object

I am trying to merge objects based off of Id, and merge each array that lives inside each account (object), but instead of merging the contents of accountList, the code overwrites the array, if there is a matching id.
I've made a new array and used the .find method to find matching objects based off there id, but stuck on how to merge the accountList together
const accounts = [
{
"Id": 103,
"accountList": [
{}
]
},
{
"Id": 103,
"accountList": [
{
"tokenId": "5aasdasdsdnjn3434nadd",
"featureId": 2840
}
]
},
{
"Id": 112,
"accountList": [
{
"tokenId": "5d30775bef4a722c38aefaaa",
"featureId": 2877
}
]
},
{
"Id": 112,
"accountList": [
{
"tokenId": "5d30775bef4a722c38aefccc",
"featureId": 2856
}
]
}
]
let result = [];
accounts.forEach(account => {
let match = result.find(r => r.Id === account.Id);
// console.log(match)
if(match) {
Object.assign(match, account);
//tried using spread operator instead of object assign, but didnt work
// match = {...match, ...account}
} else {
result.push(account);
}
});
console.log( JSON.stringify(result, null, 2))
The result which i need is to merge the object based off their id, and merge the contents of the accountList together, like so:
[
{
"Id": 103,
"accountList": [
{
"tokenId": "5aasdasdsdnjn3434nadd",
"featureId": 2840
}
]
},
{
"Id": 112,
"accountList": [
{
"tokenId": "5d30775bef4a722c38aefaaa",
"featureId": 2877
},
{
"tokenId": "5d30775bef4a722c38aefccc",
"featureId": 2856
}
]
}
]
I think, reduce() would do the job:
const accounts = [{"Id":103,"accountList":[{}]},{"Id":103,"accountList":[{"tokenId":"5aasdasdsdnjn3434nadd","featureId":2840}]},{"Id":112,"accountList":[{"tokenId":"5d30775bef4a722c38aefaaa","featureId":2877}]},{"Id":112,"accountList":[{"tokenId":"5d30775bef4a722c38aefccc","featureId":2856}]}];
const result = [...accounts
.reduce((r, o) => {
const record = r.get(o.Id)||{}
r.set(o.Id, {
Id: o.Id,
accountList: [
...(record.accountList||[]),
...o.accountList.filter(o =>
Object.keys(o).length != 0)
]
})
return r
}, new Map())
.values()]
console.log(result);
.as-console-wrapper {min-height: 100%}
You can try to use Array.concat:
let result = [];
accounts.forEach(account => {
let match = result.find(r => r.Id === account.Id);
// console.log(match)
if(match) {
match.accountList = match.accountList.concat(account.accountList);
} else {
result.push(account);
}
});
for (let res of result) {
console.log('res.Id: ', res.Id, res.accountList)
}
// res.Id: 103 [ {}, { tokenId: '5aasdasdsdnjn3434nadd', featureId: 2840 } ]
// res.Id: 112 [ { tokenId: '5d30775bef4a722c38aefaaa', featureId: 2877 },
// { tokenId: '5d30775bef4a722c38aefccc', featureId: 2856 } ]
Using Array.prototype.reduce we can accumulate the results in the final result array.
In the reduce call back just find the matching object using Id and merge the accountList array and not the object as you were doing in your code.
const accounts=[{"Id":103,"accountList":[{}]},{"Id":103,"accountList":[{"tokenId":"5aasdasdsdnjn3434nadd","featureId":2840}]},{"Id":112,"accountList":[{"tokenId":"5d30775bef4a722c38aefaaa","featureId":2877}]},{"Id":112,"accountList":[{"tokenId":"5d30775bef4a722c38aefccc","featureId":2856}]}];
const result = accounts.reduce((acc, account) => {
let match = acc.find(r => r.Id === account.Id);
if(match) {
match.accountList.push(...account.accountList); //push previous array
} else {
const act = { ...account };
act.accountList = account.accountList.filter((obj) => Object.keys(obj).length);
acc.push(act);
}
return acc;
}, []);
console.log(result);
I think you could use match.accountList.push(...account.accountList); instead of the object assign, spread operator can be used to push the element into the result item(match):
let accounts = [{ "Id": 103, "accountList": [{}] }, { "Id": 103, "accountList": [{ "tokenId": "5aasdasdsdnjn3434nadd", "featureId": 2840 }] }, { "Id": 112, "accountList": [{ "tokenId": "5d30775bef4a722c38aefaaa", "featureId": 2877 }] }, { "Id": 112, "accountList": [{ "tokenId": "5d30775bef4a722c38aefccc", "featureId": 2856 }] }];
let result = [];
accounts.forEach(account => {
(match = result.find(r => r.Id === account.Id), match ? match.accountList.push(...account.accountList) : result.push(account))
});
console.log(result);
const isNotEmptyObject = objc => Object.entries(objc).length > 0;
function mergeAccounts(accounts) {
const uniqueAccounts = new Map();
accounts.forEach(account => {
if(uniqueAccounts.has(account.Id)) {
let uniqueAccount = uniqueAccounts.get(account.Id);
if(account.accountList && account.accountList.length > 0)
uniqueAccount.accountList.push(...account.accountList);
uniqueAccount.accountList = uniqueAccount.accountList.filter(isNotEmptyObject);
} else {
uniqueAccounts.set(account.Id, account);
}
});
return Array.from(uniqueAccounts.values());
}
This will merge all the accounts having same ids. Hope this helps :)

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