How to replace JSON object kyes - javascript

I have two arrays:
[{
key: 'first_name',
value: 'FirstName'
}, {
key: 'middle_name',
value: 'MiddleName'
}, {
key: 'age',
value: 'Age'
}]
And:
[{
FirstName: "Mayuresh",
MiddleName: "Dinkar ",
LastName: "Joshi",
Age: 4
},
{
FirstName: "Arun",
MiddleName: "Vikas",
LastName: "Pathak",
Age: 25
},
{
FirstName: "Narendra",
MiddleName: "Damodardas",
LastName: "Modi",
Age: 50
}
]
in angular i want to compare both array and replace first object key with 2nd object key
i tried below code but it is not working
var updatedBooks = JSON.parse(parsed, function (key, value) {
// if key is `Number`, change it to ISBN
let data = _.find(arr, function (o) { return o.value==key });
//console.log("data",data);
if (key === data.value) {
data.key = value;
} else {
return value;
}
});
Expected output:
[{
first_name: "Mayuresh",
middle_name: "Dinkar ",
LastName: "Joshi",
age: 24
}, {
first_name: "Arun",
middle_name: "Vikas",
LastName: "Pathak",
age: 25
}, {
first_name: "Narendra",
middle_name: "Damodardas",
LastName: "Modi",
age: 50
}]

Use map with find and Object.entries:
const arr = [{key:'first_name',value:'FirstName'},{key:'middle_name',value:'MiddleName'},{key:'age',value:'Age'}];
const data = [{FirstName:"Mayuresh",MiddleName:"Dinkar ",LastName:"Joshi",Age:4},{FirstName:"Arun",MiddleName:"Vikas",LastName:"Pathak",Age:25},{FirstName:"Narendra",MiddleName:"Damodardas",LastName:"Modi",Age:50}];
const res = data.map(elem => {
let temp = {};
Object.entries(elem).forEach(([k, v]) => {
let newKey = arr.find(({ value }) => value == k);
if (newKey) temp[newKey.key] = v;
else temp[k] = v;
});
return temp;
});
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: auto; }

The callback of JSON.parse just works on values. Therefore in your case the value you want to work with is the object, not the objects key-value pairs:
const result = JSON.parse(data, (key, value) => {
if(typeof value !== "object" || value === null || Array.isArray(value))
return value;
const result = {};
for(const [key, value] of Object.entries(value)) {
const replaceKey = (keys.find(it => it.value === key) || {}).key;
result[replaceKey || key] = value;
}
return result;
});

Create a Map of value -> key from the keys array. Then map the data, and use _.mapValues() to replace the keys of the objects:
const keys = [{"key":"first_name","value":"FirstName"},{"key":"middle_name","value":"MiddleName"},{"key":"age","value":"Age"}]
const data = [{"FirstName":"Mayuresh","MiddleName":"Dinkar ","LastName":"Joshi","Age":4},{"FirstName":"Arun","MiddleName":"Vikas","LastName":"Pathak","Age":25},{"FirstName":"Narendra","MiddleName":"Damodardas","LastName":"Modi","Age":50}]
const keysMap = new Map(keys.map(o => [o.value, o.key]))
const result = data.map(o => _.mapKeys(o, (v, k) => keysMap.get(k) || k))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
And the same idea with lodash/fp:
const { map, props, mapKeys } = _
const keys = [{"key":"first_name","value":"FirstName"},{"key":"middle_name","value":"MiddleName"},{"key":"age","value":"Age"}]
const data = [{"FirstName":"Mayuresh","MiddleName":"Dinkar ","LastName":"Joshi","Age":4},{"FirstName":"Arun","MiddleName":"Vikas","LastName":"Pathak","Age":25},{"FirstName":"Narendra","MiddleName":"Damodardas","LastName":"Modi","Age":50}]
const keysMap = new Map(map(props(['value', 'key']), keys))
const result = map(mapKeys(k => keysMap.get(k) || k), data)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>

