Related
There are quite a few similar questions but I couldn't get their answers to work.
let obj = {};
const key;//a string
const value;//a string
obj[key].push(value);
Obviously this doesn't work but I don't know how to do this. I want it to add a new key and value if it doesn't exist, but if it does exist it should append it to the end of the values for that particular key. ie like the normal push action with arrays.
Expected behaviour:
key = 'hello'
value = 'thanks'
obj = {'hello' : ['thanks']}
key = 'goodbye'
value = 'ok'
obj = {'hello' : ['thanks'], 'goodbye' : ['ok']}
key = 'hello'
value = 'why'
obj = {'hello' : ['thanks','why'], 'goodbye' : ['ok']}
The value 'why' is appended to the end for key 'hello'.
EDIT: All values are put into arrays.
You could create custom function for this that checks if the key exists in object and based on that sets value directly or turns it into an array.
let obj = {
foo: 'bar'
};
let add = (obj, key, val) => {
if (key in obj) obj[key] = [].concat(obj[key], val);
else obj[key] = val;
}
add(obj, 'foo', 'baz');
add(obj, 'bar', 'baz');
console.log(obj)
You could also use Proxy with set trap that will run when you try to set new property on proxy object.
const obj = {
foo: 'bar'
}
const proxy = new Proxy(obj, {
set(obj, prop, value) {
if (prop in obj) obj[prop] = [].concat(obj[prop], value)
else obj[prop] = value;
}
})
proxy.foo = 'bar';
proxy.bar = 'baz';
console.log(proxy)
Fairly simple - use the logical OR operator ||:
let obj = {};
const key = "key";
const value = "value";
obj[key] = obj[key] || [];
obj[key].push(value);
obj[key].push("anotherValue");
console.log(obj);
.as-console-wrapper { max-height: 100% !important; top: auto; }
You can also do this via Object.assign in a pretty concise way:
let o = {}
let add = (obj, k, v) => Object.assign(obj, obj[k]
? { [k]: [].concat(obj[k], v) }
: { [k]: v })
console.log(add(o, 'a', 1))
console.log(add(o, 'b', 2))
console.log(add(o, 'a', 3))
console.log(add(o, 'a', 4))
The idea is to use the ternary operator and check if we already have the key and if so concat it to a new array. otherwise just assign a new object.
You can create a prototype function for your requirement
Object.prototype.add = function(key, value) {
if( this[key] != undefined) this[key] = [].concat(this[key], value);
else this[key] = value;
};
let obj = {'hello' : 'thanks', 'goodbye' : 'ok'}
let key = 'hello'
let value = 'why'
//obj[key] = value;
obj.add(key,value);
console.log(obj)
Try (I take this comment into account)
let obj = {};
const key = 'some_key';//a string
const value = 'first value' ;//a string
obj[key]= (obj[key]||[]).concat(value);
obj[key]= (obj[key]||[]).concat('next value');
console.log(obj);
Try this. However, this will also put the first value in an array, but that's quite a standard behaviour if you know it's going to be an array of values.
if(obj[key]){
obj[key].push(value);
}
else{
obj[key] = [value];
}
what would be the best method to remove notneeded property from this array?
function apiCall(...args) {
//at this point I need to remove 'notneeded' property from args
};
apiCall({a: 'one', b: 'two'}, {notneeded: '', c: 'three'}, 'hello');
Those passed objects could be primitives, functions etc., so ideally the solution would handle every case.
I have created following function for this problem but I am sure there is a better solution (possibly with new ECMA standards).
function omit(args, omitKey) {
let omitted = [];
for (let i = 0; i < args.length; i++) {
if (typeof args[i] === 'object') {
omitted.push(
Object.keys(args[i]).reduce((result, key) => {
if (key !== omitKey) {
result[key] = args[i][key];
}
return result;
}, {}),
);
} else {
omitted.push(args[i]);
}
}
return omitted;
}
Don't mix the array iteration into that method. Make it do one task only.
function omit(obj, keyToOmit) {
if (typeof obj != "object") return obj;
const result = {};
for (const key in obj) {
if (key != keyToOmit) {
result[key] = obj[key];
}
}
return result;
}
function apiCall(...args) {
args = args.map(arg => omit(arg, "notneeded"));
…
}
I am trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, {...object[key]});
return output;
},
{});
}
It works:
return mapCopy(state, e => {
if (e.id === action.id) {
e.title = 'new item';
}
return e;
})
However it does not deep copy inner items so I need to tweak it to:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
let newObject = {...object[key]};
newObject.style = {...newObject.style};
newObject.data = {...newObject.data};
output[key] = callback.call(this, newObject);
return output;
}, {});
}
This is less elegant as it requires to know which objects are passed.
Is there a way in ES6 to use the spread syntax to deep copy an object?
Use JSON for deep copy
var newObject = JSON.parse(JSON.stringify(oldObject))
var oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));
newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);
No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.
If you really want to deep copy:
Use a library. For example, lodash has a cloneDeep method.
Implement your own cloning function.
Alternative Solution To Your Specific Problem (No Deep Copy)
However, I think, if you're willing to change a couple things, you can save yourself some work. I'm assuming you control all call sites to your function.
Specify that all callbacks passed to mapCopy must return new objects instead of mutating the existing object. For example:
mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: 'new item'
});
} else {
return e;
}
});
This makes use of Object.assign to create a new object, sets properties of e on that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.
mapCopy can be really simple now:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}
Essentially, mapCopy is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.
From MDN
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).
Personally, I suggest using Lodash's cloneDeep function for multi-level object/array cloning.
Here is a working example:
const arr1 = [{ 'a': 1 }];
const arr2 = [...arr1];
const arr3 = _.clone(arr1);
const arr4 = arr1.slice();
const arr5 = _.cloneDeep(arr1);
const arr6 = [...{...arr1}]; // a bit ugly syntax but it is working!
// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false
// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
I often use this:
function deepCopy(obj) {
if(typeof obj !== 'object' || obj === null) {
return obj;
}
if(obj instanceof Date) {
return new Date(obj.getTime());
}
if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}
if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}
You can use structuredClone() like the following:
const myOriginal = {
title: "Full Stack JavaScript Developer",
info: {
firstname: "Abolfazl",
surname: "Roshanzamir",
age: 34
}
};
const myDeepCopy = structuredClone(myOriginal);
structuredClone()
You can use structuredClone() that is a built-in function for deep-copying.
Structured cloning addresses many (although not all) shortcomings of the JSON.stringify() technique.
Structured cloning can handle cyclical data structures,
support many built-in data types, and is generally more robust and often faster.
However, it still has some limitations that may catch you off-guard:
1-Prototypes : If you use structuredClone() with a class instance,
you’ll get a plain object as the return value, as structured cloning discards the object’s prototype chain.
2-Functions: If your object contains functions, they will be quietly discarded.
3- Non-cloneables: Some values are not structured cloneable, most notably Error and DOM nodes. It will cause structuredClone() to throw.
const myDeepCopy = structuredClone(myOriginal);
JSON.stringify
If you simply want to deep copy the object to another object,
all you will need to do is JSON.stringify the object and parse it using JSON.parse afterward.
This will essentially perform deep copying of the object.
let user1 = {
name: 'Abolfazl Roshanzamir',
age: 34,
university: {
name: 'Shiraz Bahonar University'
}
};
let user2 = JSON.parse(JSON.stringify(user1));
user2.name = 'Andy Madadian';
user2.university.name = 'Kerman Bahonar University'
console.log(user2);
// { name: 'Andy Madadian', age: 33, university: { name: 'Kerman Bahonar University' } }
console.log(user1);
// { name: 'Abolfazl Roshanzamir', age: 33, university: { name: 'Shiraz Bahonar University' } }
Spread operator / Object.assign()
One way to create a shallow copy in JavaScript using the object spread operator ... or Object.assign() like the following:
const myShallowCopySpread = {...myOriginal};
const myShallowCopyObjectAssign=Object.assign({},obj)
Performance
When it comes to performance the creator Surma has pointed out that JSON.Parse() can be a bit faster for small objects. But when you have a large object, complex object
structuredClone() starts to get significantly faster.
Browser support is pretty fantastic And even is supported by Node.js.
const a = {
foods: {
dinner: 'Pasta'
}
}
let b = JSON.parse(JSON.stringify(a))
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta
Using JSON.stringify and JSON.parse is the best way. Because by using the spread operator we will not get the efficient answer when the json object contains another object inside it. we need to manually specify that.
Here's my deep copy algorithm.
const DeepClone = (obj) => {
if(obj===null||typeof(obj)!=='object')return null;
let newObj = { ...obj };
for (let prop in obj) {
if (
typeof obj[prop] === "object" ||
typeof obj[prop] === "function"
) {
newObj[prop] = DeepClone(obj[prop]);
}
}
return newObj;
};
// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
// return non object values
if('object' !==typeof o) return o
// m: a map of old refs to new object refs to stop recursion
if('object' !==typeof m || null ===m) m =new WeakMap()
var n =m.get(o)
if('undefined' !==typeof n) return n
// shallow/leaf clone object
var c =Object.getPrototypeOf(o).constructor
// TODO: specialize copies for expected built in types i.e. Date etc
switch(c) {
// shouldn't be copied, keep reference
case Boolean:
case Error:
case Function:
case Number:
case Promise:
case String:
case Symbol:
case WeakMap:
case WeakSet:
n =o
break;
// array like/collection objects
case Array:
m.set(o, n =o.slice(0))
// recursive copy for child objects
n.forEach(function(v,i){
if('object' ===typeof v) n[i] =clone(v, m)
});
break;
case ArrayBuffer:
m.set(o, n =o.slice(0))
break;
case DataView:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
break;
case Map:
case Set:
m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
break;
case Int8Array:
case Uint8Array:
case Uint8ClampedArray:
case Int16Array:
case Uint16Array:
case Int32Array:
case Uint32Array:
case Float32Array:
case Float64Array:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
break;
// use built in copy constructor
case Date:
case RegExp:
m.set(o, n =new (c)(o))
break;
// fallback generic object copy
default:
m.set(o, n =Object.assign(new (c)(), o))
// recursive copy for child objects
for(c in n) if('object' ===typeof n[c]) n[c] =clone(n[c], m)
}
return n
}
Here is the deepClone function which handles all primitive, array, object, function data types
function deepClone(obj){
if(Array.isArray(obj)){
var arr = [];
for (var i = 0; i < obj.length; i++) {
arr[i] = deepClone(obj[i]);
}
return arr;
}
if(typeof(obj) == "object"){
var cloned = {};
for(let key in obj){
cloned[key] = deepClone(obj[key])
}
return cloned;
}
return obj;
}
console.log( deepClone(1) )
console.log( deepClone('abc') )
console.log( deepClone([1,2]) )
console.log( deepClone({a: 'abc', b: 'def'}) )
console.log( deepClone({
a: 'a',
num: 123,
func: function(){'hello'},
arr: [[1,2,3,[4,5]], 'def'],
obj: {
one: {
two: {
three: 3
}
}
}
}) )
function deepclone(obj) {
let newObj = {};
if (typeof obj === 'object') {
for (let key in obj) {
let property = obj[key],
type = typeof property;
switch (type) {
case 'object':
if( Object.prototype.toString.call( property ) === '[object Array]' ) {
newObj[key] = [];
for (let item of property) {
newObj[key].push(this.deepclone(item))
}
} else {
newObj[key] = deepclone(property);
}
break;
default:
newObj[key] = property;
break;
}
}
return newObj
} else {
return obj;
}
}
const cloneData = (dataArray) => {
newData= []
dataArray.forEach((value) => {
newData.push({...value})
})
return newData
}
a = [{name:"siva"}, {name:"siva1"}] ;
b = myCopy(a)
b === a // false`
I myself landed on these answers last day, trying to find a way to deep copy complex structures, which may include recursive links. As I wasn't satisfied with anything being suggested before, I implemented this wheel myself. And it works quite well. Hope it helps someone.
Example usage:
OriginalStruct.deep_copy = deep_copy; // attach the function as a method
TheClone = OriginalStruct.deep_copy();
Please look at https://github.com/latitov/JS_DeepCopy for live examples how to use it, and also deep_print() is there.
If you need it quick, right here's the source of deep_copy() function:
function deep_copy() {
'use strict'; // required for undef test of 'this' below
// Copyright (c) 2019, Leonid Titov, Mentions Highly Appreciated.
var id_cnt = 1;
var all_old_objects = {};
var all_new_objects = {};
var root_obj = this;
if (root_obj === undefined) {
console.log(`deep_copy() error: wrong call context`);
return;
}
var new_obj = copy_obj(root_obj);
for (var id in all_old_objects) {
delete all_old_objects[id].__temp_id;
}
return new_obj;
//
function copy_obj(o) {
var new_obj = {};
if (o.__temp_id === undefined) {
o.__temp_id = id_cnt;
all_old_objects[id_cnt] = o;
all_new_objects[id_cnt] = new_obj;
id_cnt ++;
for (var prop in o) {
if (o[prop] instanceof Array) {
new_obj[prop] = copy_array(o[prop]);
}
else if (o[prop] instanceof Object) {
new_obj[prop] = copy_obj(o[prop]);
}
else if (prop === '__temp_id') {
continue;
}
else {
new_obj[prop] = o[prop];
}
}
}
else {
new_obj = all_new_objects[o.__temp_id];
}
return new_obj;
}
function copy_array(a) {
var new_array = [];
if (a.__temp_id === undefined) {
a.__temp_id = id_cnt;
all_old_objects[id_cnt] = a;
all_new_objects[id_cnt] = new_array;
id_cnt ++;
a.forEach((v,i) => {
if (v instanceof Array) {
new_array[i] = copy_array(v);
}
else if (v instanceof Object) {
new_array[i] = copy_object(v);
}
else {
new_array[i] = v;
}
});
}
else {
new_array = all_new_objects[a.__temp_id];
}
return new_array;
}
}
Cheers#!
I would suggest using the spread operator. You'll need to spread a second time if you need to update the second level. Attempting to update the newObject using something like newObject.address.city will throw an error if address did not already exist in oldObject.
const oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
const newObject = {
...oldObject,
address: {
...oldObject.address,
city: 'Delhi'
}
}
console.log(newObject)
This is a very old question but I think in 2022 there are many ways to solve this. However, if you want a simple, fast and vanilla JS solution check this out:
const cloner = (o) => {
let idx = 1
const isArray = (a) => a instanceof Array
const isObject = (o) => o instanceof Object
const isUndefined = (a) => a === undefined
const process = v => {
if (isArray(v)) return cloneArray(v)
else if (isObject(v)) return cloneObject(v)
else return v
}
const register = (old, o) => {
old.__idx = idx
oldObjects[idx] = old
newObjects[idx] = o
idx++
}
const cloneObject = o => {
if (!isUndefined(o.__idx)) return newObjects[o.__idx]
const obj = {}
for (const prop in o) {
if (prop === '__idx') continue
obj[prop] = process(o[prop])
}
register(o, obj)
return obj
}
const cloneArray = a => {
if (!isUndefined(a.__idx)) return newObjects[a.__idx]
const arr = a.map((v) => process(v))
register(a, arr)
return arr
}
const oldObjects = {}
const newObjects = {}
let tmp
if (isArray(o)) tmp = cloneArray(o)
else if (isObject(o)) tmp = cloneObject(o)
else return o
for (const id in oldObjects) delete oldObjects[id].__idx
return tmp
}
const c = {
id: 123,
label: "Lala",
values: ['char', 1, {flag: true}, [1,2,3,4,5], ['a', 'b']],
name: undefined
}
const d = cloner(c)
d.name = "Super"
d.values[2].flag = false
d.values[3] = [6,7,8]
console.log({ c, d })
It's recursive and self-contained, all the functions needed are defined in the function cloner().
In this snippet we are handling Array and Object types if you want to add more handlers you can add specify handlers like Date and clone it like new Date(v.getTime())
For me Array and Object are the types that I use the most in my implementations.
I am trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, {...object[key]});
return output;
},
{});
}
It works:
return mapCopy(state, e => {
if (e.id === action.id) {
e.title = 'new item';
}
return e;
})
However it does not deep copy inner items so I need to tweak it to:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
let newObject = {...object[key]};
newObject.style = {...newObject.style};
newObject.data = {...newObject.data};
output[key] = callback.call(this, newObject);
return output;
}, {});
}
This is less elegant as it requires to know which objects are passed.
Is there a way in ES6 to use the spread syntax to deep copy an object?
Use JSON for deep copy
var newObject = JSON.parse(JSON.stringify(oldObject))
var oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));
newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);
No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.
If you really want to deep copy:
Use a library. For example, lodash has a cloneDeep method.
Implement your own cloning function.
Alternative Solution To Your Specific Problem (No Deep Copy)
However, I think, if you're willing to change a couple things, you can save yourself some work. I'm assuming you control all call sites to your function.
Specify that all callbacks passed to mapCopy must return new objects instead of mutating the existing object. For example:
mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: 'new item'
});
} else {
return e;
}
});
This makes use of Object.assign to create a new object, sets properties of e on that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.
mapCopy can be really simple now:
export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}
Essentially, mapCopy is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.
From MDN
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).
Personally, I suggest using Lodash's cloneDeep function for multi-level object/array cloning.
Here is a working example:
const arr1 = [{ 'a': 1 }];
const arr2 = [...arr1];
const arr3 = _.clone(arr1);
const arr4 = arr1.slice();
const arr5 = _.cloneDeep(arr1);
const arr6 = [...{...arr1}]; // a bit ugly syntax but it is working!
// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false
// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
I often use this:
function deepCopy(obj) {
if(typeof obj !== 'object' || obj === null) {
return obj;
}
if(obj instanceof Date) {
return new Date(obj.getTime());
}
if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}
if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}
You can use structuredClone() like the following:
const myOriginal = {
title: "Full Stack JavaScript Developer",
info: {
firstname: "Abolfazl",
surname: "Roshanzamir",
age: 34
}
};
const myDeepCopy = structuredClone(myOriginal);
structuredClone()
You can use structuredClone() that is a built-in function for deep-copying.
Structured cloning addresses many (although not all) shortcomings of the JSON.stringify() technique.
Structured cloning can handle cyclical data structures,
support many built-in data types, and is generally more robust and often faster.
However, it still has some limitations that may catch you off-guard:
1-Prototypes : If you use structuredClone() with a class instance,
you’ll get a plain object as the return value, as structured cloning discards the object’s prototype chain.
2-Functions: If your object contains functions, they will be quietly discarded.
3- Non-cloneables: Some values are not structured cloneable, most notably Error and DOM nodes. It will cause structuredClone() to throw.
const myDeepCopy = structuredClone(myOriginal);
JSON.stringify
If you simply want to deep copy the object to another object,
all you will need to do is JSON.stringify the object and parse it using JSON.parse afterward.
This will essentially perform deep copying of the object.
let user1 = {
name: 'Abolfazl Roshanzamir',
age: 34,
university: {
name: 'Shiraz Bahonar University'
}
};
let user2 = JSON.parse(JSON.stringify(user1));
user2.name = 'Andy Madadian';
user2.university.name = 'Kerman Bahonar University'
console.log(user2);
// { name: 'Andy Madadian', age: 33, university: { name: 'Kerman Bahonar University' } }
console.log(user1);
// { name: 'Abolfazl Roshanzamir', age: 33, university: { name: 'Shiraz Bahonar University' } }
Spread operator / Object.assign()
One way to create a shallow copy in JavaScript using the object spread operator ... or Object.assign() like the following:
const myShallowCopySpread = {...myOriginal};
const myShallowCopyObjectAssign=Object.assign({},obj)
Performance
When it comes to performance the creator Surma has pointed out that JSON.Parse() can be a bit faster for small objects. But when you have a large object, complex object
structuredClone() starts to get significantly faster.
Browser support is pretty fantastic And even is supported by Node.js.
const a = {
foods: {
dinner: 'Pasta'
}
}
let b = JSON.parse(JSON.stringify(a))
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta
Using JSON.stringify and JSON.parse is the best way. Because by using the spread operator we will not get the efficient answer when the json object contains another object inside it. we need to manually specify that.
Here's my deep copy algorithm.
const DeepClone = (obj) => {
if(obj===null||typeof(obj)!=='object')return null;
let newObj = { ...obj };
for (let prop in obj) {
if (
typeof obj[prop] === "object" ||
typeof obj[prop] === "function"
) {
newObj[prop] = DeepClone(obj[prop]);
}
}
return newObj;
};
// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
// return non object values
if('object' !==typeof o) return o
// m: a map of old refs to new object refs to stop recursion
if('object' !==typeof m || null ===m) m =new WeakMap()
var n =m.get(o)
if('undefined' !==typeof n) return n
// shallow/leaf clone object
var c =Object.getPrototypeOf(o).constructor
// TODO: specialize copies for expected built in types i.e. Date etc
switch(c) {
// shouldn't be copied, keep reference
case Boolean:
case Error:
case Function:
case Number:
case Promise:
case String:
case Symbol:
case WeakMap:
case WeakSet:
n =o
break;
// array like/collection objects
case Array:
m.set(o, n =o.slice(0))
// recursive copy for child objects
n.forEach(function(v,i){
if('object' ===typeof v) n[i] =clone(v, m)
});
break;
case ArrayBuffer:
m.set(o, n =o.slice(0))
break;
case DataView:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
break;
case Map:
case Set:
m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
break;
case Int8Array:
case Uint8Array:
case Uint8ClampedArray:
case Int16Array:
case Uint16Array:
case Int32Array:
case Uint32Array:
case Float32Array:
case Float64Array:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
break;
// use built in copy constructor
case Date:
case RegExp:
m.set(o, n =new (c)(o))
break;
// fallback generic object copy
default:
m.set(o, n =Object.assign(new (c)(), o))
// recursive copy for child objects
for(c in n) if('object' ===typeof n[c]) n[c] =clone(n[c], m)
}
return n
}
Here is the deepClone function which handles all primitive, array, object, function data types
function deepClone(obj){
if(Array.isArray(obj)){
var arr = [];
for (var i = 0; i < obj.length; i++) {
arr[i] = deepClone(obj[i]);
}
return arr;
}
if(typeof(obj) == "object"){
var cloned = {};
for(let key in obj){
cloned[key] = deepClone(obj[key])
}
return cloned;
}
return obj;
}
console.log( deepClone(1) )
console.log( deepClone('abc') )
console.log( deepClone([1,2]) )
console.log( deepClone({a: 'abc', b: 'def'}) )
console.log( deepClone({
a: 'a',
num: 123,
func: function(){'hello'},
arr: [[1,2,3,[4,5]], 'def'],
obj: {
one: {
two: {
three: 3
}
}
}
}) )
function deepclone(obj) {
let newObj = {};
if (typeof obj === 'object') {
for (let key in obj) {
let property = obj[key],
type = typeof property;
switch (type) {
case 'object':
if( Object.prototype.toString.call( property ) === '[object Array]' ) {
newObj[key] = [];
for (let item of property) {
newObj[key].push(this.deepclone(item))
}
} else {
newObj[key] = deepclone(property);
}
break;
default:
newObj[key] = property;
break;
}
}
return newObj
} else {
return obj;
}
}
const cloneData = (dataArray) => {
newData= []
dataArray.forEach((value) => {
newData.push({...value})
})
return newData
}
a = [{name:"siva"}, {name:"siva1"}] ;
b = myCopy(a)
b === a // false`
I myself landed on these answers last day, trying to find a way to deep copy complex structures, which may include recursive links. As I wasn't satisfied with anything being suggested before, I implemented this wheel myself. And it works quite well. Hope it helps someone.
Example usage:
OriginalStruct.deep_copy = deep_copy; // attach the function as a method
TheClone = OriginalStruct.deep_copy();
Please look at https://github.com/latitov/JS_DeepCopy for live examples how to use it, and also deep_print() is there.
If you need it quick, right here's the source of deep_copy() function:
function deep_copy() {
'use strict'; // required for undef test of 'this' below
// Copyright (c) 2019, Leonid Titov, Mentions Highly Appreciated.
var id_cnt = 1;
var all_old_objects = {};
var all_new_objects = {};
var root_obj = this;
if (root_obj === undefined) {
console.log(`deep_copy() error: wrong call context`);
return;
}
var new_obj = copy_obj(root_obj);
for (var id in all_old_objects) {
delete all_old_objects[id].__temp_id;
}
return new_obj;
//
function copy_obj(o) {
var new_obj = {};
if (o.__temp_id === undefined) {
o.__temp_id = id_cnt;
all_old_objects[id_cnt] = o;
all_new_objects[id_cnt] = new_obj;
id_cnt ++;
for (var prop in o) {
if (o[prop] instanceof Array) {
new_obj[prop] = copy_array(o[prop]);
}
else if (o[prop] instanceof Object) {
new_obj[prop] = copy_obj(o[prop]);
}
else if (prop === '__temp_id') {
continue;
}
else {
new_obj[prop] = o[prop];
}
}
}
else {
new_obj = all_new_objects[o.__temp_id];
}
return new_obj;
}
function copy_array(a) {
var new_array = [];
if (a.__temp_id === undefined) {
a.__temp_id = id_cnt;
all_old_objects[id_cnt] = a;
all_new_objects[id_cnt] = new_array;
id_cnt ++;
a.forEach((v,i) => {
if (v instanceof Array) {
new_array[i] = copy_array(v);
}
else if (v instanceof Object) {
new_array[i] = copy_object(v);
}
else {
new_array[i] = v;
}
});
}
else {
new_array = all_new_objects[a.__temp_id];
}
return new_array;
}
}
Cheers#!
I would suggest using the spread operator. You'll need to spread a second time if you need to update the second level. Attempting to update the newObject using something like newObject.address.city will throw an error if address did not already exist in oldObject.
const oldObject = {
name: 'A',
address: {
street: 'Station Road',
city: 'Pune'
}
}
const newObject = {
...oldObject,
address: {
...oldObject.address,
city: 'Delhi'
}
}
console.log(newObject)
This is a very old question but I think in 2022 there are many ways to solve this. However, if you want a simple, fast and vanilla JS solution check this out:
const cloner = (o) => {
let idx = 1
const isArray = (a) => a instanceof Array
const isObject = (o) => o instanceof Object
const isUndefined = (a) => a === undefined
const process = v => {
if (isArray(v)) return cloneArray(v)
else if (isObject(v)) return cloneObject(v)
else return v
}
const register = (old, o) => {
old.__idx = idx
oldObjects[idx] = old
newObjects[idx] = o
idx++
}
const cloneObject = o => {
if (!isUndefined(o.__idx)) return newObjects[o.__idx]
const obj = {}
for (const prop in o) {
if (prop === '__idx') continue
obj[prop] = process(o[prop])
}
register(o, obj)
return obj
}
const cloneArray = a => {
if (!isUndefined(a.__idx)) return newObjects[a.__idx]
const arr = a.map((v) => process(v))
register(a, arr)
return arr
}
const oldObjects = {}
const newObjects = {}
let tmp
if (isArray(o)) tmp = cloneArray(o)
else if (isObject(o)) tmp = cloneObject(o)
else return o
for (const id in oldObjects) delete oldObjects[id].__idx
return tmp
}
const c = {
id: 123,
label: "Lala",
values: ['char', 1, {flag: true}, [1,2,3,4,5], ['a', 'b']],
name: undefined
}
const d = cloner(c)
d.name = "Super"
d.values[2].flag = false
d.values[3] = [6,7,8]
console.log({ c, d })
It's recursive and self-contained, all the functions needed are defined in the function cloner().
In this snippet we are handling Array and Object types if you want to add more handlers you can add specify handlers like Date and clone it like new Date(v.getTime())
For me Array and Object are the types that I use the most in my implementations.
I've come up with
function keysToLowerCase (obj) {
var keys = Object.keys(obj);
var n = keys.length;
while (n--) {
var key = keys[n]; // "cache" it, for less lookups to the array
if (key !== key.toLowerCase()) { // might already be in its lower case version
obj[key.toLowerCase()] = obj[key] // swap the value to a new lower case key
delete obj[key] // delete the old key
}
}
return (obj);
}
But I'm not sure how will v8 behave with that, for instance, will it really delete the other keys or will it only delete references and the garbage collector will bite me later ?
Also, I created these tests, I'm hoping you could add your answer there so we could see how they match up.
EDIT 1:
Apparently, according to the tests, it's faster if we don't check if the key is already in lower case, but being faster aside, will it create more clutter by ignoring this and just creating new lower case keys ? Will the garbage collector be happy with this ?
The fastest I come up with is if you create a new object:
var key, keys = Object.keys(obj);
var n = keys.length;
var newobj={}
while (n--) {
key = keys[n];
newobj[key.toLowerCase()] = obj[key];
}
I'm not familiar enough with the current inner working of v8 to give you a definitive answer. A few years ago I saw a video where the developers talked about objects, and IIRC
it will only delete the references and let the garbage collector take care of it. But it was years ago so even if it was like that then, it doesn't need to be like that now.
Will it bite you later? It depends on what you are doing, but probably not. It is very common to create short lived objects so the code is optimized to handle it. But every environment has its limitations, and maybe it will bite you. You have to test with actual data.
Using Object.fromEntries (ES10)
Native and immutable solution using the new Object.fromEntries method:
const newObj = Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])
);
Until that function becomes widely available you could define it yourself with the following polyfill:
Object.fromEntries = arr => Object.assign({}, ...Array.from(arr, ([k, v]) => ({[k]: v}) ));
A nice thing is that this method does the opposite of Object.entries, so now you can go back and forth between the object and array representation.
I'd use Lo-Dash.transform like this:
var lowerObj = _.transform(obj, function (result, val, key) {
result[key.toLowerCase()] = val;
});
Personally, I'd use:
let objectKeysToLowerCase = function (origObj) {
return Object.keys(origObj).reduce(function (newObj, key) {
let val = origObj[key];
let newVal = (typeof val === 'object') ? objectKeysToLowerCase(val) : val;
newObj[key.toLowerCase()] = newVal;
return newObj;
}, {});
}
It's succinct, recurs to handle nested objects and returns a new object rather than modifying the original.
In my limited local testing this function is faster than the other recursive solution currently listed (once fixed). I'd love to benchmark it against the others but jsperf is down at the moment (???).
It's also written in ES5.1 so, according to the docs on MDN, should work in FF 4+, Chrome 5+, IE 9.0+, Opera 12+, Safari 5+ (so, pretty much everything).
Object.keys()
Array. prototype.reduce()
Vanilla JS for the win.
I wouldn't worry too much about the garbage collection aspect of all this. Once all references to the old object are destroyed it will be GC's but the new object will still reference basically all it's properties, so they will not.
Any Functions, Arrays or RegExp will be "copied" across by reference. In terms of memory, even Strings will not be duplicated by this process since most (all?) modern JS engines user string interning. I think that leaves just the Numbers, Booleans and the Objects that formed the original structure left to be GC'd.
Note that (all implementations of) this process will lose values if the original has multiple properties with the same lowercase representation. Ie:
let myObj = { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' };
console.log(myObj);
// { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' }
let newObj = objectKeysToLowerCase(myObj);
console.log(newObj);
// { xx: 'one!' }
Of course, sometimes this is exactly what you want.
Update 2018-07-17
A few people have noted the original function doesn't work well with arrays. Here's an expanded, more resilient version. It recurs correctly through arrays and works if the initial value is an array or simple value:
let objectKeysToLowerCase = function (input) {
if (typeof input !== 'object') return input;
if (Array.isArray(input)) return input.map(objectKeysToLowerCase);
return Object.keys(input).reduce(function (newObj, key) {
let val = input[key];
let newVal = (typeof val === 'object') && val !== null ? objectKeysToLowerCase(val) : val;
newObj[key.toLowerCase()] = newVal;
return newObj;
}, {});
};
ES6 version:
Object.keys(source)
.reduce((destination, key) => {
destination[key.toLowerCase()] = source[key];
return destination;
}, {});
The loDash/fp way, quite nice as its essentially a one liner
import {
mapKeys
} from 'lodash/fp'
export function lowerCaseObjectKeys (value) {
return mapKeys(k => k.toLowerCase(), value)
}
Using forEach seems to be a bit quicker in my tests- and the original reference is gone, so deleting the new one will put it in reach of the g.c.
function keysToLowerCase(obj){
Object.keys(obj).forEach(function (key) {
var k = key.toLowerCase();
if (k !== key) {
obj[k] = obj[key];
delete obj[key];
}
});
return (obj);
}
var O={ONE:1,two:2,tHree:3,FOUR:4,Five:5,SIX:{a:1,b:2,c:3,D:4,E:5}};
keysToLowerCase(O);
/* returned value: (Object) */
{
five:5,
four:4,
one:1,
six:{
a:1,
b:2,
c:3,
D:4,
E:5
},
three:3,
two:2
}
Simplified Answer
For simple situations, you can use the following example to convert all keys to lower case:
Object.keys(example).forEach(key => {
const value = example[key];
delete example[key];
example[key.toLowerCase()] = value;
});
You can convert all of the keys to upper case using toUpperCase() instead of toLowerCase():
Object.keys(example).forEach(key => {
const value = example[key];
delete example[key];
example[key.toUpperCase()] = value;
});
Here is easiest solution to convert all the json keys to lower case.
let o = {"Account_Number ":"0102301", "customer_NaME":"name"}
o = Object.keys(o).reduce((c, k) => (c[k.toLowerCase().trim()] = o[k], c), {})
console.log(o)
With TypeScript
/**
* Lowercase the keys of an object
* #example
lowercaseKeys({FOO: true, bAr: false}); // {foo: true, bar: false}
*/
export function lowercaseKeys<T>(object: { [key: string]: T }): { [key: string]: T } {
const result: { [key: string]: T } = {};
for (const [key, value] of Object.entries(object)) {
result[key.toLowerCase()] = value;
}
return result;
}
Usage
lowercaseKeys({FOO: true, bAr: false}); // {foo: true, bar: false}
I used ES6 and TypeScript.
toLowerCaseObject function takes an Array as parameter and looking through Object tree recursively and make every node lowercase:
function toLowerCaseObject(items: any[]) {
return items.map(x => {
let lowerCasedObject = {}
for (let i in x) {
if (x.hasOwnProperty(i)) {
lowerCased[i.toLowerCase()] = x[i] instanceof Array ? toLowerCaseObject(x[i]) : x[i];
}
}
return lowerCasedObject;
});
}
One-liner (only for top level keys):
Object.assign(...Object.keys(obj).map(key => ({[key.toLowerCase()]: obj[key]})))
Converts:
{ a: 1, B: 2, C: { Z: 4 } }
To:
{ a: 1, b: 2, c: { Z: 4 } }
While the ES10 Object.fromentries() method works
const newObj = Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])
);
You can similarly use the snippet below for ES2015 and below
this.htmlWorkbookJSON = jsonData.map((element: Object) => {
let entriesArray = Object.entries(element)
const data = new Object()
entriesArray.forEach(([key, value]) => {
data[key.toLocaleLowerCase()] = value;
})
return data
})
This is not the cleanest way but it has worked for my team so it is worth sharing.
I created this method as our backend is running a language that is not case sensitive and the database and backend will produce different key cases. For us, it has worked flawlessly. Mind you we send dates as Strings and we don't send functions.
We have reduced it to this one line.
const toLowerCase = (data) => JSON.parse(JSON.stringify(data).replace(/"([^"]+)":/g, ($0, key) => '"' + key.toString().toLowerCase() + '":'))
We clone the object by using the JSON.parse(JSON.stringify(obj)) method. This produces a string version of the object in the JSON format. While the object is in the string form you can use regex as JSON is a predictable format to convert all keys.
Broken up it looks like this.
const toLowerCase = function (data) {
return JSON.parse(JSON.stringify(data)
.replace(/"([^"]+)":/g, ($0, key) => {
return '"' + key.toString().toLowerCase() + '":'
}))
}
const keysToLowerCase = object => {
return Object.keys(object).reduce((acc, key) => {
let val = object[key];
if (typeof val === 'object') {
val = keysToLowerCase(val);
}
acc[key.toLowerCase()] = val;
return acc;
}, {});
};
Works for nested object.
Consider lowering case just once, storing it in a lowKey var:
function keysToLowerCase (obj) {
var keys = Object.keys(obj);
var n = keys.length;
var lowKey;
while (n--) {
var key = keys[n];
if (key === (lowKey = key.toLowerCase()))
continue
obj[lowKey] = obj[key]
delete obj[key]
}
return (obj);
}
Here's my recursive version based on one of the above examples.
//updated function
var lowerObjKeys = function(obj) {
Object.keys(obj).forEach(function(key) {
var k = key.toLowerCase();
if (k != key) {
var v = obj[key]
obj[k] = v;
delete obj[key];
if (typeof v == 'object') {
lowerObjKeys(v);
}
}
});
return obj;
}
//plumbing
console = {
_createConsole: function() {
var pre = document.createElement('pre');
pre.setAttribute('id', 'console');
document.body.insertBefore(pre, document.body.firstChild);
return pre;
},
info: function(message) {
var pre = document.getElementById("console") || console._createConsole();
pre.textContent += ['>', message, '\n'].join(' ');
}
};
//test case
console.info(JSON.stringify(lowerObjKeys({
"StackOverflow": "blah",
"Test": {
"LULZ": "MEH"
}
}), true));
Beware, it doesn't track circular references, so you can end up with an infinite loop resulting in stack overflow.
For all values:
to_lower_case = function(obj) {
for (var k in obj){
if (typeof obj[k] == "object" && obj[k] !== null)
to_lower_case(obj[k]);
else if(typeof obj[k] == "string") {
obj[k] = obj[k].toLowerCase();
}
}
return obj;
}
Same can be used for keys with minor tweaks.
This is how I do it. My input can be anything and it recuses through nested objects as well as arrays of objects.
const fixKeys = input => Array.isArray(input)
? input.map(fixKeys)
: typeof input === 'object'
? Object.keys(input).reduce((acc, elem) => {
acc[elem.toLowerCase()] = fixKeys(input[elem])
return acc
}, {})
: input
tested using mocha
const { expect } = require('chai')
const fixKeys = require('../../../src/utils/fixKeys')
describe('utils/fixKeys', () => {
const original = {
Some: 'data',
With: {
Nested: 'data'
},
And: [
'an',
'array',
'of',
'strings'
],
AsWellAs: [
{ An: 'array of objects' }
]
}
const expected = {
some: 'data',
with: {
nested: 'data'
},
and: [
'an',
'array',
'of',
'strings'
],
aswellas: [{ an: 'array of objects' }]
}
let result
before(() => {
result = fixKeys(original)
})
it('left the original untouched', () => {
expect(original).not.to.deep.equal(expected)
})
it('fixed the keys', () => {
expect(result).to.deep.equal(expected)
})
})
var aa = {ID:1,NAME:'Guvaliour'};
var bb= {};
var cc = Object.keys(aa);
cc.forEach(element=>{
bb[element.toLowerCase()]=aa[element];
});
cosole.log(bb)
The below code to convert the all key in lower case
array.forEach(item=>{
let data = Object.keys(item).reduce((result, p) => (result[p.toLowerCase().trim()] = item[p], result), {})
if(data.hasOwnProperty(fieldname)){
if(data[fieldname]){
if(!response['data'].includes(data[fieldname].toLowerCase()))
response['data'].push(data[fieldname])
}
}
})
const newObj = {};
for(const key in obj){
newObj[key.toLowerCase()] = obj[key];
}
Most of the above answers do not handle null and undefined values. To get around it why not use the transform helper function from lodash?
const query = {
Company: 'GH Works',
Items: {
Construction: 'FB',
LineItems: {
Quantity: '100',
QUALity: 'checked'
}
}
}
function deepLowercaseKeys(hash) {
return _.transform(hash, function(result, value, key) {
const valueIsObject = typeof value === 'object';
result[key.toLowerCase()] = valueIsObject ? deepLowercaseKeys(value) : value;
});
}
console.log(deepLowercaseKeys(query))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Additionally, you can customize the function and then use it to transform the object in any way you like.
const query = {
Company: 'GH Works',
Items: {
Construction: 'FB',
LineItems: {
Quantity: '100',
QUALity: 'checked'
}
}
}
// Base function
function deepTransform(hash, callback) {
return _.transform(hash, function(result, value, key) {
if (typeof value === 'object') {
return callback(result, deepTransform(value, callback), key)
}
callback(result, value, key)
})
}
// Custom function (can be anything)
function appendHello(hash) {
return deepTransform(hash, function(result, value, key) {
result[`${key}_hello`.toLowerCase()] = value;
})
}
console.log(appendHello(query))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
const objectToLowercase = (data) => {
const values = Object.values(data);
if (values.length === 0) {
return data;
}
return Object.keys(data).reduce((toLowerKeyObj, key) => {
const isObject = typeof data[key] === 'object';
const isArray = Array.isArray(data[key]);
let value = null;
if (isObject) {
if (!isArray) {
value = objectToLowercase(data[key]);
}
}
if (isArray) {
value = data[key].map(_value => {
return objectToLowercase(_value);
});
}
toLowerKeyObj[key.toLowerCase()] = isObject ? value : data[key];
return toLowerKeyObj;
}, {});
};