Copy an arbitrary n-dimensional array in JavaScript? - javascript

I am basically looking for a general function copy(array) that will return identical n-dimensional array without any references to the former one.

This works for arrays, it won't work for nested objects (that aren't Arrays):
function copy(arr){
var new_arr = arr.slice(0);
for(var i = new_arr.length; i--;)
if(new_arr[i] instanceof Array)
new_arr[i] = copy(new_arr[i]);
return new_arr;
}
Use it like this:
var arr = [
[[1,2,3],[75]], 100,
[[[1]]], [],
[1,[2,[3],4],5], 6, 7, 8
];
var new_arr = copy(arr);

Using jQuery (works for arrays and "plain" objects):
var a = [[1,[2,3]],[4]];
var b = $.extend(true, [], a);
a[0][0] = 42; // modify object in a
alert(JSON.stringify(b)); // [[1,[2,3]],[4]]
Or JSON direct (if all objects are JSON-izable):
var a = [[1,[2,3]],[4]];
var b = JSON.parse(JSON.stringify(a))
a[0][0] = 42; // modify object in a
alert(JSON.stringify(b)); // [[1,[2,3]],[4]]
Older versions of IE (8? 7?) will need a shim for the JSON object.

I just wanted to add to Paulpro's answer. Note that this is identical to his answer except that I changed copy(new_arr[i]) to copy(arr[i]), and new_arr.length to arr.length.
function copy(arr){
var new_arr = arr.slice(0);
for(var i = arr.length; i--;)
if(new_arr[i] instanceof Array)
new_arr[i] = copy(arr[i]);
return new_arr;
}
The reason copy(new_arr[i]) worked is because the .slice copied over what arr[i] was pointing at, making them equal.
Also, while Paulpro's answer works for all cases, if by chance each member of each dimension of the multi-dimensional array is either an array or a non-array you can make it more efficient by only slicing non-array dimensions.
I mean what is the point of copying over an array of pointers that will simply be overwritten by the following recursion?
function copy(arr){
if(arr[0] instanceof Array){
var new_arr = new Array(arr.length);
for(var i = arr.length; i--;)
new_arr[i] = copy(arr[i]);
}
else{var new_arr = arr.slice(0);}
return new_arr;
}

This is my solution to clone a multidimensional array; Actually i had to invent Array.prototype.clone() in order to invent a function to generate N dimension array and initialize it with a value.
Array.prototype.clone = function(){
return this.reduce((p,c,i) => (p[i] = Array.isArray(c) ? c.clone() : c, p),[])
}
function arrayND(...n){
return n.reduceRight((p,c) => c = (new Array(c)).fill(true).map(e => Array.isArray(p) ? p.clone() : p ));
}
var arr = arrayND(...[4,4,4],8); //last argument is the initializing value
arr[0][1][3] = "eight";
console.log(JSON.stringify(arr));

Related

why Object.keys(data).sort() not work as expected?

I am trying to sort my object keys.
But when I'm printing my object, it always print bb first. Can anyone explain this?
It should print aa first ? I already sorted my keys.
My first key should be aa and then second should be bb.
Here is my code
var data = {
bb:"bb",
aa:"cc"
};
Object
.keys(data)
.sort();
console.log(data)
DEMO
Two things:
objects in JS have no order of elements, like arrays do
Object.keys returns an array of object keys, it does not modify the object itself, see the following example:
var data={bb:"bb",aa:"cc"};
var arr = Object.keys(data);
arr.sort();
console.log(arr); // the array IS modified,
// but it has nothing to do with the original object
try this
var data={bb:"bb",aa:"cc"};
var keys = Object.keys(data);
keys.sort();
var obj = {};
for(i = 0; i < keys.length; i++){
obj[keys[i]] = data[keys[i]];
}
console.log(obj);
There is not any method for sorting object keys in JavaScript but you can do this by a object prototype like this.
Object.prototype.sortKeys = function () {
var sorted = {},
key, a = [];
for (key in this) {
if (this.hasOwnProperty(key)) {
a.push(key);
}
}
a.sort();
for (key = 0; key < a.length; key++) {
sorted[a[key]] = this[a[key]];
}
return sorted;
}
var data = {bb: "bb", aa :"cc"};
alert(JSON.stringify(data.sortKeys())); // Returns sorted object data by their keys