var arr1 = [{
key: 'first_name',
value: 'FirstName'
}, {
key: 'middle_name',
value: 'MiddleName'
}, {
key: 'age',
value: 'Age'
}];
var arr2 = [{
FirstName: "Mayuresh",
MiddleName: "Dinkar ",
LastName: "Joshi",
Age: 4
},
{
FirstName: "Arun",
MiddleName: "Vikas",
LastName: "Pathak",
Age: 25
},
{
FirstName: "Narendra",
MiddleName: "Damodardas",
LastName: "Modi",
Age: 50
}
];
Let's take an array and put the first array value as key and key as value!!
var arr1Obj = {};
arr1.forEach(e => arr1Obj[e.value] = e.key);
Now the object that we create above looks somewhat like following
var result = [];
Now it's time to iterate over the second array.
arr2.forEach(elem => {
var obj = {};
// here elem is an object having properties,
// iterate over it as well for changing the prop nam
Object.keys(elem).forEach(key => {
var propKey = arr1Obj[key];
if(propKey) {
obj[propKey] = elem[key];
} else {
obj[key] = elem[key];
}
});
result.push(obj);
})
console.log(result)

Related

How to compare two objects but skip values and just compare keys and key types?

I want to write unit testing using Jest for my Node.js functions and need to test if response object has some specific keys or not. Something like this:
expect(await mokChannelsService.getChannel(inputDto)).toEqualKeys(outputDto);
// Or just a normal function
const result = await mokChannelsService.getChannel(inputDto);
const equal = isEqualKeys(result, outputDto);
This toEqualKeys function should check equality just for keys not values. And these objects are not instances of a class. for example these two objects should be equal:
const object1 = {
names: {
firstName: '',
lastName: '',
},
phone: 1,
};
const object2 = {
names: {
firstName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const object3 = {
names: {
firstName: 'John',
lastName: 12,
},
mobile: '12345',
};
const result1 = toEqualKeys(object1, object2); // true
const result1 = toEqualKeys(object1, object3); // false
Is there any NPM packages available for this?
You can iterate each of the keys in object1, checking that the same key exists in object2 and - if the associated property in object1 is an object, that all the keys from that object also exist in a similar object in object2:
const object1 = {
names: {
firstName: '',
lastName: '',
},
phone: 1,
};
const object2 = {
names: {
firstName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const object3 = {
names: {
firstName: 'John',
lastName: 12,
},
mobile: '12345',
};
const toEqualKeys = (obj1, obj2) => Object.keys(obj1)
.every(k => k in obj2 &&
(typeof obj1[k] != 'object' || typeof obj2[k] == 'object' && toEqualKeys(obj1[k], obj2[k]))
)
console.log(toEqualKeys(object1, object2))
console.log(toEqualKeys(object1, object3))
You can get more detail on the differences by logging differences into an array (using reduce to iterate the keys in the reference object). This function will return an array of errors (empty if there are none) for each comparison:
const object1 = {
names: {
firstName: '',
lastName: '',
},
phone: 1,
};
const object2 = {
names: {
firstName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const object3 = {
names: {
firstName: 'John',
lastName: 12,
},
mobile: '12345',
};
const object4 = {
names: 'John',
mobile: '12345',
};
const object5 = {
names: {
firstName: 'John',
lastName: 12,
},
phone: {
landline : '12345',
mobile : '555'
}
};
const object6 = {
names: {
firtName: 'John',
lastName: 'Due',
},
phone: 1234567890,
};
const notEqualKeys = (obj1, obj2, prefix = '') => Object.keys(obj1)
.reduce((acc, k) => {
if (!(k in obj2)) acc.push(`key ${k} not present in obj2${prefix}`)
if (typeof obj1[k] == 'object') {
if (typeof obj2[k] == 'object') {
acc = acc.concat(notEqualKeys(obj1[k], obj2[k], `${prefix}.${k}`))
}
else {
acc.push(`${k} is an object in obj1${prefix} but a value in obj2${prefix}`)
}
}
else if (typeof obj2[k] == 'object') {
acc.push(`${k} is a value in obj1${prefix} but an object in obj2${prefix}`)
}
return acc
}, [])
console.log((arr = notEqualKeys(object1, object2)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object3)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object4)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object5)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object6)).length ? arr : true)

Compare objects within an array and create a new array without duplicates

I've an array of objects:
[
{ name: "John", age: "34" },
{ name: "Ace", age: "14" },
{ name: "John", age: "45" },
{ name: "Harry", age: "11" },
]
I want to compare the objects within an array by name. If the duplicate name exists, I should compare the age and only keep the higher age object.
The expected output should be:
[
{ name: "Ace", age: "14" },
{ name: "John", age: "45" },
{ name: "Harry", age: "11" },
]
I am new to javascript/typescript and couldn't find any optimal solution for this problem. I hope, I was able to explain my problem clearly.
Thanks.
The next provided approach uses reduce and creates in a first step just an index/map of items of highest age which are each unique by name. Thus one could use the temporary state of the programmatically built result as lookup for already existing named items.
Within a second step one would retrieve the array of unique named items of highest age by passing such an index to Object.values.
function collectHighestAgeItemOfSameName(result, item) {
const { name, age } = item;
if (
!(name in result) ||
Number(result[name].age) < Number(age)
) {
result[name] = item;
}
return result;
}
const sampleData = [{
name: "John",
age: "34"
}, {
name: "Ace",
age: "14"
}, {
name: "Harry",
age: "9"
}, {
name: "John",
age: "45"
}, {
name: "Harry",
age: "11"
}, {
name: "Ace",
age: "13"
}];
console.log(
'reduced index of unique person items of highest age ...',
sampleData
.reduce(collectHighestAgeItemOfSameName, {})
)
console.log(
'array of unique person items of highest age ...',
Object
.values(
sampleData
.reduce(collectHighestAgeItemOfSameName, {})
)
)
.as-console-wrapper { min-height: 100%!important; top: 0; }
Maybe something like that
const obj = [{ name: "John", age: "34" }, { name: "Ace", age: "14" }, { name: "John", age: "45" }, { name: "Harry", age: "11" }];
const objCopy = JSON.parse(JSON.stringify(obj))
const res = objCopy.reduce((acc, obj) => {
const personExist = acc.find(({ name }) => name === obj.name);
if (personExist) {
if (parseInt(obj.age, 10) > parseInt(personExist.age, 10)) {
personExist.age = obj.age;
}
} else {
acc.push(obj);
}
return acc;
}, []);
console.log({ res });
.as-console-wrapper { min-height: 100%!important; top: 0; }
try this
var objArr=...your json object;
var maxValueGroup = "name";
var maxValueName = "age";
console.log(JSON.stringify(newArr(objArr,maxValueGroup, maxValueName)));
newArr
var newArr = function (objArr,maxValueGroup, maxValueName) {
var arr = groupBy(objArr, maxValueGroup);
var newArr = [];
$.each(arr, function (key) {
var maxVal = 0;
var maxValItem;
arr[key].forEach((element) => {
if (element[maxValueName] > maxVal) {
maxVal = element[maxValueName];
maxValItem = element;
}
});
newArr.push(maxValItem);
});
return newArr;
};
groupby
var groupBy = function (xs, key) {
return xs.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
This works basically the same as #PeterSeliger's fine and upvote-worthy answer, except it uses a Map object which is nice because Map.set returns the Map object, allowing you to return it as the accumulator for the next iteration of the reduce function.
const data = [{name: "John", age: "34"}, {name: "Ace", age: "14"}, {name: "John", age: "45"}, {name: "Harry", age: "11"}];
const res = [...data.reduce(
(acc, val) =>
+(acc.get(val.name)?.age ?? -1) >= +val.age ?
acc :
acc.set(val.name, val),
new Map()
).values()];
console.log(JSON.stringify( res ));
.as-console-wrapper { min-height: 100%!important; top: 0; }
Other references:
Unary plus (+)
Optional chaining (?.)
Nullish coalescing operator (??)
Conditional (ternary) operator
Array.prototype.reduce()
Spread syntax (...)

2 Array combine into one array javascript

Here is two array one is firstName another is lastName
{
firstName: ['name1', 'name2'],
lastName: ['last1', 'last2'],
}
but I want to formate the data into one array like this.
{
"name": [{
"firstName": "name1",
"lastName": "last1"
},
{
"firstName": "name2",
"lastName": "last2"
},
]
}
Using Array#map:
const getNameObj = (data = {}) => {
const { firstName = [], lastName = [] } = data;
if(firstName.length !== lastName.length) return;
const name = firstName.map((fn, i) => (
{ firstName: fn, lastName: lastName[i] }
));
return { name };
};
console.log(
getNameObj({ firstName: [ 'name1', 'name2' ], lastName: [ 'last1', 'last2' ] })
);
You could loop through it using for...in and push each element inside new array like this example
const data = {
firstName: [ 'name1', 'name2' ],
lastName: [ 'last1', 'last2' ],
}
let newData = [];
for (let el in data.firstName){
newData.push({
firstName: data.firstName[el],
lastName: data.lastName[el],
})
}
console.log(newData)
You can use Array.reduce to get the result.
const input = {
firstName: [ 'name1', 'name2' ],
lastName: [ 'last1', 'last2' ],
};
const result = input.firstName.reduce((acc, firstName, index) => {
acc.name.push({
firstName: firstName,
lastName: input.lastName[index]
})
return acc
}, {'name': []})
console.log(result);
You can build a new object {name: <value>}, and set your <value> to a mapped version of the firstName array, where each firstName from your array is mapped to an object of the form {firstName: ..., lastName: ...}. Here the firstName key has a value that is the current name, and the lastName key has a value from the lastName array at the corresponding index:
const obj = { firstName: ['name1', 'name2'], lastName: ['last1', 'last2'], };
const res = {
name: obj.firstName.map((firstName, i) => ({firstName, lastName: obj.lastName[i]}))
}
console.log(res);
here it is:
let obj = {firstName: ['name1', 'name2'],lastName: ['last1', 'last2']};
let finalObj = {};
finalObj.name = []
for (let i = 0; i < obj.firstName.length; i++)
finalObj.name.push(
{
firstName: obj.firstName[i],
lastName: obj.lastName[i]
}
)

How can I minimize a code function that finds repeated values in an array of objects in JavaScript?

I need to fix this function, which must find two similar names in an array of objects.
I tried to do this, and it's working, but the test tells me that it should be just only one loop and one if
function searchByName() {
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
for (let obj of values) {
for (let elem of values) {
if (obj == elem)
continue;
if (elem.name === obj.name && elem.age === obj.age) {
console.log(obj);
break;
}
}
}
};
Here is the example that must come out:
[
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Andrew', birthDate: '2011-05-09' }
]
Just push names to the array and stop when there is a duplicate:
let hasDuplicates = [];
values.forEach((e, idx) => {
if(!hasDuplicates.includes(e.name))
if(idx !== values.length-1) { hasDuplicates.push(e.name); }
else { hasDuplicates = false; }
else { hasDuplicates = e; }
});
And then you can use that variable:
if(hasDuplicates) {...}
You can find the duplication count by name key using Array.reduce function.
And from the duplication result, you can filter the duplicated ones only and show them using Array.filter & Array.map.
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
let duplicates = values.reduce((acc, cur) => {
if (acc[cur.name]) {
acc[cur.name].push(cur.birthDate);
} else {
acc[cur.name] = [ cur.birthDate ];
}
return acc;
}, {});
duplicates = Object.entries(duplicates).filter(([name, birth]) => birth.length > 1).map(([name, birth]) => {
return birth.map((item) => ({
name,
birthDate: item
}));
});
console.log(duplicates);
You can use the reduce method to iterate over the array and push items into the array. Then we just filter the array by its length. So if the length is greater than 1, it means we have found a duplicate:
const result = values.reduce((a, c)=> {
a[c.name] = a[c.name] || {...c, items: []} ;
a[c.name].items.push(c);
return a;
}, { });
let result = Object.values(result).filter(f => f.items.length > 1).flatMap(s => s.items);
An example:
const values = [
{ name: 'Johny Walker', birthDate: '1995-12-17' },
{ name: 'Andrew', birthDate: '2001-10-29' },
{ name: 'Viktor', birthDate: '1998-11-09' },
{ name: 'Andrew', birthDate: '2011-05-09' }
];
const result = values.reduce((a, c)=> {
a[c.name] = a[c.name] || {...c, items: []} ;
a[c.name].items.push(c);
return a;
}, { });
console.log(Object.values(result).filter(f => f.items.length > 1).flatMap(s => s.items));

Get sum of property values within array of objects

I am trying to get an object as output using JavaScript reduce function. It's working, if I define an object {ageTotal: 0} as the second argument. How can I implement this sum of age without defining a second argument property only using an empty object {}.
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
// output as a *int*
const sumAge = users.reduce((totals, current) => {
return totals + current.age;
}, 0);
console.log(sumAge);
// output as *object*
function getUserData (users) {
return users.reduce((data, user) => {
data.ageTotal += user.age
return data;
}, { ageTotal: 0 });
}
console.log(getUserData(users));
You may use short-circuit evaluation while incrementing to add current age value to accumulator property ageTotal (if it exists, or set it to 0, otherwise):
data.ageTotal = (data.ageTotal || 0) + user.age
Following is a quick demo:
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
// output as *object*
function getUserData (users) {
return users.reduce((data, user) => {
data.ageTotal = (data.ageTotal || 0) + user.age
return data;
}, {});
}
console.log(getUserData(users));
Or, if you seek to make your syntax more concise:
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
// output as *object*
const getUserData = users =>
users.reduce((data, {age}) =>
(data.ageTotal = (data.ageTotal || 0) + age, data), {});
console.log(getUserData(users));

Categories

Resources