I am using _.isEqual that compares 2 array of objects (ex:10 properties each object), and it is working fine.
Now there are 2 properties (creation and deletion) that i need not to be a part of comparison.
Example:
var obj1 = {name: "James", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
var obj2 = {name: "Maria", age: 17, creation: "13-02-2016", deletion: "13-04-2016"}
// lodash method...
_.isEqual(firstArray, secondArray)
You can use omit() to remove specific properties in an object.
var result = _.isEqual(
_.omit(obj1, ['creation', 'deletion']),
_.omit(obj2, ['creation', 'deletion'])
);
var obj1 = {
name: "James",
age: 17,
creation: "13-02-2016",
deletion: "13-04-2016"
};
var obj2 = {
name: "Maria",
age: 17,
creation: "13-02-2016",
deletion: "13-04-2016"
};
var result = _.isEqual(
_.omit(obj1, ['creation', 'deletion']),
_.omit(obj2, ['creation', 'deletion'])
);
console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
#ryeballar's answer is not great for large objects because you are creating a deep copy of each object every time you do the comparison.
It's better to use isEqualWith. For example, to ignore differences in the "creation" and "deletion" properties:
var result = _.isEqualWith(obj1, obj2, (value1, value2, key) => {
return key === "creation" || key === "deletion" ? true : undefined;
});
EDIT (important caveat pointed out in the comments): if objects have different numbers of keys, then isEqualWith considers them to be different, regadless of what your customizer does. Therefore do not use this approach if you want to ignore an optional property. Instead, consider using _.isMatch(), _.isMatchWith(), or #ryeballar's _.omit() approach.
Note that if you're writing for ES5 and earlier, you'll have to replace the arrow syntax (() => {) with function syntax (function() {)
_.omit creates deep copy of the object. If you need to exclude only root props it is better to create shallow copy using, for example, destructuring assignment:
const x = { a: 4, b: [1, 2], c: 'foo' }
const y = { a: 4, b: [1, 2], c: 'bar' }
const { c: xC, ...xWithoutC } = x
const { c: yC, ...yWithoutC } = y
_.isEqual(xWithoutC, yWithoutC) // true
xWithoutC.b === x.b // true, would be false if you use _.omit
Best way is not to create copies at all (TypeScript):
function deepEqual(
x?: object | null,
y?: object | null,
ignoreRootProps?: Set<string>
) {
if (x == null || y == null) return x === y
const keys = Object.keys(x)
if (!_.isEqual(keys, Object.keys(y)) return false
for (let key of keys) {
if (ignoreRootProps && ignoreRootProps.has(key)) continue
if (!_.isEqual(x[key], y[key])) return false
}
return true
}
You could map your array into a "cleaned" array, then compare those.
// Create a function, to do some cleaning of the objects.
var clean = function(obj) {
return {name: obj.name, age: obj.age};
};
// Create two new arrays, which are mapped, 'cleaned' copies of the original arrays.
var array1 = firstArray.map(clean);
var array2 = secondArray.map(clean);
// Compare the new arrays.
_.isEqual(array1, array2);
This has the downside that the clean function will need to be updated if the objects are expecting any new properties. It is possible to edit it so that it removes the two unwanted properties instead.
I see two options.
1) Make a second copy of each object that doesn't contain the creation or date.
2) Loop through all the properties and, assuming you know for certain that they both have the same properties, try something like this.
var x ={}
var y ={}
for (var property in x) {
if(property!="creation" || property!="deletion"){
if (x.hasOwnProperty(property)) {
compare(x[property], y[property])
}
}
}
Where compare() is some simple string or object comparison. If you are certain of the properties on one or both the objects, you can simplify this code a bit further, but this should work in most cases.
My final solution required a full comparison ignoring an optional property so the above solutions did not work.
I used a shallow clone to remove the keys I wanted to ignore from each object before comparing with isEqual:
const equalIgnoring = (newItems, originalItems) => newItems.length === originalItems.length
&& newItems.every((newItem, index) => {
const rest1 = { ...newItem };
delete rest1.creation;
delete rest1.deletion;
const rest2 = { ...originalItems[index] };
delete rest2.creation;
delete rest2.deletion;
return isEqual(rest1, rest2);
});
If you want to check a subset for each item in the array this works:
const equalIgnoringExtraKeys = (fullObjs, partialObjs) =>
fullObjs.length === partialObjs.length
&& fullObjs.every((fullObj, index) => isMatch(fullObj, partialObjs[index]));
If you also want to ignore a specific property and check subset:
const subsetIgnoringKeys = (fullObjs, partialObjs) =>
fullObjs.length === partialObjs.length
&& fullObjs.every((fullObj, index) => isMatchWith(
fullObj,
partialObjs[index],
(objValue, srcValue, key, object, source) => {
if (["creation", "deletion"].includes(key)) {
return true;
}
return undefined;
}
));
Related
I have any array of objects like this
let myObj=[{a:'CR',showMe: true},{a:'PY'}];
Now I'm trying to find the object which has a as CR and showMe as true and want to change the a value.
let findObj = myObj.filter(i=> i.a == 'CR' && i.showMe);
findObj.map(ele => ele['a'] = "PS");
When I'm trying to console myObj,value of a in myObj is changed along with findObj.
I don't want myObj to be changed.
What is causing the issue, could someone help?
You need to (shallow) clone the objects in findObj so that modifying them doesn't modify the objects in myObj
let myObj=[{a:'CR',showMe: true},{a:'PY'}];
let findObj = myObj.filter(i=> i.a == 'CR' && i.showMe);
findObj = findObj.map(obj => ({...obj, a: 'PS'}));
console.log(myObj);
console.log(findObj);
Other comments & answers suggest suggest using JSON.parse(JSON.strinfigy(obj)) to deep clone objects, but this is lossy; e.g. it loses types and methods. In your case, your objects are 1 level deep (i.e. don't contain nested arrays or objects), so a shallow clone is sufficient. The spread operator {...obj} is the simplest way to shallow clone objects. Object.assign({}, obj) is another more verbose alternative.
let myObj = [{
a: 'CR',
showMe: true
}, {
a: "FF",
showMe: true
}];
let result = [];
myObj.filter(i=> {
let item = Object.assign({}, i);
return item.a == 'CR' && item.showMe && result.push(item)
});
result.map(ele => { ele['a'] = "PS"});
console.log({myObj, result});
I want to reverse the mapping of an object (which might have duplicate values). Example:
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France'
};
reverseMapping(city2country) Should output:
{
'Netherlands': ['Amsterdam', 'Rotterdam'],
'France': ['Paris']
}
I've come up with the following, naive solution:
const reverseMapping = (obj) => {
const reversed = {};
Object.keys(obj).forEach((key) => {
reversed[obj[key]] = reversed[obj[key]] || [];
reversed[obj[key]].push(key);
});
return reversed;
};
But I'm pretty sure there is a neater, shorter way, preferably prototyped so I could simply do:
const country2cities = city2country.reverse();
You could use Object.assign, while respecting the given array of the inserted values.
const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
const reverseMapping = o => Object.keys(o).reduce((r, k) =>
Object.assign(r, { [o[k]]: (r[o[k]] || []).concat(k) }), {})
console.log(reverseMapping(city2country));
There is no such built-in function in JavaScript. Your code looks fine, but given that there are so many edge cases here that could wrong, I'd suggesting using invertBy from lodash, which does exactly what you describe.
Example
var object = { 'a': 1, 'b': 2, 'c': 1 };
_.invertBy(object);
// => { '1': ['a', 'c'], '2': ['b'] }
You can use something like this to get raid of duplicates first :
function removeDuplicates(arr, key) {
if (!(arr instanceof Array) || key && typeof key !== 'string') {
return false;
}
if (key && typeof key === 'string') {
return arr.filter((obj, index, arr) => {
return arr.map(mapObj => mapObj[key]).indexOf(obj[key]) === index;
});
} else {
return arr.filter(function(item, index, arr) {
return arr.indexOf(item) == index;
});
}
}
and then use this to make it reverse :
function reverseMapping(obj){
var ret = {};
for(var key in obj){
ret[obj[key]] = key;
}
return ret;
}
You could try getting an array of values and an array of keys from the current object, and setup a new object to hold the result. Then, as you loop through the array of values -
if the object already has this value as the key, like Netherlands, you create a new array, fetch the already existing value (ex: Rotterdam), and add this and the new value (Amsterdam) to the array, and set up this array as the new value for the Netherlands key.
if the current value doesn't exist in the object, set it up as a new string, ex: France is the key and Paris is the value.
Code -
const city2country = {
'Amsterdam': 'Netherlands',
'Rotterdam': 'Netherlands',
'Paris': 'France',
};
function reverseMapping(obj) {
let values = Object.values(obj);
let keys = Object.keys(obj);
let result = {}
values.forEach((value, index) => {
if(!result.hasOwnProperty(value)) {
// create new entry
result[value] = keys[index];
}
else {
// duplicate property, create array
let temp = [];
// get first value
temp.push(result[value]);
// add second value
temp.push(keys[index]);
// set value
result[value] = temp;
}
});
console.log(result);
return result;
}
reverseMapping(city2country)
The benefit here is - it adjusts to the structure of your current object - Netherlands being the repeated values, gets an array as it's value in the new object, while France gets a string value Paris as it's property. Of course, it should be very easy to change this.
Note - Object.values() might not be supported across older browsers.
You could use reduce to save the declaration line reduce.
Abusing && to check if the map[object[key]] is defined first before using Array.concat.
It's shorter, but is it simpler? Probably not, but a bit of fun ;)
const reverseMapping = (object) =>
Object.keys(object).reduce((map, key) => {
map[object[key]] = map[object[key]] && map[object[key]].concat(key) || [key]
return map;
}, {});
#Nina Scholz answer works well for this exact question. :thumbsup:
But if you don't need to keep both values for the Netherlands key ("Netherlands": ["Amsterdam", "Rotterdam"]), then this is a little bit shorter and simpler to read:
const city2country = { Amsterdam: 'Netherlands', Rotterdam: 'Netherlands', Paris: 'France' };
console.log(
Object.entries(city2country).reduce((obj, item) => (obj[item[1]] = item[0]) && obj, {})
);
// outputs `{Netherlands: "Rotterdam", France: "Paris"}`
Javascript has arrays which use numeric indexes ["john", "Bob", "Joe"] and objects which can be used like associative arrays or "maps" that allow string keys for the object values {"john" : 28, "bob": 34, "joe" : 4}.
In PHP it is easy to both A) sort by values (while maintaining the key) and B) test for the existence of a value in an associative array.
$array = ["john" => 28, "bob" => 34, "joe" => 4];
asort($array); // ["joe" => 4, "john" => 28, "bob" => 34];
if(isset($array["will"])) { }
How would you acheive this functionality in Javascript?
This is a common need for things like weighted lists or sorted sets where you need to keep a single copy of a value in data structure (like a tag name) and also keep a weighted value.
This is the best I've come up with so far:
function getSortedKeys(obj) {
var keys = Object.keys(obj);
keys = keys.sort(function(a,b){return obj[a]-obj[b]});
var map = {};
for (var i = keys.length - 1; i >= 0; i--) {
map[keys[i]] = obj[keys[i]];
};
return map;
}
var list = {"john" : 28, "bob": 34, "joe" : 4};
list = getSortedKeys(list);
if(list["will"]) { }
Looking at this answer by Luke Schafer I think I might have found a better way to handle this by extending the Object.prototype:
// Sort by value while keeping index
Object.prototype.iterateSorted = function(worker, limit)
{
var keys = Object.keys(this), self = this;
keys.sort(function(a,b){return self[b] - self[a]});
if(limit) {
limit = Math.min(keys.length, limit);
}
limit = limit || keys.length;
for (var i = 0; i < limit; i++) {
worker(keys[i], this[keys[i]]);
}
};
var myObj = { e:5, c:3, a:1, b:2, d:4, z:1};
myObj.iterateSorted(function(key, value) {
console.log("key", key, "value", value)
}, 3);
http://jsfiddle.net/Xeoncross/kq3gbwgh/
With ES6 you could choose to extend the Map constructor/class with a sort method that takes an optional compare function (just like arrays have). That sort method would take two arguments, each of which are key/value pairs so that the sorting can happen on either the keys or the values (or both).
The sort method will rely on the documented behaviour of Maps that entries are iterated in insertion order. So this new method will visit the entries according to the sorted order, and then delete and immediately re-insert them.
Here is how that could look:
class SortableMap extends Map {
sort(cmp = (a, b) => a[0].localeCompare(b[0])) {
for (const [key, value] of [...this.entries()].sort(cmp)) {
this.delete(key);
this.set(key, value); // New keys are added at the end of the order
}
}
}
// Demo
const mp = new SortableMap([[3, "three"],[1, "one"],[2, "two"]]);
console.log("Before: ", JSON.stringify([...mp])); // Before
mp.sort( (a, b) => a[0] - b[0] ); // Custom compare function: sort numerical keys
console.log(" After: ", JSON.stringify([...mp])); // After
I'm not sure why none of these answers mentions the existence of a built-in JS class, Set. Seems to be an ES6 addition, perhaps that's why.
Ideally override either add or keys below... NB overriding keys doesn't even need access to the Set object's prototype. Of course you could override these methods for the entire Set class. Or make a subclass, SortedSet.
const mySet = new Set();
const mySetProto = Object.getPrototypeOf(mySet);
const addOverride = function(newObj){
const arr = Array.from(this);
arr.add(newObj);
arr.sort(); // or arr.sort(function(a, b)...)
this.clear();
for(let item of arr){
mySetProto.add.call(this, item);
}
}
mySet.add = addOverride;
const keysOverride = function(){
const arr = Array.from(this);
arr.sort(); // or arr.sort(function(a, b)...)
return arr[Symbol.iterator]();
}
mySet.keys = keysOverride;
Usage:
mySet.add(3); mySet.add(2); mySet.add(1); mySet.add(2);
for(let item of mySet.keys()){console.log(item)};
Prints out:
1 ... 2 ... 3
NB Set.keys() returns not the items in the Set, but an iterator. You could choose to return the sorted array instead, but you'd obviously be breaking the class's "contract".
Which one to override? Depends on your usage and the size of your Set. If you override both you will be duplicating the sort activity, but in most cases it probably won't matter.
NB The add function I suggest is of course naive, a "first draft": rebuilding the entire set each time you add could be pretty costly. There are clearly much cleverer ways of doing this based on examining the existing elements in the Set and using a compare function, a binary tree structure*, or some other method to determine where in it to add the candidate for adding (I say "candidate" because it would be rejected if an "identical" element, namely itself, were already found to be present).
The question also asks about similar arrangements for a sorted map... in fact it turns out that ES6 has a new Map class which lends itself to similar treatment ... and also that Set is just a specialised Map, as you might expect.
* e.g. https://github.com/Crizstian/data-structure-and-algorithms-with-ES6/tree/master/10-chapter-Binary-Tree
You usually don't sort an object. But if you do: Sorting JavaScript Object by property value
If you want to sort an array, let's say the following
var arraylist = [{"john" : 28},{ "bob": 34},{ "joe" : 4}];
You can always use Array.prototype.sort function.
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Maybe this code look like what you want:
Object.prototype.asort = function(){
var retVal = {};
var self = this;
var keys = Object.keys(this);
keys = keys.sort(function(a,b){return self[a] - self[b]});
for (var i = 0; i < keys.length; i++) {
retVal[keys[i]] = this[keys[i]];
}
return retVal;
}
var map = {"john" : 28, "bob": 34, "joe" : 4}
var sortedMap = map.asort();//sortedMap["will"]: undefined
If you use the open source project jinqJs its easy.
See Fiddler
var result = jinqJs()
.from([{"john" : 28},{ "bob": 34},{ "joe" : 4}])
.orderBy([{field: 0}])
.select();
Here's an implementation of OrderedMap.
Use the functions get() and set() to extract or push key value pairs to the OrderedMap.
It is internally using an array to maintain the order.
class OrderedMap {
constructor() {
this.arr = [];
return this;
}
get(key) {
for(let i=0;i<this.arr.length;i++) {
if(this.arr[i].key === key) {
return this.arr[i].value;
}
}
return undefined;
}
set(key, value) {
for(let i=0;i<this.arr.length;i++) {
if(this.arr[i].key === key) {
this.arr[i].value = value;
return;
}
}
this.arr.push({key, value})
}
values() {
return this.arr;
}
}
let m = new OrderedMap();
m.set('b', 60)
m.set('a', 10)
m.set('c', 20)
m.set('d', 89)
console.log(m.get('a'));
console.log(m.values());
https://github.com/js-sdsl/js-sdsl
The OrderedMap in Js-sdsl maybe helpful.
This is a sorted-map which implement refer to C++ STL Map.
/*
* key value
* 1 1
* 2 2
* 3 3
* Sorted by key.
*/
const mp = new OrderedMap(
[1, 2, 3].map((element, index) => [index, element])
);
mp.setElement(1, 2); // O(logn)
mp.eraseElementByKey(1) // O(logn)
// custom comparison function
mp = new OrderedMap(
[1, 2, 3].map((element, index) => [index, element]),
(x, y) => x - y
);
// enable tree iterator index (enableIndex = true)
console.log(new OrderedMap([[0, 1], [1, 1]], undefined, true).begin(),next().index); // 1
This question already has answers here:
How to deeply merge two object values by keys
(5 answers)
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 6 years ago.
I have two json objects obj1 and obj2, i want to merge them and crete a single json object.
The resultant json should have all the values from obj2 and the values from obj1 which is not present in obj2.
Question:
var obj1 = {
"name":"manu",
"age":23,
"occupation":"SE"
}
var obj2 = {
"name":"manu",
"age":23,
"country":"india"
}
Expected:
var result = {
"name":"manu",
"age":23,
"occupation":"SE",
"country":"india"
}
There are couple of different solutions to achieve this:
1 - Native javascript for-in loop:
const result = {};
let key;
for (key in obj1) {
if(obj1.hasOwnProperty(key)){
result[key] = obj1[key];
}
}
for (key in obj2) {
if(obj2.hasOwnProperty(key)){
result[key] = obj2[key];
}
}
2 - Object.keys():
const result = {};
Object.keys(obj1)
.forEach(key => result[key] = obj1[key]);
Object.keys(obj2)
.forEach(key => result[key] = obj2[key]);
3 - Object.assign():
(Browser compatibility: Chrome: 45, Firefox (Gecko): 34, Internet Explorer: No support, Edge: (Yes), Opera: 32, Safari: 9)
const result = Object.assign({}, obj1, obj2);
4 - Spread Operator:
Standardised from ECMAScript 2015 (6th Edition, ECMA-262):
Defined in several sections of the specification: Array Initializer, Argument Lists
Using this new syntax you could join/merge different objects into one object like this:
const result = {
...obj1,
...obj2,
};
5 - jQuery.extend(target, obj1, obj2):
Merge the contents of two or more objects together into the first object.
const target = {};
$.extend(target, obj1, obj2);
6 - jQuery.extend(true, target, obj1, obj2):
Run a deep merge of the contents of two or more objects together into the target. Passing false for the first argument is not supported.
const target = {};
$.extend(true, target, obj1, obj2);
7 - Lodash _.assignIn(object, [sources]): also named as _.extend:
const result = {};
_.assignIn(result, obj1, obj2);
8 - Lodash _.merge(object, [sources]):
const result = _.merge(obj1, obj2);
There are a couple of important differences between lodash's merge function and Object.assign:
1- Although they both receive any number of objects but lodash's merge apply a deep merge of those objects but Object.assign only merges the first level. For instance:
_.isEqual(_.merge({
x: {
y: { key1: 'value1' },
},
}, {
x: {
y: { key2: 'value2' },
},
}), {
x: {
y: {
key1: 'value1',
key2: 'value2',
},
},
}); // true
BUT:
const result = Object.assign({
x: {
y: { key1: 'value1' },
},
}, {
x: {
y: { key2: 'value2' },
},
});
_.isEqual(result, {
x: {
y: {
key1: 'value1',
key2: 'value2',
},
},
}); // false
// AND
_.isEqual(result, {
x: {
y: {
key2: 'value2',
},
},
}); // true
2- Another difference has to do with how Object.assign and _.merge interpret the undefined value:
_.isEqual(_.merge({x: 1}, {x: undefined}), { x: 1 }) // false
BUT:
_.isEqual(Object.assign({x: 1}, {x: undefined}), { x: undefined })// true
Update 1:
When using for in loop in JavaScript, we should be aware of our environment specially the possible prototype changes in the JavaScript types. For instance some of the older JavaScript libraries add new stuff to Array.prototype or even Object.prototype.
To safeguard your iterations over from the added stuff we could use object.hasOwnProperty(key) to mke sure the key is actually part of the object you are iterating over.
Update 2:
I updated my answer and added the solution number 4, which is a new JavaScript feature but not completely standardized yet. I am using it with Babeljs which is a compiler for writing next generation JavaScript.
Update 3:
I added the difference between Object.assign and _.merge.
WORKING FIDDLE
Simplest Way with Jquery -
var finalObj = $.extend(obj1, obj2);
Without Jquery -
var finalobj={};
for(var _obj in obj1) finalobj[_obj ]=obj1[_obj];
for(var _obj in obj2) finalobj[_obj ]=obj2[_obj];
1)
var merged = {};
for(key in obj1)
merged[key] = obj1[key];
for(key in obj2)
merged[key] = obj2[key];
2)
var merged = {};
Object.keys(obj1).forEach(k => merged[k] = obj1[k]);
Object.keys(obj2).forEach(k => merged[k] = obj2[k]);
OR
Object.keys(obj1)
.concat(Object.keys(obj2))
.forEach(k => merged[k] = k in obj2 ? obj2[k] : obj1[k]);
3) Simplest way:
var merged = {};
Object.assign(merged, obj1, obj2);
Just another solution using underscore.js:
_.extend({}, obj1, obj2);
I've used this function to merge objects in the past, I use it to add or update existing properties on obj1 with values from obj2:
var _mergeRecursive = function(obj1, obj2) {
//iterate over all the properties in the object which is being consumed
for (var p in obj2) {
// Property in destination object set; update its value.
if ( obj2.hasOwnProperty(p) && typeof obj1[p] !== "undefined" ) {
_mergeRecursive(obj1[p], obj2[p]);
} else {
//We don't have that level in the heirarchy so add it
obj1[p] = obj2[p];
}
}
}
It will handle multiple levels of hierarchy as well as single level objects. I used it as part of a utility library for manipulating JSON objects. You can find it here.
This simple function recursively merges JSON objects, please notice that this function merges all JSON into first param (target), if you need new object modify this code.
var mergeJSON = function (target, add) {
function isObject(obj) {
if (typeof obj == "object") {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
return true; // search for first object prop
}
}
}
return false;
}
for (var key in add) {
if (add.hasOwnProperty(key)) {
if (target[key] && isObject(target[key]) && isObject(add[key])) {
this.mergeJSON(target[key], add[key]);
} else {
target[key] = add[key];
}
}
}
return target;
};
BTW instead of isObject() function may be used condition like this:
JSON.stringify(add[key])[0] == "{"
but this is not good solution, because it's will take a lot of resources if we have large JSON objects.
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}
})