Convert object keys into array list?

If I have an object like:
var o = {a:1,b:2,c:3};
I want to get:
['a','b','c'];
I want to use for (var i in o) because this will include properties from prototype chain. I could have used Object.keys(o) if I didn't need properties from prototype chain.
I was thinking I could use some of Array.prototype.* methods here. Like map but this is too advance for me.
I can make a string from loop and do split. But is there more efficient and professional level of doing this?
You can use the keys method in lodash, https://lodash.com/docs#keys
You could also use the following code
var arr = [];
var o = {a:1,b:2}
for(k in o) { arr.push(k); }
Here is another version which goes deep into the object
function getKeys(o) { var arr = []; for(k in o) { arr.push(k); if(typeof(o[k]) === 'object') { arr.push(getKeys(o[k])); }} return arr; }
If you have an object like {a:1,b:2, c: {d:1, e:2}} you would get the return as ['a','b','c',['d','e']]
A completely flat version would be this one
function getKeys(o) { var arr = []; for(k in o) { arr.push(k); if(typeof(o[k]) === 'object') { for(j of getKeys(o[k])){ arr.push(j); } }} return arr; }
If you have the following hash
var o = {a:1,b:2,c:3};
Create a new empty array for storing the keys
var array_keys = new Array();
Then use a for loop to push the keys into the array
for (var key in o) {
array_keys.push(key);
}
You can then run alert(array_keys)
This is a solution that avoids the for...in that you mentioned. However, I think the for...in solution is much more elegant.
This will walk the prototype chain and return all properties as a recursive function. As well as remove all duplicates.
function getKeys(obj) {
var keys = Object.keys(obj);
var parent = Object.getPrototypeOf(obj);
if (!parent) {
return keys;
}
return keys.concat(getKeys(parent))
.filter(function (item, index, col) {
return col.indexOf(item) === index;
});
}
var a = { a: 1, b: 2 };
var b = Object.create(a);
b.a = 2;
b.c = 3;
var c = Object.create(b);
console.log(getKeys(c));

JavaScript extending Array to implement map2

