How to copy an object in javascript? - javascript

I am trying to change only the value of copied object, not the main but both objects change as of the result of running this code.
const Randomdata = {
a: 10,
b: 5,
c: {
f: "value1",
q: "value2"
}
};
function copy(MainObj) {
let ObjCopy = {};
for (let key in MainObj) {
ObjCopy[key] = MainObj[key];
}
return ObjCopy;
}
const newObj = copy(Randomdata);
Randomdata.c.q = "value3";
console.log(newObj);
console.log(Randomdata);

Though JSON.parse and JSON.stringify will work , but you will need a recusive function if want to achieve the same result using for..in. The reason is value of key c is a object and when you are copying it, it is just referencing to the old copy
const Randomdata = {
a: 10,
b: 5,
c: {
f: "value1",
q: "value2"
}
};
function copy(MainObj, ObjCopy) {
for (let key in MainObj) {
// check if the value is a object , if so then
// reclusively call the same function
if (typeof MainObj[key] === 'object') {
copy(MainObj[key], ObjCopy)
} else {
ObjCopy[key] = MainObj[key];
}
}
return ObjCopy;
}
const newObj = copy(Randomdata, {});
Randomdata.c.q = "value3";
console.log(newObj);
console.log(Randomdata);

Related

How to recursively map/restructure an object?

