I have a object where data is an array of objects, inside data object I have one users property which is an array.
I need to get all the users into a single array. I have written using map and concat.
Is there any way I can have a better solution, or this is correct?
See the below snippet.
var response = {data: [{users: [1,2,3]}, {users: [4,5,6]}]}
var users = response.data.map(o => o.users)
const usersCollection = [].concat(...users)
console.log(usersCollection)
You can use Array.prototype.flat():
The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.
depth | Optional
The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
var response = {data: [{users: [1,2,3]}, {users: [4,5,6]}]}
var users = response.data.map(o => o.users).flat();
console.log(users);
You can also try with Array.prototype.flatMap() which is identical to a map followed by a call to flat of depth 1.
var users = response.data.flatMap(o => o.users);
If Using ES6, utilizing the power of spread with Array.reduce
var response = {
data: [{
users: [1, 2, 3]
}, {
users: [4, 5, 6]
}]
}
var users = response.data.reduce((accumulator, obj) => [...accumulator, ...obj.users], []);
console.log(users);
Using ES6 Spread operator you can do the job quick.
var response = {data: [{users: [1,2,3]}, {users: [4,5,6]}]}
let wantedArray = [];
for(let v of response.data){
wantedArray.push(...v.users);
}
console.clear();
console.log(wantedArray);
The flatMap() method first maps each element using a mapping function,
then flattens the result into a new array. It is identical to a map
followed by a flat of depth 1, but flatMap is often quite useful, as
merging both into one method is slightly more efficient
response.data.flatMap(({users})=>users)
You should be careful with vendor support though if you’re running in the browser.
You can use ES6 spread operator
var response = {data: [{users: [1,2,3]}, {users: [4,5,6]}]}
var NEWARRAY = [];
for(var v of response.data){
NEWARRAY.push(...v.users);
}
console.log(NEWARRAY);
...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;
}
I have several objects in an array which looks like this:
var objectArray = [
abc: {id: "qq09qed0000", status: "running"},
def: {id: "qq09qed0001", status: "paused"},
...
xyz: {id: "qq09qed9999", status: "paused"}
];
EDIT: I am not sure, how it is represented in memory, the objectArray is created by doing array.push(object); several hundred times in a loop. The above is just an example to show you what the objects look like, they are truncated and in reality have lots more fields. Please stop telling me that the array is formatted wrong, it is, I am aware, it is just an example. The main thing to consider is how to go from an array of objects to an array of strings.
Edit2: Perhaps it looks like this in memory:
var objectArray = [
{id: "qq09qed0000", status: "running"},
{id: "qq09qed0001", status: "paused"},
...
{id: "qq09qed9999", status: "paused"}
];
If I wanted an ordered array of just the ids (they are non sequential) of all of these elements, what would be the best way to extract them?
I have attempted a foreach loop,
for(var obj in objectArray ) {
dataArray.push(objectArray[obj].getid());
}
But I am told this is not guarranteed to keep the ordering intact.
You can use maybe arr.sort() function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
function sortById (a,b)
{
if (a.id < b.id) return -1
if (a.id > b.id) return 1;
return 0;
}
objectArray.sort (sortById);
Your objectArray is an object, not an array, and as such, has no obvious / defined ordering.
What order the elements are in, depends on the JavaScipt implementation of the browser it's running in.
For that reason, there is no way to "keep the ordering intact.", since there is no set ordering in the first place.
Now that you've edite the question, the objectArray isn't valid JavaScript syntax.
Arrays can't have key:value pairs like this:
var objectArray = [
abc: {id: "qq09qed0000", status: "running"},
From what I gathered from your question, this should work:
data = data2.map(function(e){return e.id;});
This should return an array with only id values, in the same order as in data2.
If you push all the ids in an array first, then sort them after, the fact that the 'for' function does not keep the ordering won't matter
Push all the ids in a new array
var objectArray = {
abc: {id: "qq09qed0000", status: "running"},
def: {id: "qq09qed0001", status: "paused"},
...
xyz: {id: "qq09qed9999", status: "paused"}
};
var idArray = [];
for(var item in objectArray){
idArray.push(item.id);
};
This will produce an array looking like this:
idsArray = ["qq09qed0000","qq09qed0001", ..., "qq09qed9999"]
Next, sort the array using the array sort method
idsArray.sort();
OR
idsArray.sort(function(a,b){
return a - b;
});
This will sort them in alphanumeric order by default. You can also reverse the order using:
idsArray.sort(function(a,b){
return b - a;
});
Namespace.pluck = function(obj, prop)
{
var isFunction = Namespace.isFunction(prop);
return Namespace.map(obj, function(key, element)
{
return isFunction ? prop(element) : element[prop];
});
};
var ids = Namespace.pluck(objectArray, 'id').sort();
Array.prototype.sort() will give you a lexicographical sort, and not a natural sort. 10 will come after 1 but before 2