Related
Problem
I would like to have the below two JSON combined together using the ID and have the expected result as mentioned below. I have tried a few solutions that were available but none worked for my use case. Any suggestions will be great !!
Tried to do:
How to merge two json object values by id with plain Javascript (ES6)
Code
var json1 = [
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes"
},
{
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
},
{
"id":"A789",
"cost":"3423.04",
"fruitName":"banana"
}
];
var json2 = [
{
"id":"A123",
"quantity":"7"
},
{
"id":"A789",
"quantity":"10"
},
{
"id":"ABCD",
"quantity":"22"
}
];
Below is the code I tried:
var finalResult = [...[json1, json2].reduce((m, a) => (a.forEach(o => m.has(o.id) && Object.assign(m.get(o.id), o) || m.set(o.id, o)), m), new Map).values()];
Expected result:
[
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes",
"quantity":"7"
},
{
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
},
{
"id":"A789",
"cost":"3423.04",
"fruitName":"banana",
"quantity":"10"
},
{
"id":"ABCD",
"quantity":"22"
}
]
You can accomplish this fairly easily without getting too fancy. Here's the algorithm:
Put the items from json1 into an object by id, so that you can look them up quickly.
For each item in json2: If it already exists, merge it with the existing item. Else, add it to objectsById.
Convert objectsById back to an array. I've used Object.values, but you can also do this easily with a loop.
var json1 = [
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes"
}, {
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
}, {
"id":"A789",
"cost":"3423.04",
"fruitName":"banana"
}
];
var json2 = [
{
"id":"A123",
"quantity":"7"
}, {
"id":"A789",
"quantity":"10"
}
];
const objectsById = {};
// Store json1 objects by id.
for (const obj1 of json1) {
objectsById[obj1.id] = obj1;
}
for (const obj2 of json2) {
const id = obj2.id;
if (objectsById[id]) {
// Object already exists, need to merge.
// Using lodash's merge because it works for deep properties, unlike object.assign.
objectsById[id] = _.merge(objectsById[id], obj2)
} else {
// Object doesn't exist in merged, add it.
objectsById[id] = obj2;
}
}
// All objects have been merged or added. Convert our map to an array.
const mergedArray = Object.values(objectsById);
I think a few steps are being skipped in your reduce function. And it was a little difficult to read because so many steps are being combined in one.
One critical piece that your function does not account for is that when you add 2 numerical strings together, it concats the strings.
const stringTotal = "5020.67" + "3423.04" // result will be "5020.673423.04"
The following functions should give you the result you are looking for.
// calculating the total cost
// default values handles cases where there is no obj in array 2 with the same id as the obj compared in array1
const calcualteStringTotal = (value1 = 0, value2 = 0) => {
const total = parseFloat(value1) + parseFloat(value2)
return `${total}`
}
const calculateTotalById = (array1, array2) => {
const result = []
// looping through initial array
array1.forEach(outterJSON => {
// placeholder json obj - helpful in case we have multiple json in array2 with the same id
let combinedJSON = outterJSON;
// looping through second array
array2.forEach(innerJSON => {
// checking ids
if(innerJSON.id === combinedJSON.id) {
// calls our helper function to calculate cost
const updatedCost = calcualteStringTotal(innerJSON.cost, outterJSON.cost)
// updating other properties
combinedJSON = {
...outterJSON,
...innerJSON,
cost: updatedCost
}
}
})
result.push(combinedJSON)
})
return result
}
const combinedResult = calculateTotalById(json1, json2)
I figured that by using reduce I could make it work.
var finalResult = [...[json1, json2].reduce((m, a) => (a.forEach(o => m.has(o.id) && Object.assign(m.get(o.id), o) || m.set(o.id, o)), m), new Map).values()];
I do have an array with objects. Some of the objects properties have to be renamed.
So I have a function which gives me the desired result and I surely know about switch cases but I guess there must be a way less laborius way to do so. So I'm wondering what's the correct way for a shorthand technique to rename some property values and keep the ones which don't have to be changed:
renameMinMaxActual(data) {
const newArray = data.map(element => {
if (element.key.includes('Min')) {
element.key = element.key.replace('Min', '.min');
return element;
}
if (element.key.includes('Max')) {
element.key = element.key.replace('Max', '.max');
return element;
}
if (element.key.includes('Actual')) {
element.key = element.key.replace('Actual', '.actual');
return element;
} else {
return element;
}
});
console.log('newArray', newArray);
}
Indeed there are many ways to minimize the code - you mention 'shorthand' - here's one. I'm interested to see what others offer.
With this approach, the set of keys to transform are defined in keyTransforms, which makes it quite easy to add new keys without changing the transformKeys function.
// Define all keys to transform
const keyTransforms = {
Min: '.min',
Max: '.max',
Actual: '.actual'
};
// transformKeys replaces all keys found in data according to keyTransforms object.
function transformKeys(data) {
return data.map(element => {
// replace if match found, otherwise no change.
element.key = keyTransforms[element.key] || element.key;
return element;
});
}
// sample input data
const data = [{
key: 'Min'
}, {
key: 'Max'
}, {
key: 'Actual'
}];
const result = transformKeys([...data]);
console.log(result);
Create two arrays
fromKeys(present keys) fromKeys=['Min','Max','Actual'];
and toKeys(modified keys) toKeys=['.min','.max','.actual'];
While iterating through entries if key is present in fromKeys get the index and use the same key to get the value from toKeys array and set it in the object to be returned in map() function.
let data=[{Min:0,Max:1,Actual:5,type:"A"},{Min:0,Max:1,type:"B"},{Min:0,Max:1,type:"C"},{Min:0,Actual:5,type:"D"},{Max:1,Actual:5,type:"E"}];
let fromKeys=['Min','Max','Actual'];
let toKeys=['.min','.max','.actual'];
console.log(data.map(obj => {
let newObj={};
for (const [key,value] of Object.entries(obj)){
let index=fromKeys.indexOf(key);
if(index!=-1)newObj[toKeys[index]]=value;
else newObj[key]=value;
}
return newObj;
}));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm looping trough 2-dimensional objects inside an array. I currently do this the following way:
My array looks like this
var myarray = [
0: {
child_obj: {}
}
1: {//etc}
];
And I loop through the second-level objects like this
jQuery.each(myarray, function(i, first) {
jQuery.each(first.child_obj, function(j, second) {
//do stuff
}
});
});
So that's a loop inside a loop. It works fine, but it doesn't look very neat and I feel there might be a better (and shorter) way to do this.
The reason I'm doing this is because I need to do stuff with all child_objs.
Worth mentioning:
I use jQuery.each() because this allows looping through objects, while for(), .map() etc. can't handle that properly.
I can't change the structure of the array or its contents
I don't need to use the indexes (args i and j).
Is there a better way?
If you want to ditch jquery (and it's slow speed in .each) and use ES2015+
var myarray = [
{
child_obj: {a:1,b:2,c:3}
},
{
child_obj: {a:4,b:5,c:6},
child_obj2: {a:7,b:8,c:9}
}
];
// specific rewrite of your code would be
myarray.forEach(obj => Object.values(obj.child_obj).forEach(value => {
console.log(value);
}));
console.log('-------');
// other examples
myarray.forEach(obj => Object.values(obj).forEach(value => {
// do things with each "child object"
console.log(value);
}));
myarray.forEach(obj => Object.values(obj).forEach(child => Object.values(child).forEach(value => {
// do things with each property in each child object
console.log(value);
})));
It's not a better way, it's more like alternate.
for (var i = 0; i < myarray.length; i++)
{
var child_obj = myarray[i].child_obj;
// get the keys of this object
var keys = Object.keys(child_obj);
// loop all those keys
for (var keyi = 0; keyi < keys.length; keyi++)
{
var key = keys[keyi];
// get the objects item based on key;
var item = child_obj[key];
}
}
but here you can change their values directly as you are iterating the original vars.
hope that helps
using underscore-js library, you can do the following:
var first = _.map(myarray, element => { return element.child_obj; });
_.each(first, element => {/*do stuff*/});
You could use forEach with a for in loop inside::
myArray.forEach(function(obj){
for(var i in obj){
// do stuff
}
})
Naive recursive approach can be used for primitive types:
function forEachPrimitive(o, f, k) { if (o !== Object(o)) f(k, o)
else for (k in o) forEachPrimitive(o[k], f, k) }
var obj = [ { x: { a: '0', b: true, c: 2 } },
{ y: { d: /3/, e: null, f: undefined } } ]
forEachPrimitive(obj, console.log)
var addObjectResponse = [{
'SPO2': '222.00000',
'VitalGroupID': 1152,
'Temperature': 36.6666666666667,
'DateTimeTaken': '/Date(1301494335000-0400)/',
'UserID': 1,
'Height': 182.88,
'UserName': 'Admin',
'BloodPressureDiastolic': 80,
'Weight': 100909.090909091,
'TemperatureMethod': 'Oral',
'Resprate': 111,
'HeartRate': 111,
'BloodPressurePosition': 'Standing',
'VitalSite': 'Popliteal',
'VitalID': 1135,
'Laterality': 'Right',
'HeartRateRegularity': 'Regular',
'HeadCircumference': '',
'BloodPressureSystolic': 120,
'CuffSize': 'XL',
}];
How to rename the keys... like SPO2 into O2... there are such many objects in the array...
maybe something like this?
var i, len = addObjectResponse.length;
for (i = 0; i < len; i++) {
addObjectResponse[i]['O2'] = addObjectResponse[i]['SPO2'];
delete addObjectResponse[i]['SPO2'];
}
or
addObjectResponse = addObjectResponse.map(function (obj) {
obj['O2'] = obj['SP02'];
delete obj['S02'];
return obj;
});
or
for (let obj of addObjectResponse) {
obj['O2'] = obj['SP02'];
delete obj['S02'];
}
or
function renameProperty(obj, fromKey, toKey) {
obj[toKey] = obj[fromKey];
delete obj[fromKey];
}
addObjectResponse.forEach(obj => renameProperty(obj, 'SP02', 'O2'));
You cannot directly rename the properties. However, you can set new properties and unset the old ones, indirectly "renaming" them:
function rename(obj, oldName, newName) {
if(!obj.hasOwnProperty(oldName)) {
return false;
}
obj[newName] = obj[oldName];
delete obj[oldName];
return true;
}
Immutable key renaming in vanilla JS one-liner
This may not be the most efficient way to rename a key but I think it's interesting in certain ways:
It doesn't mutate the original objects
It takes one line of vanilla JavaScript
It demonstrates the use of modern syntax
No.1 may sometimes be needed if you still need to use the original array.
No.2 may be interesting considering the fact that some of the examples here have more than 30 lines of code.
No.3 may serve an educational purpose to demostrate some of the features of the language that are not used as often as they should, considering the fact how powerful and how widely supported they are.
If you create a mapping object like this:
const m = { SPO2: 'O2' };
then you'll be able to add more keys to rename in the future easily.
Now, can create a one-liner in vanilla JS:
const t = o => Object.assign(...Object.keys(o).map(k => ({ [m[k] || k]: o[k] })));
Let's say that you have an array of objects:
const a = [{
'SPO2': '222.00000',
'VitalGroupID': 1152,
}, {
'SPO2': '333.00000',
'VitalGroupID': 1153,
}, {
'SPO2': '444.00000',
'VitalGroupID': 1154,
}];
You can get a new array with a.map(t) like this:
console.log('Before:', a);
console.log('After:', a.map(t));
Your original objects are still intact in the original array.
I have created a nice function to rename properties names: https://github.com/meni181818/simpleCloneJS/blob/master/renameProperties.js
usage:
var renamedObj = renameProperties(sourceObject, {propName: 'propNEWname', anotherPropName: 'anotherPropNEWname'});
My function, also handles objects inside arrays so in your case you can do:
addObjectResponse = renameProperties(addObjectResponse, {SPO2: 'O2'});
DEMO
function renameProperties(sourceObj, replaceList, destObj) {
destObj = destObj || {};
each(sourceObj, function(key) {
if(sourceObj.hasOwnProperty(key)) {
if(sourceObj[key] instanceof Array) {
if(replaceList[key]) {
var newName = replaceList[key];
destObj[newName] = [];
renameProperties(sourceObj[key], replaceList, destObj[newName]);
} else if(!replaceList[key]) {
destObj[key] = [];
renameProperties(sourceObj[key], replaceList, destObj[key]);
}
} else if(typeof sourceObj[key] === 'object') {
if(replaceList[key]) {
var newName = replaceList[key];
destObj[newName] = {};
renameProperties(sourceObj[key], replaceList, destObj[newName]);
} else if(!replaceList[key]) {
destObj[key] = {};
renameProperties(sourceObj[key], replaceList, destObj[key]);
}
} else {
if(replaceList[key]) {
var newName = replaceList[key];
destObj[newName] = sourceObj[key];
} else if(!replaceList[key]) {
destObj[key] = sourceObj[key];
}
}
}
});
return destObj;
}
on line 3 in the function above, we using each() function. which is this:
function each(objOrArr, callBack) {
if(objOrArr instanceof Array) {
for(var i = 0; i < objOrArr.length; i++) {
callBack(i);
}
} else if(typeof objOrArr === 'object') {
for(var prop in objOrArr) {
// if the property really exist
if(objOrArr.hasOwnProperty(prop)) {
callBack(prop);
}
}
}
}
note: If you are using Jquery OR underscore.js Or another library that has 'each()' function, you can use it Instead. just replece to $.each (jquery) or _.each (underscore.js).
Ok, so there's two things you're doing here, iterating through an array and renaming properties of an object.
Firstly, to itterate you should generally be using the arrays map() function.
It's less error prone than using a for ( .. ) loop and slightly nicer than forEach(), I think.
A for ( .. ) loop will usually give you better performance (depending on the JS engine) but you need to be dealing with pretty massive array to notice (ie. maybe a ~10ms difference for 100k elements).
Secondly, to rename a object property, the obvious solution is to just set the new key and deleting the old.
This will work but won't always give you properties that behave exactly like the old one if a custom getter or setter has been defined.
If you're creating a generic helper function to do this kind of work you'd be better off using
Object.defineProperty() and
Object.getOwnPropertyDescriptor().
Putting this together we get:
function renameKeyInObjArray (array, oldKey, newKey) {
return array.map(function (obj) {
Object.defineProperty(obj, newKey, Object.getOwnPropertyDescriptor(obj, oldKey));
delete obj[oldKey];
return obj;
});
}
// Use our new function to process the array
renameKeyInObjArray(addObjectResponse, 'SPO2', 'O2');
This function updates the contents of the array by reference and also returns a reference to the array, so can be chained. It's also written in ES5.1 syntax so should run pretty much everywhere.
Here's one that works over an array of objects and takes a map of old object keys to new object keys.
I mostly copied the very nice code from here and just made it operate over arrays of objects rather than a single one.
Code
const renameKeys = (keysMap, objArr) =>
(renamedArr = objArr.map((obj) =>
Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] },
}),
{}
)
));
Example
renameKeys({ tWo: 'two', FreE: 'three' }, [
{ one: 1, tWo: 2, three: 3 },
{ one: 100, two: 200, FreE: 300 },
]);
[ { one: 1, two: 2, three: 3 }, { one: 100, two: 200, three: 300 } ]
You can add + delete (read the IE caveat);
var addObjectResponse = [{
'SPO2': '222.00000',
'VitalGroupID': 1152
}]
for (var k in addObjectResponse[0])
log(k)
>>SPO2
>>VitalGroupID
addObjectResponse[0]['O2'] = addObjectResponse[0]['SPO2']
delete addObjectResponse[0]['SPO2']
for (var k in addObjectResponse[0])
log(k)
>>VitalGroupID
>>O2
addObjectResponse[0]["O2"] = addObjectResponse[0]["SPO2"];
addObjectResponse[0]["SP02"] = null;
The [0] is necessary because addObjectResponse is set to an array with one element, which contains an object. Do you have any rules as to what keys will be renamed or how?
Edit: I misunderstood the OP, thinking that "many objects" referred to many keys in the object that need to be renamed, as opposed to many objects in the array that each need to have that one key renamed.
Instead of renaming this object key, you could create another object with proper names, like this:
var obj={wrongKeyName:'test'};
var obj2 = {}
obj2.rightKeyName = obj.wrongKeyName;
console.log(obj2);
A little late to the game here but how about something like this:
const newAddObjectResponse = addObjectResponse.map((obj) => {
const {SPO2: O2, ...rest} = obj
return {O2, ...rest}
})
If you want to replace your original array then you could do:
let addObjectResponse = [
{
SPO2: '222.00000',
VitalGroupID: 1152,
Temperature: 36.6666666666667,
DateTimeTaken: '/Date(1301494335000-0400)/',
UserID: 1,
Height: 182.88,
UserName: 'Admin',
BloodPressureDiastolic: 80,
Weight: 100909.090909091,
TemperatureMethod: 'Oral',
Resprate: 111,
HeartRate: 111,
BloodPressurePosition: 'Standing',
VitalSite: 'Popliteal',
VitalID: 1135,
Laterality: 'Right',
HeartRateRegularity: 'Regular',
HeadCircumference: '',
BloodPressureSystolic: 120,
CuffSize: 'XL',
},
]
addObjectResponse = addObjectResponse.map((obj) => {
const {SPO2: O2, ...rest} = obj
return {O2, ...rest}
})
Consider:
var object = {
foo: {},
bar: {},
baz: {}
}
How would I do this:
var first = object[0];
console.log(first);
Obviously, that doesn’t work because the first index is named foo,
not 0.
console.log(object['foo']);
works, but I don’t know it’s named foo. It could be named anything. I just want the first.
Just for fun this works in JS 1.8.5
var obj = {a: 1, b: 2, c: 3};
Object.keys(obj)[0]; // "a"
This matches the same order that you would see doing
for (o in obj) { ... }
If you want something concise try:
for (first in obj) break;
alert(first);
wrapped as a function:
function first(obj) {
for (var a in obj) return a;
}
they're not really ordered, but you can do:
var first;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof(i) !== 'function') {
first = obj[i];
break;
}
}
the .hasOwnProperty() is important to ignore prototyped objects.
This will not give you the first one as javascript objects are unordered, however this is fine in some cases.
myObject[Object.keys(myObject)[0]]
If the order of the objects is significant, you should revise your JSON schema to store the objects in an array:
[
{"name":"foo", ...},
{"name":"bar", ...},
{"name":"baz", ...}
]
or maybe:
[
["foo", {}],
["bar", {}],
["baz", {}]
]
As Ben Alpert points out, properties of Javascript objects are unordered, and your code is broken if you expect them to enumerate in the same order that they are specified in the object literal—there is no "first" property.
for first key of object you can use
console.log(Object.keys(object)[0]);//print key's name
for value
console.log(object[Object.keys(object)[0]]);//print key's value
There is no way to get the first element, seeing as "hashes" (objects) in JavaScript have unordered properties. Your best bet is to store the keys in an array:
var keys = ["foo", "bar", "baz"];
Then use that to get the proper value:
object[keys[0]]
ES6
const [first] = Object.keys(obj)
Using underscore you can use _.pairs to get the first object entry as a key value pair as follows:
_.pairs(obj)[0]
Then the key would be available with a further [0] subscript, the value with [1]
I had the same problem yesterday. I solved it like this:
var obj = {
foo:{},
bar:{},
baz:{}
},
first = null,
key = null;
for (var key in obj) {
first = obj[key];
if(typeof(first) !== 'function') {
break;
}
}
// first is the first enumerated property, and key it's corresponding key.
Not the most elegant solution, and I am pretty sure that it may yield different results in different browsers (i.e. the specs says that enumeration is not required to enumerate the properties in the same order as they were defined). However, I only had a single property in my object so that was a non-issue. I just needed the first key.
You could do something like this:
var object = {
foo:{a:'first'},
bar:{},
baz:{}
}
function getAttributeByIndex(obj, index){
var i = 0;
for (var attr in obj){
if (index === i){
return obj[attr];
}
i++;
}
return null;
}
var first = getAttributeByIndex(object, 0); // returns the value of the
// first (0 index) attribute
// of the object ( {a:'first'} )
To get the first key of your object
const myObject = {
'foo1': { name: 'myNam1' },
'foo2': { name: 'myNam2' }
}
const result = Object.keys(myObject)[0];
// result will return 'foo1'
Based on CMS answer. I don't get the value directly, instead I take the key at its index and use this to get the value:
Object.keyAt = function(obj, index) {
var i = 0;
for (var key in obj) {
if ((index || 0) === i++) return key;
}
};
var obj = {
foo: '1st',
bar: '2nd',
baz: '3rd'
};
var key = Object.keyAt(obj, 1);
var val = obj[key];
console.log(key); // => 'bar'
console.log(val); // => '2nd'
My solution:
Object.prototype.__index = function(index)
{
var i = -1;
for (var key in this)
{
if (this.hasOwnProperty(key) && typeof(this[key])!=='function')
++i;
if (i >= index)
return this[key];
}
return null;
}
aObj = {'jack':3, 'peter':4, '5':'col', 'kk':function(){alert('hell');}, 'till':'ding'};
alert(aObj.__index(4));