How to copy or duplicate an array of arrays - javascript

I'm trying to make a function that duplicates an array of arrays. I tried blah.slice(0); but it only copies the references. I need to make a duplicate that leaves the original intact.
I found this prototype method at http://my.opera.com/GreyWyvern/blog/show.dml/1725165
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (i in this) {
if (i == 'clone') continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
} else newObj[i] = this[i]
} return newObj;
};
It works, but messes up a jQuery plugin I'm using - so I need to turn it onto a function... and recursion isn't my strongest.
Your help would be appreciated!
Cheers,

function clone (existingArray) {
var newObj = (existingArray instanceof Array) ? [] : {};
for (i in existingArray) {
if (i == 'clone') continue;
if (existingArray[i] && typeof existingArray[i] == "object") {
newObj[i] = clone(existingArray[i]);
} else {
newObj[i] = existingArray[i]
}
}
return newObj;
}

For example:
clone = function(obj) {
if (!obj || typeof obj != "object")
return obj;
var isAry = Object.prototype.toString.call(obj).toLowerCase() == '[object array]';
var o = isAry ? [] : {};
for (var p in obj)
o[p] = clone(obj[p]);
return o;
}
improved as per comments

Related

Deep cloning object (JS) [duplicate]

This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 4 years ago.
how can I deep clone an object, what could be wrong with this solution.
I wrote this decision, but I'm not sure if this is good, and what bottlenecks it has.
How to do it correctly on vanilla js, without using jQuery. If the object has (enumerable: false)?
let user = {
name: 'SomeName',sayHi: function(){console.log(this.name);}}
Object.defineProperty(user, 'sayHi', {enumerable:false});
function deepCloneNew(obj){
if (!obj) { return };
let cloneObj = {};
let keys = Object.getOwnPropertyNames(obj);
keys.forEach((key)=>{
if(typeof obj[key] === 'object' && obj[key] !== null){
deepCloneNew(obj[key]);
}
if(typeof obj[key] === 'function'){
Object.defineProperty(cloneObj, key, Object.getOwnPropertyDescriptor(obj, key));
}
if(typeof obj[key] !== 'object' && typeof obj[key] !== 'function' || obj[key] === null){
Object.defineProperty(cloneObj, key, Object.getOwnPropertyDescriptor(obj, key));
}
})
return cloneObj;
}
let copy = deepCloneNew(user);
Please Follow this
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
var result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}

How to remove a property from an object and/or array if the value is null, undefined or empty?

So I have a fairly complex object like this:
var obj = {
v1:"ok",
v2:[
{av1:"foo", av2:null}, // notice there's a null here
{av1:"thing", av2:"stuff"}
],
v3: null,
v4:{
ov1:"slim",
ov2:"shady",
ov3:null // null
},
v5:[], // empty
v6:{} // empty
}
I'd like to get this back:
var obj = {
v1:"ok",
v2:[{av1:"foo"},{av1:"thing", av2:"stuff"}],
v4:{ov1:"slim",ov2:"shady"}
}
I'm trying to write a function that can delete anything that is null, undefined, or empty, but it's quickly becoming a spaghetti nightmare, and doesn't work.
I feel like there's a shorter more elegant way to do this, but this is my code so far:
function deleteNulls(o){
for(let i in o){
if(typeof o[i] == "Object"){
o[i] = deleteNulls(o[i])
}
else if(typeof o[i] == "Array"){
o[i] = deleteNulls(o[i])
}
if(o[i] == null || o[i] == "undefined" || o[i] == [] | o[i] == {})
delete o[i]
else {
if(typeof o == "Object"){
delete this.o[i]
return o
}
else if (typeof o == "Array")
return o.filter(k => o[k] != null)
}
}
return o
}
var obj = deleteNulls(obj)
I'm not interested in how to fix errors in the code above. I could get this to work if I wanted to,
I'm wondering if there's an easier way.
I'd suggest using something like lodash. It's tested and peer-reviewed for speed and efficiency.
Something like:
var result = _.omitBy(my_object, _.isNil);
This would remove all null values, you'll need to change the second parameter to remove empty objects and arrays.
It helped me writing this solution to split the logic into a recursive clean function (simplifying existing object structures) and a shouldKeep function (which tells you whether a key can be removed entirely from an object structure based on its value).
Demo Snippet:
var object = {
v1: "ok",
v2: [{
av1: "foo",
av2: null
}, // notice there's a null here
{
av1: "thing",
av2: "stuff"
}
],
v3: null,
v4: {
ov1: "slim",
ov2: "shady",
ov3: null // null
},
v5: [], // empty
v6: {} // empty
}
function shouldKeep (o) {
if (Array.isArray(o)) {
return o.length
} else if (typeof o === 'object') {
return o && Object.keys(o).length
}
return o != null
}
function clean (o) {
if (Array.isArray(o)) {
o.forEach(clean)
var a = o.filter(shouldKeep)
o.length = a.length
for (var i = 0; i < a.length; i++) {
o[i] = a[i]
}
} else if (o && typeof o === 'object') {
Object.keys(o).forEach(function (k) {
clean(o[k])
if (!shouldKeep(o[k])) delete o[k]
})
}
return o
}
console.log(clean(object))
.as-console-wrapper { min-height: 100vh; }
Here is my take at it. Should be good enough for most situations.
var object = {
v1: "ok",
v2: [{
av1: "foo",
av2: null
}, // notice there's a null here
{
av1: "thing",
av2: "stuff"
}
],
v3: null,
v4: {
ov1: "slim",
ov2: "shady",
ov3: null // null
},
v5: [], // empty
v6: {} // empty
}
function isEmpty(v) {
return v == undefined
|| v == null
|| (Array.isArray(v) && v.length === 0)
|| (typeof v === 'object' && Object.keys(v).length === 0)
}
function removeFalsyFromObject(v) {
var keys = Object.keys(v);
keys.forEach(function(k){
var val = v[k];
if (isEmpty(val)) {
delete v[k];
} else if (Array.isArray(val)) {
removeFalsyFromArray(val);
} else if (typeof val === 'object') {
removeFalsyFromObject(val);
}
})
}
function removeFalsyFromArray(a) {
for (var i=0; i<a.length; i++) {
var v = a[i];
if (isEmpty(v)) {
a.splice(i,1);
i--;
} else if (Array.isArray(v)) {
removeFalsyFromArray(v);
} else if (typeof v === 'object') {
removeFalsyFromObject(v);
}
}
}
function clean(obj) {
removeFalsyFromObject(obj);
return obj;
}
console.log(clean(object));