I want extend javascript Array to implement map2 functionality without using inbuilt map function. Where map2 functionality doubles the passed value of the array.
For example:
var m = [1,2,3,4,5]
var double = [1,2,3,4,5].map2(doubleFn)
console.log(double) should output 2,4,6,8,10
And above functionality need be developed by without using any in built method of JS array
Code Snippet
Array.prototype.map2= function(callback, thisArg){
var len=this.length
for(var i in this){
callback.call(this,this[i]*2)
}
}
Please let me know, what approach can I follow to do this
Array.prototype.map2 = function (callback, thisArg){
var i, el,
len = this.length,
res = [],
_this = thisArg ? thisArg : this;
for (i = 0; i < len; i++) {
el = this[i]; // also you can use this[i] * 2 - it depend what do want;
res[i] = callback.call(_this, el);
}
return res;
};
var double = [1,2,3,4,5].map2(function (el) {
return el * 2;
});
Example
About you errors, don't use for..in for Arrays., in map you need create new array and return it...
I'm guessing that you want to re implement built in Array.prototype.map.
Here's one of the way of doing it:
Array.prototype.map2 = function(f1){
var a = [];
this.forEach(function(element){
a.push(f1(element));
})
return a;
}
a = [1,2,3,4,5]
console.log(a.map2(function(a){return a<<1;});
//output: [ 2, 4, 6, 8, 10 ]
Edit: without using inbuilt functions:
Array.prototype.map2 = function(f1){
var a = [];
var that = this;
return (function recArray(index, target){
if(typeof(that[index]) !== 'undefined') {
target[index] = f1(that[index]);
return recArray(index + 1, target);
}
return target;
})(0, a);
}
But there is a problem with this solution:
What if input Array has holes:
a = [1,2,3,4,5]
a[12] = 11
//now a is: [ 1, 2, 3, 4, 5, , , , , , , , 11 ]
JavaScript Arrays can have holes and if there are undefined values in between then above method will fail. Without knowing the length of the Array, it is impossible to traverse it if it contains 'holes'.
Explicitly storing length doesn't makes sense as it is stored in Array.prototype.length.
Hence if the array is not contiguous then without using length it is impossible to implement map.

Check whether an object with property value 'x' exists in an array in Javascript

I have a lot of objects that I'm trying to filter out duplicates from. When an object has a property, IMAGEURL which is present in another object, I want to ignore this object and move on.
I'm using nodeJS for this so if there's a library I can use to make it easier let me know.
I've done similar implementations before with checking string values in arrays, doing something like:
var arr = ['foo', 'bar'];
if(arr.indexOf('foo') == -1){
arr.push('foo')
}
But this won't work for objects, as best I can tell. What are my options here? To put it more simply:
var obj1 = {IMAGEURL: 'http://whatever.com/1'};
var obj2 = {IMAGEURL: 'http://whatever.com/2'};
var obj3 = {IMAGEURL: 'http://whatever.com/1'};
var arr = [obj1, obj2, obj3];
var uniqueArr = [];
for (var i = 0; i<arr.length; i++){
// For all the iterations of 'uniqueArr', if uniqueArr[interation].IMAGEURL == arr[i].IMAGEURL, don't add arr[i] to uniqueArr
}
How can I do this?
You can just use an inner loop (keeping track of whether we've seen the loop by using a seen variable -- you can actually use labels here, but I find the variable method to be easier to read):
for (var i = 0; i<arr.length; i++){
var seen = false;
for(var j = 0; j != uniqueArr.length; ++j) {
if(uniqueArr[j].IMAGEURL == arr[i].IMAGEURL) seen = true;
}
if(!seen) uniqueArr.push(arr[i]);
}
Here is a concise way:
var uniqueArr = arr.filter(function(obj){
if(obj.IMAGEURL in this) return false;
return this[obj.IMAGEURL] = true;
}, {});
http://jsfiddle.net/rneTR/2
Note: this is concise, but orders of magnitude slower than Nirk's answer.
See also: http://monkeyandcrow.com/blog/why_javascripts_filter_is_slow/

Dividing elements in one array by elements in other?

Maybe trivial but what is an elegant way of dividing elements in one array by another (assume arrays are of equal length)? For instance
var A = [2,6,12,18]
var B = [2,3,4,6]
Dividing should give me: [1,2,3,3]
If you have ES5 support, this may be a good option:
var result = A.map(function(n, i) { return n / B[i]; });
Where n in callback represents the iterated number in A and i is the index of n in A.
Assuming the two arrays are always the same length:
var C = [];
for (var i = 0; i < A.length; i++) {
C.push(A[i] / B[i]);
}
There isn't any elegant method per se, as in one which avoids a forloop with a neat trick. There are some methods using map() which have already been listed. Those end up using a (longer) forloop, but they're smaller pieces of code. Otherwise, use this:
var C= new Array(A.length)
for(i=0;i<A.length;i++){
C[i]=A[i]/B[i];
}
If you consider changing the Array prototype an option:
Array.prototype.zip = function (other, reduce, thisArg) {
var i, result = [], args,
isfunc = typeof reduce == "function",
l = Math.max(this.length, other.length);
for (i=0; i<l; i++) {
args = [ this[i], other[i] ];
result.push( isfunc ? reduce.apply(thisArg, args) : args );
}
return result;
}
var A = [2,6,12,18]
var B = [2,3,4,6]
var C = A.zip(B, function (l, r) { return l / r; });
// -> [1, 2, 3, 3]

Categories

Resources