I would like an array of objects with all object keys from a nested object. I wrote a recursive function to do this however at the point that the function is recalled it is not going through the object as expected but rather sending back an index infinitely.
let array = [];
const findKeys = (ob) => {
let id = 0;
let keys = Object.keys(ob);
for (let i = 0; i < keys.length; i++) {
let object = {
id: id,
label: keys[i],
};
array.push(object);
id ++;
findKeys(ob[keys[i]]);
}
return array;
};
let newArray = findKeys(data);
console.log(newArray);
example data structure:
const data = {a: {
b: {
c: {
foo: 'bar'
}
}
}}
You need to check to see if you have an object before you do the next recursive call. You also are resetting id so you are going to have the ids repeated (maybe you want that?) and you are using a global for the array so it can not be used more than once.
You are going to want something like:
function getKeys(obj) {
const array = [];
let id = 0;
function loop(obj) {
Object.entries(obj).forEach(entry => {
array.push({
id: ++id,
label: entry[0],
});
if(entry[1] != null && entry[1].constructor.name === "Object") {
loop(entry[1]);
}
});
}
loop(obj);
return array;
}
const obj1 = { a: 1, b: 'bar' };
console.log(getKeys(obj1));
const obj2 = { a: 1, b: { c: 'bar' } };
console.log(getKeys(obj2));
some thing like that
see also Check that value is object literal?
const data = { a: { b: { c: { foo: 'bar' } } }}
const isObject = el => (Object.prototype.toString.call(el) === '[object Object]')
const findKeys = obj =>
{
let arr = []
, id = 0
;
getKeys(obj)
return arr
function getKeys(o)
{
Object.keys(o).forEach(key =>
{
arr.push({ id:id++, label:key })
if (isObject(o[key]))
getKeys(o[key])
})
}
}
console.log( findKeys(data) )
.as-console-wrapper {max-height: 100%!important;top:0 }
Perhaps you may do like
var data = {a: {
b: {
c: {
foo: 'bar',
arr: [1,2,3,4]
}
}
}};
function getAllKeys(obj){
var keys = (typeof obj === "object") && (obj !== null) && Object.keys(obj);
return !!keys ? keys.reduce((r,k) => r.concat(getAllKeys(obj[k])),keys)
: [];
};
var res = getAllKeys(data);
console.log(JSON.stringify(res));
Here is a simple technique, using a fairly generic, depth-first, key-collecting traversal, followed by a mapping to add the indices:
const flattenKeys = (o) =>
Object (o) === o
? Object .entries (o) .flatMap (([k, v]) => [k, ...flattenKeys (v)])
: []
const getKeys = (o) =>
flattenKeys (o) .map ((label, id) => ({label, id}))
const data = {a: {b: {c: {foo: 'bar'}}}}
console .log (getKeys (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you wanted a breadth-first traversal it wouldn't be much harder.
This separation of key collection and index generation makes the code much simpler to my mind.

If function parameter is empty is there a way to pass ...rest of values to that parameter?

If function parameter is empty is there a way to pass ...rest of the destructured values to that parameter with spread operator?
For an example:
const obj = {
/* param: {
a: 2,
b: 3
}, */
c: 1,
d: 3
}
const fun = ({ param = ...rest}) => {
console.log(param);
};
fun(obj);
In this case param is "undefined" and i would like to get the rest of the obj assign to the param {c:1, d:3}
In case when param is defined, I would like to have param data {a:2, b:3}
Use a ternary operator in the function parameter section to determine which properties to log:
const obj = {
/*param: {
a: 2,
b: 3
},*/
c: 1,
d: 3
}
const fun = (args = obj.param ? obj.param : obj) => {
console.log(args)
}
fun(obj)

Get all the keys of objects within another object with property value that matches the query

I have a nested object. I need to filter them out by property of the child object but only get the keys.
I have tried so far to first, inject a property id into each child object and assign the object's key as its value. Then proceed to filter the object, compare property if it will match with the query, then return the injected property id.
let test_obj = {
A: {
a: 1,
b: 1,
},
B: {
a: 1,
b: 2,
},
C: {
a: 1,
b: 3,
}
}
let identify = (e) => {
for (e of Object.entries(e)){
key = e[0];
val = e[1];
val.id = key;
console.log(e);
}
}
identify(test_obj);
let query = (test_obj,prop,val) => (Object.values(test_obj).filter(o => o[prop] == val).map(o=>o.id));
let result = query(test_obj,"b",2);
console.log(result)
It currently return my desired results, yet I feel like I cheated. Is there a way to do this without having to inject another property to determine the key? I feel like I'm missing something, but I can't wrap my head around this.
Instead of adding an additional key, and then filtering values, you can filter the keys like this instead:
const test_obj = {
A: {
a: 1,
b: 1,
},
B: {
a: 1,
b: 2,
},
C: {
a: 1,
b: 3,
}
}
const query = (obj, prop, val) => Object.keys(obj).filter(k => obj[k][prop] === val);
console.log(query(test_obj, "b", 2))
A more elegant solution is to use the reduce functionality, which you can (and always should) use if you find yourself using filter and map:
function findKeysForValue(test_obj, value) {
return Object.entries(test_obj).reduce((myKeys, [objKey, outerValue]) => {
if (Object.values(outerValue).find(nestedValue => nestedValue === value)) {
return [...myKeys, objKey];
}
return myKeys;
}, []);
}

Simple way to turn array of objects into object with grouped keys as arrays

I am looking for a simple way to do the following. I have tried to do this with lodash.reduce and it is clunky, is there an easier way.
From:
[{a: 'meow'}, {a: 'woof'}]
To:
{a: ['meow', 'woof']}
You can do that with pure JS, no need of loadash.
Call the reduce method of arrays on your input array, and reduce the array to an object, looping over the keys of your inner objs:
const input = [{a: 'meow'}, {a: 'woof'}, {b: 'hi'}, {a: 'dog', c: 'bye'}, {}];
console.log(input.reduce((acc, val) => {
Object.keys(val).forEach(key => {
if(!acc[key]) {
acc[key] = [];
}
acc[key].push(val[key]);
});
return acc;
}, {}));
You can use lodash#assignWith to assign all properties their respective values into one object, together with a customizer function to determine how you want to structure the object.
const result = _.assignWith({}, ...data, (v = [], s) => v.concat(s));
Note: To make sure that we don't mutate any of the objects in the data array, I passed an empty object as the first parameter to act as the destination object.
const data = [
{ a: 'meow' },
{ a: 'woof', k: 'hey' },
{ k: 'yo', d: 'hehe' },
{ d: 'wazup', q: 'ohoho' }
];
const result = _.assignWith({}, ...data, (v = [], s) => v.concat(s));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
I had some issues with typescript and lodash.reduce, this worked.
export function getFuncsObject(funcs): Record<Funcs, Case[]> {
let f = { ...funcs };
f = lodash.mapValues(f, () => []);
return f;
}
export function mockMerge(funcs, mocks: Record<Funcs, Case | null>[]): Record<Funcs, Case[]> {
const f = getFuncsObject(funcs);
lodash.each(mocks, (v, k) => {
f[k].push(v);
});
return f;
}
One option would be to use two reductions as follows:
const input = [{
a: 'meow'
}, {
a: 'woof'
}, {
b: 'moo'
}];
const result = input
.reduce((itemResult, item) => Object.keys(item)
.reduce((keyResult, key) => ({
...keyResult,
[key]: (keyResult[key] || []).concat(item[key])
}), itemResult), {});
console.log(result)
Not sure if this is clunky compared to your current solution, but it's fairly concise and does not require an external library.
Without using any external libraries or reduce.
const input = [ {a: 'meow'}, {a: 'woof'}, {b: 'hi'}, {a: 'dog', c: 'bye'}, {} ];
let output = {};
input.forEach((inputObj) => {
for(let key in inputObj){
if(!output[ key ]){
output[ key ] = [];
}
output[ key ].push(inputObj[key])
}
});
console.log(output);

Javascript: Modifying nested object in ES6 shorthand

Consider a function returns an nested object and I want to modify the property inside the nested object.
In the below example, I'm calling the function many times or I need to store it in a temporary variable. Is there a way to invoke only once inside the braces and spread/modify inside the same object many times.
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
var modifiedD = {
...getObject(),
a: {
b: {
...getObject().a.b,
d: 4
}
}
}
console.log(modifiedD);
when declaring a key after ...getObject() it replace the whole value. It does not merge the inner object behind a.
So you could do it as you have done and call getObject() multiple time.
An other solution could be to handle it using a function of your own merging the objects, like :
function mergeObjects(obj1, obj2) {
// We are going to copy the value of each obj2 key into obj1
Object.keys(obj2).forEach((x) => {
// If we have an object, we go deeper
if (typeof obj2[x] === 'object') {
if (obj1[x] === void 0) {
obj1[x] = {};
}
mergeObjects(obj1[x], obj2[x]);
} else {
obj1[x] = obj2[x];
}
});
return obj1;
}
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
const modifiedD = mergeObjects(getObject(), {
a: {
b: {
d: 4,
},
},
});
console.log(modifiedD);
WARNING, the function I have made mutate the object which may not be the best answer
Or call it only once and then set the keys one by one like :
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
const modifiedD = getObject();
modifiedD.a.b.d = 4;
console.log(modifiedD);
Further to my previous answer, as Grégory NEUT pointed out you could have a lot larger complexity.
If so, you could simply create two objects and then merge them. I found a function code snippet to be able to do that using Object.assign
Example:
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
var modifiedD = getObject();
var newD = {
a: {
b: {
d: 4
},
y: 1
},
z: 20
}
/** TAKEN FROM https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6 **/
// Merge a `source` object to a `target` recursively
const merge = (target, source) => {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source)) {
if (source[key] instanceof Object) Object.assign(source[key], merge(target[key], source[key]))
}
// Join `target` and modified `source`
Object.assign(target || {}, source)
return target
}
modifiedD = merge(modifiedD, newD);
console.log(modifiedD);
You can try the following:
getParentObj(path, obj) {
return path.split('.').reduce((o,i)=>o[i], obj);
}
const parent = getParentObj('a.b', getObject());
parent[d] = 24;

Categories