Checking if an arbitrary value is symmetric

Task:
Give you an obj, it can be 3 types: string, number and number array, Check that they are symmetrical or not, return a Boolean value.
Example:
obj="" return true (Empty string should return true)
obj="1" return true (one char should return true)
obj="11" return true
obj="12" return false
obj="121" return true
obj=1 return true (number<10 should return true)
obj=-1 return false (negative number should return false)
obj=121 return true
**obj=[] return true (Empty array should return true)**
**obj=[1] return true (an array with one element should return true)**
obj=[1,2,3,4,5] return false
**obj=[1,2,3,2,1] return true**
**obj=[11,12,13,12,11] return true (left element = right element)**
obj=[11,12,21,11] return false (not verify them as a string)
My code fails the bolded ones and I've tried to work out why for ages!
Here's my code:
function sc(obj){
var obj2= obj;
if (obj==="") {
return true;
} else if (typeof obj === "string") {
var revd = obj.split("").reverse("").join("");
return revd === obj;
} else if (typeof obj === "number") {
var revd = parseInt(obj.toString().split("").reverse("").join(""));
return revd === obj;
} else if (typeof obj === "object") {
var obj2 = []
for (var i = obj.length-1; i >= 0; i--) {
obj2.push(obj[i])
}
return obj==obj2;
}
}
console.log(sc([11,12,13,12,11]));
Could anyone give me a hint as to what's going wrong?
instead of return obj==obj2;
write the following:
for (var i=0; i<obj.length, i++) {
if (obj[i] != obj2[i]) { return false; }
}
return true;
You cannot compare arrays like that since they are references. You can however, create a function for comparing them like this:
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length)
return false;
for(var i = arr1.length; i--;) {
if(arr1[i] !== arr2[i])
return false;
}
return true;
}
And then use it like this:
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length)
return false;
for(var i = arr1.length; i--;) {
if(arr1[i] !== arr2[i])
return false;
}
return true;
}
function sc(obj){
var obj2= obj;
if (obj==="") {
return true;
} else if (typeof obj === "string") {
var revd = obj.split("").reverse("").join("");
return revd === obj;
} else if (typeof obj === "number") {
var revd = parseInt(obj.toString().split("").reverse("").join(""));
return revd === obj;
} else if (typeof obj === "object") {
var obj2 = []
for (var i = obj.length-1; i >= 0; i--) {
obj2.push(obj[i])
}
return arraysEqual(obj, obj2);
}
}
console.log(sc([11,12,13,12,11]));
Just iterate forward and backward at the same time and check.
Here is a alrothim written in c, easy to convert to C++.
Checking if an array in C is symmetric
(Javascript makes life easy re-write to JavaScript and use !=)

recursively extending and merging javascript objects

I have written two methods for extending and merging javascript objects. It might be bad question, but I'm not sure that I'm doing it right. Can you look at code and tell if I need something to improve and if i'm doing something wrong (code works at the first glance)
merge: function (a, b) {
var copy = {};
CL.extend(copy, a, [HTMLElement, Array]);
CL.extend(copy, b, [HTMLElement, Array]);
return copy;
},
extend: function extend(a, b, excludeInstances) {
for (var prop in b)
if (b.hasOwnProperty(prop)) {
var isInstanceOfExcluded = false;
if (excludeInstances)
for (var i = 0; i < excludeInstances.length; i++)
if (b[prop] instanceof excludeInstances[i])
isInstanceOfExcluded = true;
if (typeof b[prop] === 'object' && !isInstanceOfExcluded) {
a[prop] = a[prop] !== undefined ? a[prop] : {};
extend(a[prop], b[prop], excludeInstances);
} else
a[prop] = b[prop];
}
}

