Renaming keys in an object using reduce method in JS - javascript

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]}
}, {}
)
}

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 }

Get the difference object from two object in typescript/javascript angular

I am trying to get the change object from two objects using typescript in angular.
For example
this.productPreviousCommand = {
"id": "60f910d7d03dbd2ca3b3dfd5",
"active": true,
"title": "ss",
"description": "<p>ss</p>",
"category": {
"id": "60cec05df64bde4ab9cf7460"
},
"subCategory": {
"id": "60cec18c56d3d958c4791117"
},
"vendor": {
"id": "60ced45b56d3d958c479111c"
},
"type": "load_product_success"
}
model = {
"active": true,
"title": "ss",
"description": "<p>ss sss</p>",
"category": "60cec05df64bde4ab9cf7460",
"subCategory": "60cec18c56d3d958c4791117",
"vendor": "60ced45b56d3d958c479111c",
"tags": []
}
Now the difference between two objects are description: "<p>hello hello 1</p>". So I want to return {description: "<p>hello hello 1</p>"}
I used lodash https://github.com/lodash/lodash
import { transform, isEqual, isObject, isArray} from 'lodash';
function difference(origObj, newObj) {
function changes(newObj, origObj) {
let arrayIndexCounter = 0
return transform(newObj, function (result, value, key) {
if (!isEqual(value, origObj[key])) {
let resultKey = isArray(origObj) ? arrayIndexCounter++ : key
result[resultKey] = (isObject(value) && isObject(origObj[key])) ? changes(value, origObj[key]) : value
}
})
}
return changes(newObj, origObj)
}
This library is not working for me, it returns the whole object using this code const differenc = difference(this.productPreviousCommand, model);
The output of above code is
{
active: true
description: "<p>hello hello 1</p>"
id: "60f8f29dd03dbd2ca3b3dfd1"
title: "hello"
}
Try this function
differenceInObj(firstObj: any, secondObj: any): any {
let differenceObj: any = {};
for (const key in firstObj) {
if (Object.prototype.hasOwnProperty.call(firstObj, key)) {
if(firstObj[key] !== secondObj[key]) {
differenceObj[key] = firstObj[key];
}
}
}
return differenceObj;
}
You can check loop through each key of the first object and compare it with the second object.
function getPropertyDifferences(obj1, obj2) {
return Object.entries(obj1).reduce((diff, [key, value]) => {
// Check if the property exists in obj2.
if (obj2.hasOwnProperty(key)) {
const val = obj2[key];
// Check if obj1's property's value is different from obj2's.
if (val !== value) {
return {
...diff,
[key]: val,
};
}
}
// Otherwise, just return the previous diff object.
return diff;
}, {});
}
const a = {
active: true,
description: '<p>hello</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
const b = {
active: true,
description: '<p>hello hello 1</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
const c = {
active: true,
description: '<p>hello hello 2</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'world',
};
console.log(getPropertyDifferences(a, b));
console.log(getPropertyDifferences(b, c));
function difference(origObj, newObj) {
const origObjKeyList = Object.keys(origObj),
newObjKeyList = Object.keys(newObj);
// if objects length is not same
if (origObjKeyList?.length !== newObjKeyList?.length) {
return;
}
// if object keys some difference in keys
if (Object.keys(origObj).filter((val) => !Object.keys(newObj).includes(val))?.length) {
return;
}
return Object.entries(origObj).reduce(
(acc, [key, value]) => (newObj[key] !== value ? { ...acc, ...{ [key]: newObj[key] } } : acc),
[]
);
}
const a = {
active: true,
description: '<p>hello</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
const b = {
active: true,
description: '<p>hello hello 1</p>',
id: '60f8f29dd03dbd2ca3b3dfd1',
title: 'hello',
};
console.log(difference(a, b));
You can try this code.
function difference(origObj, newObj) {
const origObjKeyList = Object.keys(origObj),
newObjKeyList = Object.keys(newObj);
// if objects length is not same
if (origObjKeyList?.length !== newObjKeyList?.length) {
return;
}
// if object keys is not same
if (Object.keys(origObj).filter((val) => !Object.keys(newObj).includes(val))?.length) {
return;
}
return Object.entries(origObj).reduce(
(acc, [key, value]) => (newObj[key] !== value ? { ...acc, ...{ [key]: newObj[key] } } : acc),
[]
);
}

How to convert JSON to list of key with dotnotation

I am converting JSON keys to the list with dot-notation. If any dot is there represent nested jsonobject and if any [](array notation) is there resents jsonarray.
var keyify = (obj, prefix = '') =>
Object.keys(obj).reduce((res, el) => {
if (Array.isArray(obj[el])) {
return [...res, ...keyify(obj[el][0], prefix + el + '[].')];
} else if (typeof obj[el] === 'object' && obj[el] !== null) {
return [...res, ...keyify(obj[el], prefix + el + '.')];
} else {
return [...res, prefix + el];
}
}, []);
Above is the sample code that I am using for the converion. If input is
{
"input": {
"test": {
"phone": [
{
"phone1": "123"
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
Output will be:
[ 'input.test.phone[].phone1',
'customer[].lastname',
'customer[].firstname' ]
But the above code searches for only first JSONObject's keys in the JSONArray. But if the input is like this:
{
"input": {
"test": {
"phone": [
{
"phone1": "123"
},
{
"a": "456"
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
Then in the above JSON case the code will give output :
[ 'input.test.phone[].phone1',
'customer[].lastname',
'customer[].firstname' ]
So, the key a is missing only phone1 is coming in the list.So, how to get if multiple json keys are there then get keys with index of first occurence.
Expected output
[ 'input.test.phone[0].phone1',
'input.test.phone[1].a',
'customer[0].lastname',
'customer[0].firstname' ]
And if the JSONarray is value then it should be replaced by empty string.
For input:
const data = {
"input": {
"test": {
"phone": [
{
"phone1": ["123456"]
},
{
"a": ["1","2","3","4"]
}
]
}
},
"customer": [
{
"lastname": "def",
"firstname": "abc"
}
]
}
In this case "phone1": ["123456"] and "a": ["1","2","3","4"] are Json array as values this case lis will be like:
Expected Output:
[ 'input.test.phone[0].phone1',//instead of 'input.test.phone[0].phone1[0]'
'input.test.phone[1].a',//instead of 'input.test.phone[1].a[0]','input.test.phone[1].a[1]','input.test.phone[1].a[2]','input.test.phone[1].a[3]',
'customer[0].lastname',
'customer[0].firstname' ]
In the above case jsonarray should be considered as value not key.
You could use for...in loop to create recursive function for this and check if the current data input is an array or not to add dot or square brackets.
const data = { "input": { "test": { "phone": [ { "phone1": ["123456"] }, { "a": ["1","2","3","4"] } ] } }, "customer": [ { "lastname": "def", "firstname": "abc" } ] }
function parse(data, prev = '') {
const result = []
const check = data => {
if (typeof data !== 'object') {
return false
}
if (Array.isArray(data)) {
if (data.some(e => (typeof e != 'object'))) {
return false
}
}
return true;
}
for (let i in data) {
let dot = prev ? '.' : ''
let str = Array.isArray(data) ? `[${i}]` : dot + i
let key = prev + str;
if (check(data[i])) {
result.push(...parse(data[i], key))
} else {
result.push(key)
}
}
return result
}
const result = parse(data);
console.log(result)
You can traverse through the scope of the object and capture any paths that have a non-object value.
This is an extremely uncoupled and generic soulution.
const traverse = (obj, visitorFn, scope = []) => {
for (let key in obj) {
visitorFn.apply(this, [key, obj[key], scope]);
if (obj[key] !== null && typeof obj[key] === 'object') {
traverse(obj[key], visitorFn, scope.concat(key));
}
}
}
const scopeToPath = (obj) => obj.reduce((path, key) =>
path + (!isNaN(key) ? `[${key}]` : `.${key}`), '').substring(1);
const findObjectPaths = (obj) => {
let paths = [];
traverse(obj, (key, value, scope) => {
if (typeof value !== 'object') {
paths.push(scopeToPath(scope.concat(key)));
}
});
return paths;
};
console.log(findObjectPaths(getData()));
function getData() {
return {
"input": {
"test": {
"phone": [{ "phone1": "123" }, { "a": "456" }]
}
},
"customer": [{ "lastname": "def", "firstname": "abc" }]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
You could take a nested approach by having a look to the types of the object.
function flat(object, keys = '') {
if (!object || typeof object !== 'object') return [keys];
if (Array.isArray(object))
return object.every(o => !o|| typeof o!== 'object')
? [keys]
: object.flatMap((o, i, { length }) =>
flat(o, `${keys}[${length === 1 ? '' : i}]`));
return Object
.entries(object)
.flatMap(([k, v]) => flat(v, `${keys}${keys && '.'}${k}`));
}
var data = { input: { test: { phone: [{ phone1: ["123456"] }, { a: ["1", "2", "3", "4"] }] } }, customer: [{ lastname: "def", firstname: "abc" }] },
result = flat(data);
console.log(result);

Grouping the Json object with multiple objects

I need to group by sector and company-name of below json object.
original JSON object:
data =[
{"sector":"IT",
"company-name": "ABC",
"contact": "Person1",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC",
"contact": "Person2",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC2",
"contact": "Person1",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC2",
"contact": "Person2",
"position":"Accountant"
},
{"sector":"IT",
"company-name": "ABC2",
"contact": "Person3",
"position":"Accountant"
},
{"sector":"Finance",
"company-name": "Fin1",
"contact": "Person3",
"position":"Accountant"
}
]
output:
I could able to group by one i.e. company-name.Not how to extend this dynamically for multiple groups.
const result = {};
const groubyfiled = 'company-name'
this.data.foreach((item: any) => {
const gname = item[groubyfiled];
result[gname] = result[gname] || {};
result[gnmae].items = result[gname].items || [];
result[gname].items.push(item);
});
output:
{
"IT": {
"ABC": {...},
"ABC2": {...}
},
"Finance" : {
"Fin1":{....}
}
}
You can write a function that groups items by a single key.
function groupBySingleField(data, field){
return data.reduce((acc, val) => {
const rest = Object.keys(val).reduce((newObj, key) => {
if(key !== field){
newObj[key] = val[key]
}
return newObj;
}, {});
if (acc[val[field]]) {
acc[val[field]].push(rest);
} else {;
acc[val[field]] = [rest];
}
return acc;
}, {})
}
And then write a recursive function to additionally group already grouped items with a different key.
function groupByMultipleFields(data,...fields){
if(fields.length === 0 ) return;
let newData = {};
const [field] = fields;
newData = groupBySingleField(data, field);
const remainingFields = fields.slice(1);
if(remainingFields.length > 0){
Object.keys(newData).forEach((key) => {
newData[key] = groupByMultipleFields(newData[key],...remainingFields)
})
}
return newData;
}
You can try the code snippet here itself, to see if this is the result you are expecting
data = [
{
sector: "IT",
"company-name": "ABC",
contact: "Person1",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC",
contact: "Person2",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC2",
contact: "Person1",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC2",
contact: "Person2",
position: "Accountant"
},
{
sector: "IT",
"company-name": "ABC2",
contact: "Person3",
position: "Accountant"
},
{
sector: "Finance",
"company-name": "Fin1",
contact: "Person3",
position: "Accountant"
}
];
function groupBySingleField(data, field){
return data.reduce((acc, val) => {
const rest = Object.keys(val).reduce((newObj, key) => {
if(key !== field){
newObj[key] = val[key]
}
return newObj;
}, {});
if (acc[val[field]]) {
acc[val[field]].push(rest);
} else {;
acc[val[field]] = [rest];
}
return acc;
}, {})
}
console.log("Grouping by single fields");
console.log("Grouping by sector");
console.log(groupBySingleField(data, "sector"));
console.log("Grouping by company-name");
console.log(groupBySingleField(data, "company-name"));
function groupByMultipleFields(data,...fields){
if(fields.length === 0 ) return;
let newData = {};
const [field] = fields;
newData = groupBySingleField(data, field);
const remainingFields = fields.slice(1);
if(remainingFields.length > 0){
Object.keys(newData).forEach((key) => {
newData[key] = groupByMultipleFields(newData[key],...remainingFields)
})
}
return newData;
}
console.log("Grouping by multiple fields");
console.log("Grouping by company-name and position");
console.log(groupByMultipleFields(data,"company-name", "position"));
console.log("Grouping by position");
console.log(groupByMultipleFields(data,"position"));
console.log("Grouping by sector, company-name and position");
console.log(groupByMultipleFields(data,"sector", "company-name", "position"));
To get the output you're looking for, you could use something like this:
const result = {};
// loop through each object in data array
data.forEach(function(obj){
// create sector property if it doesn't exist
if (!result.hasOwnProperty(obj.sector)) result[obj.sector] = {};
// create company_name property within sector if it doesn't exist
if (!result[obj.sector].hasOwnProperty(obj.company_name)) result[obj.sector][obj.company_name] = {}
// set contact and position properties
result[obj.sector][obj.company_name]['contact'] = obj.contact;
result[obj.sector][obj.company_name]['position'] = obj.position;
});
console.log(result);
This code relies on each object in your data array having the same structure and properties. I'm not sure if you need your code to be more flexible.
Please check out the comments in the code to see exactly how it works.
Also, one important change is that you cannot use a hyphen in your property names. JS may interpret those as a minus sign when you're accessing properties. For that reason, you'll need to repace company-name with company_name in your data array for this code to work!

Group by using same key and join value with comma

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);

Categories