...where each object also has references to other objects within the same array?
When I first came up with this problem I just thought of something like
var clonedNodesArray = nodesArray.clone()
would exist and searched for information on how to clone objects in JavaScript. I did find a question on Stack Overflow (answered by the very same #JohnResig) and he pointed out that with jQuery you could do
var clonedNodesArray = jQuery.extend({}, nodesArray);
to clone an object. I tried this though, and this only copies the references of the objects in the array. So if I
nodesArray[0].value = "red"
clonedNodesArray[0].value = "green"
the value of both nodesArray[0] and clonedNodesArray[0] will turn out to be "green". Then I tried
var clonedNodesArray = jQuery.extend(true, {}, nodesArray);
which deep copies an Object, but I got "too much recursion" and "control stack overflow" messages from both Firebug and Opera Dragonfly respectively.
How would you do it? Is this something that shouldn't even be done? Is there a reusable way of doing this in JavaScript?
Creating a deep copy with structuredClone
The modern way to deep copy an array in JavaScript is to use structuredClone:
array2 = structuredClone(array1);
However, this function is relatively new (Chrome 98, Firefox 94) and is currently only available to about 85% of users, so it's not ready for production yet without a polyfill.
As an alternative, you can use one of the well-supported JSON-based solutions below.
Creating a deep copy with JSON.parse
A general solution, that accounts for all possible objects inside an Array of objects may not be possible.
That said, if your array contains objects that have JSON-serializable content (no functions, no Number.POSITIVE_INFINITY, etc.) one simple way to avoid loops, at a performance cost, is this pure vanilla one-line solution.
let clonedArray = JSON.parse(JSON.stringify(nodesArray))
To summarize the comments below, the primary advantage of this approach is that it also clones the contents of the array, not just the array itself. The primary downsides are its limit of only working on JSON-serializable content, and it's performance is ~30 times slower than the spread method.
If you have shallow objects in the array, and IE6 is acceptable, a better approach is to use the spread operator combined with the .map array operator. For a two levels deep situation (like the array in the Appendix below):
clonedArray = nodesArray.map(a => {return {...a}})
The reasons are two fold: 1) It is much, much faster (see below for a benchmark comparison) and it will also allow any valid object in your array.
*Appendix:
The performance quantification is based on cloning this array of objects a million times:
[{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic1.jpg?raw=true', id: '1', isFavorite: false}, {url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic2.jpg?raw=true', id: '2', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic3.jpg?raw=true', id: '3', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic4.jpg?raw=true', id: '4', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic5.jpg?raw=true', id: '5', isFavorite: true},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic6.jpg?raw=true', id: '6', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic7.jpg?raw=true', id: '7', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic8.jpg?raw=true', id: '8', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic9.jpg?raw=true', id: '9', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic10.jpg?raw=true', id: '10', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic11.jpg?raw=true', id: '11', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic12.jpg?raw=true', id: '12', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic13.jpg?raw=true', id: '13', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic14.jpg?raw=true', id: '14', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic15.jpg?raw=true', id: '15', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic16.jpg?raw=true', id: '16', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic17.jpg?raw=true', id: '17', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic18.jpg?raw=true', id: '18', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic19.jpg?raw=true', id: '19', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic20.jpg?raw=true', id: '20', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic21.jpg?raw=true', id: '21', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic22.jpg?raw=true', id: '22', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic23.jpg?raw=true', id: '23', isFavorite: false}]
either using:
let clonedArray = JSON.parse(JSON.stringify(nodesArray))
or:
clonedArray = nodesArray.map(a => {return {...a}})
The map/spread approach took 0.000466 ms per pass and the JSON.parse and JSON.stringify 0.014771 ms per pass.*
I solved cloning of an array of objects with Object.assign
const newArray = myArray.map(a => Object.assign({}, a));
or even shorter with spread syntax
const newArray = myArray.map(a => ({...a}));
If all you need is a shallow copy, a really easy way is:
new_array = old_array.slice(0);
The issue with your shallow copy is that all the objects aren't cloned. While the references to each object are unique in each array, once you ultimately grab onto it you're dealing with the same object as before. There is nothing wrong with the way you cloned it... the same result would occur using Array.slice().
The reason your deep copy is having problems is because you're ending up with circular object references. Deep will go as deep as it can go, and if you've got a circle, it'll keep going infinitely until the browser faints.
If the data structure cannot be represented as a directed acyclic graph, then I'm not sure you're going to be able to find an all-purpose method for deep cloning. Cyclic graphs provide many tricky corner cases, and since it's not a common operation I doubt anyone has written a full solution (if it's even possible - it might not be! But I have no time to try to write a rigorous proof now.). I found some good comments on the issue on this page.
If you need a deep copy of an Array of Objects with circular references I believe you're going to have to code your own method to handle your specialized data structure, such that it is a multi-pass clone:
On round one, make a clone of all objects that don't reference other objects in the array. Keep a track of each object's origins.
On round two, link the objects together.
If you only need a shallow clone, the best way to do this clone is as follows:
Using the ... ES6 spread operator.
Here's the simplest example:
var clonedObjArray = [...oldObjArray];
This way we spread the array into individual values and put it in a new array with the [] operator.
Here's a longer example that shows the different ways it works:
let objArray = [ {a:1} , {b:2} ];
let refArray = objArray; // this will just point to the objArray
let clonedArray = [...objArray]; // will clone the array
console.log( "before:" );
console.log( "obj array" , objArray );
console.log( "ref array" , refArray );
console.log( "cloned array" , clonedArray );
objArray[0] = {c:3};
console.log( "after:" );
console.log( "obj array" , objArray ); // [ {c:3} , {b:2} ]
console.log( "ref array" , refArray ); // [ {c:3} , {b:2} ]
console.log( "cloned array" , clonedArray ); // [ {a:1} , {b:2} ]
This works for me:
var clonedArray = $.map(originalArray, function (obj) {
return $.extend({}, obj);
});
And if you need a deep copy of objects in the array:
var clonedArray = $.map(originalArray, function (obj) {
return $.extend(true, {}, obj);
});
$.evalJSON($.toJSON(origArray));
If you want to implement a deep clone, use JSON.parse(JSON.stringify(your {} or [])):
const myObj ={
a: 1,
b: 2,
b: 3
}
const deepClone = JSON.parse(JSON.stringify(myObj));
deepClone.a = 12;
console.log("deepClone-----"+myObj.a);
const withOutDeepClone = myObj;
withOutDeepClone.a = 12;
console.log("withOutDeepClone----" + myObj.a);
Map will create a new array from the old one (without reference to old one) and inside the map you create a new object and iterate over properties (keys) and assign values from the old Array object to corresponding properties to the new object.
This will create exactly the same array of objects.
let newArray = oldArray.map(a => {
let newObject = {};
Object.keys(a).forEach(propertyKey => {
newObject[propertyKey] = a[propertyKey];
});
return newObject;
});
Lodash has the cloneDeep function for these purposes:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
I may have a simple way to do this without having to do painful recursion and not knowing all the finer details of the object in question. Using jQuery, simply convert your object to JSON using the jQuery $.toJSON(myObjectArray), then take your JSON string and evaluate it back to an object. BAM! Done, and done! Problem solved. :)
var oldObjArray = [{ Something: 'blah', Cool: true }];
var newObjArray = eval($.toJSON(oldObjArray));
I'm answering this question because there doesn't seem to be a simple and explicit solution to the problem of "cloning an array of objects in JavaScript":
function deepCopy (arr) {
var out = [];
for (var i = 0, len = arr.length; i < len; i++) {
var item = arr[i];
var obj = {};
for (var k in item) {
obj[k] = item[k];
}
out.push(obj);
}
return out;
}
// test case
var original = [
{'a' : 1},
{'b' : 2}
];
var copy = deepCopy(original);
// change value in copy
copy[0]['a'] = 'not 1';
// original[0]['a'] still equals 1
This solution iterates the array values, iterates the object keys, saving the latter to a new object, and pushes that new object to a new array.
See jsfiddle. Note: a simple .slice() or [].concat() isn't enough for the objects within the array.
This method is very simple and you can modify your clone without modifying the original array.
// Original Array
let array = [{name: 'Rafael'}, {name: 'Matheus'}];
// Cloning Array
let clone = array.map(a => {return {...a}})
// Editing the cloned array
clone[1].name = 'Carlos';
console.log('array', array)
// [{name: 'Rafael'}, {name: 'Matheus'}]
console.log('clone', clone)
// [{name: 'Rafael'}, {name: 'Carlos'}]
As Daniel Lew mentioned, cyclic graphs have some problems. If I had this problem I'd either add special clone() methods to the problematic objects or remember which objects I've already copied.
I'd do it with a variable copyCount which increases by 1 every time you copy in your code. An object that has a lower copyCount than the current copy-process is copied. If not, the copy, that exists already, should be referenced. This makes it necessary to link from the original to its copy.
There is still one problem: Memory. If you have this reference from one object to the other, it's likely that the browser can't free those objects, as they are always referenced from somewhere. You'd have to make a second pass where you set all copy-references to Null. (If you do this, you'd not have to have a copyCount but a boolean isCopied would be enough, as you can reset the value in the second pass.)
jQuery extend is working fine. You just need to specify that you are cloning an array rather than an object (note the [] instead of {} as parameter to the extend method):
var clonedNodesArray = jQuery.extend([], nodesArray);
My approach:
var temp = { arr : originalArray };
var obj = $.extend(true, {}, temp);
return obj.arr;
gives me a nice, clean, deep clone of the original array - with none of the objects referenced back to the original :-)
I use the new ECMAScript 6 Object.assign method:
let oldObject = [1, 3, 5, "test"];
let newObject = Object.assign({}, oldObject);
The first argument of this method is the array to be updated. We pass an empty object, because we want to have a new object.
We can also use this syntax, which is the same but shorter:
let newObject = [...oldObject];
I was pretty frustrated by this problem. Apparently the problem arises when you send in a generic Array to the $.extend method. So, to fix it, I added a little check, and it works perfectly with generic arrays, jQuery arrays, and any objects.
jQuery.extend({
deepclone: function(objThing) {
// return jQuery.extend(true, {}, objThing);
/// Fix for arrays, without this, arrays passed in are returned as OBJECTS! WTF?!?!
if ( jQuery.isArray(objThing) ) {
return jQuery.makeArray( jQuery.deepclone($(objThing)) );
}
return jQuery.extend(true, {}, objThing);
},
});
Invoke using:
var arrNewArrayClone = jQuery.deepclone(arrOriginalArray);
// Or more simply/commonly
var arrNewArrayClone = $.deepclone(arrOriginalArray);
This deeply copies arrays, objects, null and other scalar values, and also deeply copies any properties on non-native functions (which is pretty uncommon but possible). (For efficiency, we do not attempt to copy non-numeric properties on arrays.)
function deepClone (item) {
if (Array.isArray(item)) {
var newArr = [];
for (var i = item.length; i-- > 0;) {
newArr[i] = deepClone(item[i]);
}
return newArr;
}
if (typeof item === 'function' && !(/\(\) \{ \[native/).test(item.toString())) {
var obj;
eval('obj = '+ item.toString());
for (var k in item) {
obj[k] = deepClone(item[k]);
}
return obj;
}
if (item && typeof item === 'object') {
var obj = {};
for (var k in item) {
obj[k] = deepClone(item[k]);
}
return obj;
}
return item;
}
Array.slice can be used to copy an array or part of an array...
This would work with strings and numbers .. - changing a string in one array would not affect the other - but objects are still just copied by reference, so changes to referenced objects in one array would have an affect on the other array.
Here is an example of a JavaScript undo manager that could be useful for this: http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx
Forget eval() (it is the most misused feature of JavaScript and makes the code slow) and slice(0) (works for simple data types only)
This is the best solution for me:
Object.prototype.clone = function() {
var myObj = (this instanceof Array) ? [] : {};
for (i in this) {
if (i != 'clone') {
if (this[i] && typeof this[i] == "object") {
myObj[i] = this[i].clone();
}
else
myObj[i] = this[i];
}
}
return myObj;
};
In JavaScript, array and object copy change the original values, so a deep copy is the solution for this.
A deep copy means actually creating a new array and copying over the values, since whatever happens to it will never affect the origin one.
JSON.parse and JSON.stringify is the best and simple way to deep copy. The JSON.stringify() method converts a JavaScript value to a JSON string.The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.
Deep Clone
let a = [{ x:{z:1} , y: 2}];
let b = JSON.parse(JSON.stringify(a));
b[0].x.z=0
console.log(JSON.stringify(a)); //[{"x":{"z":1},"y":2}]
console.log(JSON.stringify(b)); // [{"x":{"z":0},"y":2}]
For more details: Read Here
In 2022, We can use structuredClone to deep copy.
structuredClone(array)
For more details about it click here
We can invent a simple recursive Array method to clone multidimensional arrays. While the objects within the nested arrays keep their reference to the corresponding objects in the source array, arrays won't.
Array.prototype.clone = function(){
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));
With jQuery:
var target = [];
$.each(source, function() {target.push($.extend({}, this));});
I am using Vue.js, so arrays/objects have other code tacked-on for Vue.js functionality. I tried many of the answers given, but I ended up using clone-deep.
person1 = {
name: 'Naved',
last: 'Khan',
clothes: {
jens: 5,
shirts: 10
}
};
person2 = {
name: 'Naved',
last: 'Khan'
};
// first way shallow copy single lavel copy
// const person3 = { ...person1 };
// secound way shallow copy single lavel copy
// const person3 = Object.assign({}, person1);
// third way shallow copy single lavel copy but old
// const person3 = {};
// for (let key in person1) {
// person3[key] = person1[key];
// }
// deep copy with array and object best way
const person3 = JSON.parse(JSON.stringify(person1));
person3.clothes.jens = 20;
console.log(person1);
console.log(person2);
console.log(person3);
The following code will perform a deep copy of objects and arrays recursively:
function deepCopy(obj) {
if (Object.prototype.toString.call(obj) === '[object Array]') {
var out = [], i = 0, len = obj.length;
for ( ; i < len; i++ ) {
out[i] = arguments.callee(obj[i]);
}
return out;
}
if (typeof obj === 'object') {
var out = {}, i;
for ( i in obj ) {
out[i] = arguments.callee(obj[i]);
}
return out;
}
return obj;
}
Source
Some elegant ways for deep cloning in JavaScript:
Type: Object
Copying Objects in JavaScript
A vanilla JavaScript method for cloning objects
A clever exploit of the JSON library to deep-clone objects
Using jQuery’s $.extend() function
Using Mootools’ clone() function to clone objects
Here is a my solution. It works for an array of objects or Map. This solution keeps the methods also.
A deep copy means actually creating a new array and copying over the values, since whatever happens to it will never affect the origin one.
This is the best solution for me:
deepCopy(inputObj: any) {
var newObj = inputObj;
if (inputObj && typeof inputObj === "object") {
newObj = Object.prototype.toString.call(inputObj) === "[object Array]" ? [] : {};
for (var i in inputObj) {
newObj[i] = this.deepCopy(inputObj[i]);
}
//For maps
if(Object.prototype.toString.call(inputObj) === "[object Map]"){
newObj = new Map;
inputObj.forEach((v,k) =>{
newObj.set(k,this.deepCopy(v));
});
}
}
return newObj;
}
Related
I was looking online to see various ways to delete a record from object and to replace the id of the record deleted by the next record available, meaning The usage of delete operator in JavaScript does not the delete the record id associated with it. say I have an Object:
obj= {
0:{},
1:{},
2:{},
3:{}
}
and I want to delete a record from that object, I do:
delete obj[1]
and it deletes the record but when I console.log(obj)
I see:
obj= {
0:{},
2:{},
3:{}
}
I know one way could be to convert this object into an array, then delete the element from array and convert it back to an object. If I have to avoid all this conversion, is there a way in JavaScript object when we delete the record, the obj returns as the following when we delete the obj[1] record:
obj= {
0:{},
1:{},
2:{}
}
instead
obj= {
0:{},
2:{},
3:{}
}
obj= {
0:{a: 1},
1:{b: 2},
2:{c: 5},
3:{d: 10}
}
const deleteElement = (index, obj) => {
delete obj[index];
return Object.keys(obj).reduce((agg, ele, i) => {
agg[i] = obj[ele];
return agg;
}, {});
}
console.log(deleteElement(2, obj))
As the others have mentioned, you should be converting the object into an array. Here is another way to do it, via Array.from, and then let Array#splice take care of deletion and reindexing.
Adding a length property to the object makes Array.from treat the value as an array-like object and it also guarantees that the array is "in order".
var obj = {
0:{0:0},
1:{1:1},
2:{2:2},
3:{3:3}
};
obj.length = Object.keys(obj).length;
var arr = Array.from(obj);
arr.splice(1,1);
console.log(arr);
However, if you really need an object, because of how array methods work, you could apply Array#splice directly to the object, as long as it has a length property. The effect is that the object is mutated in place:
var obj = {
0:{0:0},
1:{1:1},
2:{2:2},
3:{3:3}
};
obj.length = Object.keys(obj).length;
Array.prototype.splice.call(obj, 1, 1);
console.log(obj);
// optionally: delete obj.length
As mentioned before no. But a quick way to convert it could be
let Array=Object.keys(obj).map(x=>{
return obj[x]});
console.log(array);
Only way I can think about doing it without mapping to an array is to copy all the properties in the object down one and delete the last item.
var myObject = {
0: {org: 0},
1: {org: 1},
2: {org: 2},
3: {org: 3}
}
const removeIndex = (index, obj) => {
var size = Object.keys(obj).length
if (index<size-1) {
for (let i = index; i<size-1; i++) {
obj[i] = obj[i+1];
}
}
delete obj[size-1]
}
removeIndex(1, myObject)
console.log(myObject)
But in reality, you should NOT be using an Object to act like an index on an Array. I would fix the server, if you can not do that, I would map it t an array when page loads and use it that way. If the server needs it back, map it back to this weird object.
If you have a large object, this deletion is going to take awhile, while in an array, it will be fast with splice.
Is there a way?
No automatically, you need to execute some logic (like the conversion you talked about).
I don't understand how work copying for arrays and what is the best way to copying objects from an array.
When I create a new array which is a copy of my existing array by spread operator or method slice() any actions still change the values in both arrays.
example:
let array = [{count: 10}, {count: 20}, {count: 30}, {count: 40}];
let newArray = array.slice();
newArray[1].count = 0;
// console.log(array[1].count === newArray[1].count);
// > true
This means that I should use JSON.parse(JSON.stringify(array))?
example:
let array = [{count: 10}, {count: 20}, {count: 30}, {count: 40}];
let arrayByJSON = JSON.parse(JSON.stringify(array));
arrayByJSON[1].count = 5;
// console.log(array[1].count === arrayByJSON[1].count)
// false
I think the confusion here is over deep vs. shallow clones, which basically comes down to if references are followed or not.
Shallow copy: Shallow copy is a bit-wise copy of an object. A new
object is created that has an exact copy of the values in the original
object. If any of the fields of the object are references to other
objects, just the reference addresses are copied i.e., only the memory
address is copied.
Deep copy: A deep copy copies all fields, and makes
copies of dynamically allocated memory pointed to by the fields. A
deep copy occurs when an object is copied along with the objects to
which it refers.
In general, Use slice/spread for shallow clones, and JSON.parse/stringify for deep clones.
In your case, use JSON.parse(JSON.stringify(array)) because you don't want references preserved during the clone.
More info here.
You can copy elements of an array by using .slice or [...] however, the things that cannot be copied this way are multi-dimensional arrays or objects since they are stored as references.
Using JavaScript syntax we can easily determine if an item is an Array or an Object and copy them using appropriate methods.
Objects can be copied by using Object.assign({}, obj).
Arrays can be copied by using .slice(). In the code, however, we want to iterate through the internal array as well since you never know if that internal array will also contain more Arrays or Objects. We can make sure this problem doesn't cause us any hassle by simply recursively calling our deep copy function when we run into instances of an Array.
function deepCopyArray(arr) {
return arr.map(item => {
if (Array.isArray(item)) {
return deepCopyArray(item);
}
if (typeof item === "object") {
return Object.assign({}, item);
}
else return item;
});
}
function deepCopyArray(arr) {
return arr.map(item => {
if (Array.isArray(item)) {
return deepCopyArray(item);
}
if (typeof item === "object") {
return Object.assign({}, item);
}
else return item;
});
}
let array = [{
count: 10
}, {
count: 20
}, {
count: 30
}, {
count: 40
}];
let newArray = deepCopyArray(array);
newArray[1].count = 0;
console.log(newArray, array);
Is there an equivalent method available in javascript to switch keys in a hash
For example
Eg. {x: 50, y: 75, data: 99}, to {x: 50, y: 99, data: 75}
I would use reduce
hash = { name: 'Rob', age: '28' }
Object.keys(hash).reduce((object, key) => {
object[key.toUpperCase()] = hash[key];
console.log(object);
return object;
}, {});
Basically, what is happening is I iterate over an array of the hash object's keys. For each key, I alter an empty object (the last argument), declaring that key with the required transformations as a property on that object and then assigning it the value of the corresponding property on the hash object. The altered object is then used in place of the initial empty object, and the function calls itself recursively until the end of the array. Reduce is awesome and if you're new to JS I highly suggest learning more about it! All of the functional array methods (map, filter, reduce, forEach) are worth looking into.
Read more about reduce here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?v=a
You're going to have to write a function to swap object keys
const propSwap = (obj1, key1, key2, obj2) => {
obj2 = obj2 || JSON.parse(JSON.stringify(obj1));
obj1[key2] = obj2[key1];
obj1[key1] = obj2[key2];
return obj1;
}
that first line makes it so that obj2 is an optional argument
unfortunately the javascript standard library isn't extremely deep but you can accomplish those two operations separately using the methods I've outlined above. If you have any questions let me know!
You could do something like this:
const transformed = Object.entries(hash).reduce((aggr, [key, value]) => {
aggr[key.toUpperCase()] = value;
return aggr;
}, {});
I got two Objects
object = {
id: 1,
Oid: 'ff2d344e-77e8-43b3-a6f3-bfba577d6abd',
name: 'Some name'
}
and
object2 = {
id: 3,
array_ref: 'ff2d344e-77e8-43b3-a6f3-bfba577d6abd',
value1: 'Additional Data',
value2: 'Some other additional data'
}
As you see, the only reference between these Objects is the Oid and the Array_ref. Now I want to bind these Objects together to one where the Oid and the Array_ref matches. In the end I have to do this multiple times (with multiple objects).
Is there some easy way to do this?
This Code is realized in JavaScript using angular partly. I'm not able to use anything else.
You are not using arrays (nor JSON) but objects.
If you want to merge two objets, one solution is to use next Javascript API (ES6) Object.assign method through a polyfill (polyfills give you the ability to use standard APIs that are not yet supported):
var obj1 = {
id: 1,
Oid: 'ff2d344e-77e8-43b3-a6f3-bfba577d6abd',
name: 'Some name'
}
var obj2 = {
id: 3,
array_ref: 'ff2d344e-77e8-43b3-a6f3-bfba577d6abd',
value1: 'Additional Data',
value2: 'Some other additional data'
}
var obj3 = Object.assign({}, obj1, obj2);
console.log(obj3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.js"></script>
Which will gives you:
In the above snippet I included a whole es6 polyfill but this polyfill here only contains what you are looking for and is probably better if you want to avoid loading a whole bunch of useless code (I could not easily load it in the snippet).
If you use jQuery or underscore, they also both provides an extend method that you can use instead of a polyfill.
Warning! As you can see, this will actually overwrites any content in obj1 that is also in obj2 with the content of obj2. Here, for example, the resulting object has the id of obj2. If you are looking for a more clever kind of merging, you will need to go for more complicated algorithmic.
If you want to merge only the objects with the same id, I would use underscore:
var objectList = [
{ id: 1, val: "val1", otherVal: "val2" },
{ id: 2, val: "val3" },
{ id: 1, val: "val4", differentVal: "val5" }
];
// group objects by ids
var groups = _.groupBy(objectList, 'id');
// merge the groups
var mergedObjects = _.map(groups, function(objects){
// Adds a new empty object at the beginning of the list
// to avoid modifying the original objects.
// It will be the destination of _.extend.
objects.unshift({});
// use extend with apply so we can provide it an array to be used as arguments
return _.extend.apply(_, objects);
});
console.log(mergedObjects);
<script src="http://underscorejs.org/underscore-min.js"></script>
Notice that some overwriting still can happen (first object's val is overwritten by third object's val here).
Edit: As pointed out in the comment, lodash is likely to be a better solution than underscore. Read here about lodash vs. underscore.
Here's a simple way.
Your object could look like this:
{
'ff2d344e-77e8-43b3-a6f3-bfba577d6abd': {
id: 1,
name: 'Some name'
}
}
And then you access it like this:
object[object2.array_ref]
And you would have all the data you want.
Later on, you can add properties to that object using a for( in ) loop:
var obj = object[object2.array_ref];
for(var k in object2)
{
if( !(k in obj) && object2.hasOwnProperty(k))
{
obj[k] = object2[k];
}
}
This would add the values to the object if they aren't already present.
You could verify other calues by adding conditions to that if.
I have a scenario where i need to copy the array of Objects(Main array) to another Temp array which should not have object reference basically if i make any modification to Main array it should not reflect in the Temp array so that i will preserve the copy independently.
I have used one of the code snippet from stack overflow this one does partially like if i delete all objects from the Main array the temp array still hold the value but when i do some modifications in main array and click cancel button iam removing all objects from the main array using array.Removeall(); but the modification still exist in Temp array so which means that object having a reference.
clone: function (existingArray) {
var newObj = (existingArray instanceof Array) ? [] : {};
console.debug('newObj value is ' + newObj);
for (i in existingArray) {
console.debug('i value is' + i);
if (i == 'clone') continue;
console.debug('existingArray[i] value ' + existingArray[i]);
if (existingArray[i] && typeof existingArray[i] == "object") {
newObj[i] = this.clone(existingArray[i]);
} else {
console.debug('in else part ' + existingArray[i]);
newObj[i] = existingArray[i];
}
}
return newObj;
}
my object structure is like
iam using knockout framework.
newObjectCreation = function (localIp, RemoteIp, areaId) {
this.localIP = ko.observable(localIp);
this.remoteIP = ko.observable(RemoteIp);
this.areaId = ko.observable(areaId);
};
template.ProtocolArray.push(new newObjectCreation('', '', '')); // to create default row
Let me understand: you don't want just have a new array, but you want to create a new instance for all objects are present in the array itself? So if you modify one of the objects in the temp array, that changes is not propagated to the main array?
If it's the case, it depends by the values you're keeping in the main array. If these objects are simple objects, and they can be serialized in JSON, then the quickest way is:
var tempArray = JSON.parse(JSON.stringify(mainArray));
If you have more complex objects (like instances created by some your own constructors, html nodes, etc) then you need an approach ad hoc.
Edit:
If you don't have any methods on your newObjectCreation, you could use JSON, however the constructor won't be the same. Otherwise you have to do the copy manually:
var tempArray = [];
for (var i = 0, item; item = mainArray[i++];) {
tempArray[i] = new newObjectCreation(item.localIP, item.remoteIP, item.areaId);
}
For some other people with the same question. You could also do it this way.
Using the new es6 features you could create a copy of an array (without reference) and a copy of every object without one level of references.
const copy = array.map(object => ({ ...object }))
It's much more functional and idiomatic IMHO
Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
So basically if your objects doesn't have objects as properties. This syntax is everything you need. Unfortunately there is not "out of the box" deep clone feature on the spec but you can always use a library if that's what you need
Browser Compatibility Warning: I think it is part of the specification of Ecma now, but some browsers doesn't have full support of spread syntax jet. But using one of the popular transpilers out there you will be fine
Lodash can be used for deep copying objects _.cloneDeep(value)
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// → false
To copy the values of an array without copying the reference of the array, you can simply do:
const tempArray = [...mainArray];
This is the recommended solution for AirBnb's JS Style Guide: https://github.com/airbnb/javascript#arrays
However, this will not create new referenes for the objects inside the array. To create a new reference for the array and the objects inside, you can do:
JSON.parse(JSON.stringify(mainArray));
So you want a deep copy without object reference? Sure, use .slice().
Example:
var mainArr = [],
tmpArr = []
tmpArr = mainArr.slice(0) // Shallow copy, no reference used.
PS: I don't think double-JSON parsing is performance wise.
You can use Angular's copy: angular.copy();
On nested array you can do:
const origin = [{ cat: 'Bengal', dog: 'Birman' }, { cat: 'Abyssinian', dog: 'Bombay' }];
const clone = [];
origin.forEach(v=> clone.push(Object.assign({}, v)));
Use angular.copy. But not for the whole array (because it would pass array items by reference), but iterate it and use angular.copy on its members.
var newArray = [];
for (var i = 0, item; item = mainArray[i];) {
newArray[i] = angular.copy(item);
i++;
}
Use this function
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
let deepClonedArrayOfObjects = structuredClone(originalArrayOfObjects);