Resolve circular references from JSON object

If I have a serialized JSON from json.net like so:
User:{id:1,{Foo{id:1,prop:1}},
FooList{$ref: "1",Foo{id:2,prop:13}}
I want to have knockout output a foreach over FooList but I am not sure how to proceed because the $ref things could throw things.
I'm thinking the solution would be to somehow force all the Foos to be rendered in the FooList by not using:
PreserveReferencesHandling = PreserveReferencesHandling.Objects
but that seems wasteful..
I've found some bugs and implemented arrays support:
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++)
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
continue;
else if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
return obj;
}
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj);
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i = 0; i < refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[ref[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
The json object which you are receiving from the server contains Circular References. Before using the object you should have to first remove all the $ref properties from the object, means in place of $ref : "1" you have to put the object which this link points.
In your case may be it is pointing to the User's object whose id is 1
For this you should check out Douglas Crockfords Plugin on github.There is a cycle.js which can do the job for you.
or you can use the following code (not tested) :
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj)
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i=0; i<refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[refs[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
Let me know if it helps !
This is actually extremely simple if you take advantage of JSON.parse's reviver parameter.
Example below. See browser console for the output because StackOverflow's snippet console output will not provide an accurate picture of what the result is.
// example JSON
var j = '{"$id":"0","name":"Parent",' +
'"child":{"$id":"1", "name":"Child","parent":{"$ref":"0"}},' +
'"nullValue":null}';
function parseAndResolve(json) {
var refMap = {};
return JSON.parse(json, function (key, value) {
if (key === '$id') {
refMap[value] = this;
// return undefined so that the property is deleted
return void(0);
}
if (value && value.$ref) { return refMap[value.$ref]; }
return value;
});
}
console.log(parseAndResolve(j));
<b>See actual browser console for output.</b>
I had trouble with the array correction in the answer of Alexander Vasiliev.
I can't comment his answer (don't own enough reputations points ;-) ), so I had to add a new answer...
(where I had a popup as best practice not to answer on other answers and only on the original question - bof)
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++) {
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
return obj[i];
if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
}
return obj;
}
In the accepted implementation, if you're inspecting an array and come across a primitive value, you will return that value and overwrite that array. You want to instead continue inspecting all of the elements of the array and return the array at the end.
function resolveReferences(json) {
if (typeof json === 'string')
json = JSON.parse(json);
var byid = {}, // all objects by id
refs = []; // references to objects that could not be resolved
json = (function recurse(obj, prop, parent) {
if (typeof obj !== 'object' || !obj) // a primitive value
return obj;
if (Object.prototype.toString.call(obj) === '[object Array]') {
for (var i = 0; i < obj.length; i++)
// check also if the array element is not a primitive value
if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value
continue;
else if ("$ref" in obj[i])
obj[i] = recurse(obj[i], i, obj);
else
obj[i] = recurse(obj[i], prop, obj);
return obj;
}
if ("$ref" in obj) { // a reference
var ref = obj.$ref;
if (ref in byid)
return byid[ref];
// else we have to make it lazy:
refs.push([parent, prop, ref]);
return;
} else if ("$id" in obj) {
var id = obj.$id;
delete obj.$id;
if ("$values" in obj) // an array
obj = obj.$values.map(recurse);
else // a plain object
for (var prop in obj)
obj[prop] = recurse(obj[prop], prop, obj);
byid[id] = obj;
}
return obj;
})(json); // run it!
for (var i = 0; i < refs.length; i++) { // resolve previously unknown references
var ref = refs[i];
ref[0][ref[1]] = byid[ref[2]];
// Notice that this throws if you put in a reference at top-level
}
return json;
}
my solution(works for arrays as well):
usage: rebuildJsonDotNetObj(jsonDotNetResponse)
The code:
function rebuildJsonDotNetObj(obj) {
var arr = [];
buildRefArray(obj, arr);
return setReferences(obj, arr)
}
function buildRefArray(obj, arr) {
if (!obj || obj['$ref'])
return;
var objId = obj['$id'];
if (!objId)
{
obj['$id'] = "x";
return;
}
var id = parseInt(objId);
var array = obj['$values'];
if (array && Array.isArray(array)) {
arr[id] = array;
array.forEach(function (elem) {
if (typeof elem === "object")
buildRefArray(elem, arr);
});
}
else {
arr[id] = obj;
for (var prop in obj) {
if (typeof obj[prop] === "object") {
buildRefArray(obj[prop], arr);
}
}
}
}
function setReferences(obj, arrRefs) {
if (!obj)
return obj;
var ref = obj['$ref'];
if (ref)
return arrRefs[parseInt(ref)];
if (!obj['$id']) //already visited
return obj;
var array = obj['$values'];
if (array && Array.isArray(array)) {
for (var i = 0; i < array.length; ++i)
array[i] = setReferences(array[i], arrRefs)
return array;
}
for (var prop in obj)
if (typeof obj[prop] === "object")
obj[prop] = setReferences(obj[prop], arrRefs)
delete obj['$id'];
return obj;
}

Categories