Javascript deepcopy without JSON or variable assignment - javascript

I have a javascript question:
I was doing some challenge exercises from a JS book I'm learning from and I have no idea how to go about it and I don't have the sample answers. The aim is to write a deep copy function using *only ES6*'s arrow functions without using any JSON methods or any variable assignments. The author also suggests no scopes using the {} brackets. Any hints or tips on how I can do this?

Create a recursive function which accepts a value and will return a copy of it. Check the type of the value. If it's an Array, return an new Array which maps every element to a recursive call of that function. If it's an Object, create a new Object with copies of its properties. If it's primitive (string, number, boolean...), just return it.
On a side note, writing this without any variable assignment & curly brackets may be fun for learning, but it makes it a lot less readable. I would never write it this way at work:
const deepCopy = v =>
// Is it an Array?
v instanceof Array ?
// If it is, map every value to its copy
v.map(deepCopy) :
// Otherwise, is it a non-null Object?
(typeof v === "object" && v !== null) ?
// If it is, create a new Object with copies of its property values
Object.entries(v)
.reduce((acc, [k, v]) => ({ ...acc, [k]: deepCopy(v) }), {}) :
// Otherwise, just return the value
v;
console.log(deepCopy({foo: 'bar', arr: [{baz: 42}, true]}));
console.log(deepCopy("Hello world"));
console.log(deepCopy(["a", "b", null]));

You can get arrays of keys and values with Object.keys() and Object.values(), then iterates and assigns those items to a new object, try this:
let obj = {key1: 'value1', key2: 'value2'};
let keys = Object.keys(obj);
let values = Object.values(obj)
console.log(keys, values)
let newObj = {};
for( let i = 0; i < keys.length; i++ ){
newObj[keys[i]] = values[i]
}
console.log(newObj);
newObj.key1 = 'asd';
console.log(newObj);

Related

Can I assign a value, of an item in an array of objects, from another value from an array of objects without referencing in javascript?

I have two arrays of objects
A = [{x: x1, y: y1 ...}, {x: x1, y: y1}];
and I am iterating over these upading
B.forEach((d, i) => d['x'] = A[i]['x']));
However, whenever I am now updating B, A is updating as well.
Edit: Apparently the problem is not above, so here is all I do:
I have 3 arrays of objects, A, B, C.
I initialize them as follows:
A = await d3.json(endpointA).then(res => res);
C = await d3.json(endpointB).then(res => res);
B = [...A];
They are the bound with .data(B) to some svg elements with d3 and updated.
I am guessing they get referenced but while I have now a few months behind me, this is still my first javascript project so I am not 100% sure.
Any suggestions are very welcome!
Thank you!
Your B only clones array itself, but you also need to clone the objects inside the array:
B = [...A].map(o => Object.assign({}, o));
As #Pointy pointed out, this will only work if objects don't have nested objects.
For nested objects you'd need a deep cloning function:
B = cloneObj(A);
function cloneObj(obj)
{
const ret = Array.isArray(obj) ? [] : {};
for(let o in obj)
{
if (obj[o] && typeof obj[o] == "object")
ret[o] = cloneObj(obj[o]);
else
ret[o] = obj[o];
}
return ret;
}
The basic issue is this:
B = [ ... A ];
That makes a shallow copy of A. Making deep copies of objects is, in the general case, pretty difficult because objects can have lots of "shapes". In your case, however, it's likely that the following trick will work:
B = A.map(v => JSON.parse(JSON.stringify(v)));
That uses the JSON facility to stringify each object in A, and then parse it back to an object. That trick does not always work; in particular, if an object has functions or various other data values that cannot be serialized to JSON, it will strip those values out of the objects.

Compare keys of 2 different JSON Objects and return the key along with its value that does not match

I've two json objects. I have also used json.stringfy on them.
Json1 and json2 has certain keys.
Few keys are similar in both the objects while others are different.
I don't care if the values are different in both the objects.
I wanted to return the unmatched keys of Json1 along with its values after checking if the key exists in json2.
Ex:
Json1:{
"A":2,
"B": "jrrj" }
Json2 : {
"A" : 6,
"C" : "jrdj"
}
Output Expected:
Result: {
"B" : "jrrj"
}
I searched for the solution a lot on the SO but couldn't find a suitable one that does exactly the same and I'm unable to figure out one.
Please help me out.
P.S - right now I'm on js version ES5.
If that's flat object and keys that are missing from second object, you may:
Extract Object.keys() of the first object
Use Array.prototype.reduce() to traverse the latter and turn that into an object having its properties set to unique properties of the first object
Following is a quick demo:
const o1 ={"A":2,"B":"jrrj"},
o2 ={"A":6,"C":"jrdj"},
diff = Object
.keys(o1)
.reduce((r,key) =>
(!(key in o2) && (r[key] = o1[key]), r), {})
console.log(diff)
ES5-compatible version
const o1 ={"A":2,"B":"jrrj"},
o2 ={"A":6,"C":"jrdj"},
diff = Object
.keys(o1)
.reduce(function(r, key){
if(!(key in o2)) r[key] = o1[key]
return r
}, {})
console.log(diff)
const object_1 = {"A":2,"B":"jrrj"};
const object_2 = {"A":6,"C":"jrdj"};
var new_obj = {};
Object.keys(object_1).map(
function(obj_1_key){
if(!object_2[obj_1_key]){
new_obj[obj_1_key] = object_1[obj_1_key];
}
}
)
console.log(new_obj);

add array of keys as property on obj as accumulator by reduce?

i have array of keys that i want to add as property with calculated val(not including on code) on empty object like this by reduce:
const f = ['a','b','c'].reduce((obj,key) => obj[key]='', {})
i was expecting the obj is the accumulator {} so i added properties that way? how do i make this work by reduce?
i was expecting and want it to result like this for that code:
{ a:'', b:'', c:'' }
but my code only results to empty string on console. how do i achieve it?
The value that the reduce callback returns will be the accumulator in the next iteration. So, in your code, obj only refers to the object in the first iteration - on the next iteration, it refers to what obj[key] = '' resolves to, which is the empty string.
Return the object instead:
const f = ['a', 'b', 'c'].reduce((obj, key) => {
obj[key] = '';
return obj;
}, {});
console.log(f);
You could use Object.fromEntries instead, if you wanted (though, it's very new, so for good cross-browser support, include a polyfill):
const f = Object.fromEntries(
['a', 'b', 'c'].map(key => [key, ''])
);
console.log(f);

How to find Key value from an object based on the value passed

Thanks for your time.
I have the following object in JavaScript:
{
"key1":"value1,value2,value3",
"key2":"value4,value5,value6"
}
Now I want to parse for a specific value and if the value exists, I want to return the key associated with it. Suppose that I am passing 'value3', it should return key1; if passing value5, it should return me key2 and so on.
What is the best way to implement it using Javascript, keeping in mind the execution time. I have tried using sting manipulation functions like indexOf, substr; but not most effective I believe.
TIA.
Here is a slightly different approach that will generate a map where the key is actually the value of your original values object.
The generated map will be a sort of fast lookup. Just to make things clear this solution is efficient as long as you need to do a lot of lookups. For a single, unique lookup this solution is the less efficient, since building the hashmap requires much more time than just looking up for a single value.
However, once the map is ready, acquiring values through keys will be incredibly fast so, if you need to later acquire multiple values, this solution will be more suitable for the use case.
This can be accomplished using Object.entries and Object.values. Further explanations are available in the code below.
The code below (despite not required) will also take care of avoiding indexOf with limit cases like searching 'value9' over 'value9999' which, on a regular string, would actually work with indexOf.
const values = {
"key1":"value1,value2,value3",
"key2":"value4,value5,value6",
"key3":"value444,value129839,value125390", // <-- additional test case.
"key4": "value9" // <-- additional test case.
};
const map = Object.entries(values).reduce((acc, [key, value]) => {
// If the value is invalid, return the accumulator.
if (!value) return acc;
// Otherwise, split by comma and update the accumulator, then return it.
return value.split(',').forEach(value => acc[value] = key), acc;
}, {});
// Once the map is ready, you can easily check if a value is somehow linked to a key:
console.log(map["value444"]); // <-- key 3
console.log(map["value9"]); // <-- key 4
console.log(map["Hello!"]); // undefined
To me, the fastest and most concise way of doing that would be the combination of Array.prototype.find() and String.prototype.includes() thrown against source object entries:
const src={"key1":"value1,value2,value3","key2":"value4,value5,value6"};
const getAkey = (obj, val) => (Object.entries(obj).find(entry => entry[1].split(',').includes(val)) || ['no match'])[0];
console.log(getAkey(src, 'value1'));
console.log(getAkey(src, 'value19'));
p.s. while filter(), or reduce(), or forEach() will run through the entire array, find() stops right at the moment it finds the match, so, if performance matters, I'd stick to the latter
Lodash has a function for this called findKey which takes the object and a function to determine truthiness:
obj = { 'key1': 'value1, value2, value3', 'key2': 'value4,value5,value6' }
_.findKey(obj, val => val.includes('value3'))
# 'key1'
_.findKey(obj, val => val.includes('value5'))
# 'key2'
Based on your search, you can use indexOf after looping through your object.
Here's an old school method:
var obj = {
"key1":"value1,value2,value3",
"key2":"value4,value5,value6"
}
function search (str) {
for (var key in obj) {
var values = obj[key].split(',');
if (values.indexOf(str) !== -1) return key
}
return null;
}
console.log(search('value1'))
console.log(search('value6'))
Or you can use Object.keys() with filter() method and get the index 0 of the returned array.
var obj = {
"key1":"value1,value2,value3",
"key2":"value4,value5,value6"
}
function search (str) {
return Object.keys(obj).filter((key) => {
const values = obj[key].split(',');
if (values.indexOf(str) !== -1) {
return key
}
})[0]
}
console.log(search('value1'))
console.log(search('value6'))
You can try iterating over each value in your object and then splitting the value on each comma, then checking if the value is in the returned array like so:
const myObj = {"key1":"value1,value2,value3","key2":"value4,value5,value6"}
function findKeyByValue(obj, value) {
for (var key in myObj) {
const valuesArray = myObj[key].split(',')
if (valuesArray.includes(value)) {
return key
}
}
}
const key = findKeyByValue(myObj, 'value5') // returns 'key2'
console.log(key)
EDIT: Changed loop for efficiency, and extracted code to function
This should do it. Just uses Object.entries and filters to find the entries that contain the value you're looking for. (Can find more than one object that has the desired value too)
var obj = {
"key1": "value1,value2,value3",
"key2": "value4,value5,value6"
};
var find = 'value2';
var key = Object.entries(obj).filter(([k, v]) => v.split(',').includes(find))[0][0];
console.log(key);
Might want to check the return value of Object.entries(obj).filter((o) => o[1].split(',').includes(find)) before trying to access it, in case it doesn't return anything. Like so:
var obj = {
"key1": "value1,value2,value3",
"key2": "value4,value5,value6"
};
function findKey(objectToSearch, valueToFind) {
var res = Object.entries(objectToSearch).filter(([key, value]) => value.split(',').includes(valueToFind));
if(res.length > 0 && res[0].length > 0) {
return res[0][0];
}
return false;
}
console.log(findKey(obj, 'value5'));
includes can be used to check whether a value is present in an array. Object.keys can be used for iteration and checking for the match.
function findKey(json, searchQuery) {
for (var key of Object.keys(json)) if (json[key].split(',').includes(searchQuery)) return key;
}
const json = {
"key1": "value1,value2,value3",
"key2": "value4,value5,value6"
}
console.log(findKey(json, 'value5'))
Use Object.entries with Array.prototype.filter to get what the desired key.
const data = {
"key1": "value1,value2,value3",
"key2": "value4,value5,value6"
};
const searchStr = 'value3';
const foundProp = Object.entries(data).filter(x => x[1].indexOf(searchStr) !== -1);
let foundKey = '';
if (foundProp && foundProp.length) {
foundKey = foundProp[0][0];
}
console.log(foundKey);

Difference between array and object in javascript? or Array Vs Object

How to recognize array & object in js where typeof doesn’t come in handy?
var arr = [], ob = {};
As everything in js are objects,
if(typeof arr == typeof ob) => returns true
I want a operator or ... that will tell me that the variable is an array. I can then use only the arrayfunctions to objects which are array. How is that possible?
var arr = [], ob = {};
As everything in js are objects, even **Array is an Object but an instance of class Array
if(typeof arr == typeof ob) => returns true as Both are **Objects
So, how will you to identify objects.
This is where instanceof operator comes in handy, to identify whether its an array you can put a additional check cde:
if(arr instanceof Object && arr instanceof Array) => returns true
if(ob instanceof Object && ob instanceof Array) => returns false
You could use Array.isArray() method to check if a variable is array or otherwise.
var myArray = [1,2,3,4,5];
console.log(Array.isArray(myArray));
true
Among numerous simple/sophisticated comparisons, one difference is:
var arr = []; # arr.length => 0
var obj = {}; # obj.length => undefined
There are multiple ways of differentiating between array and object, some on them are already mentioned above i would like to just add on above answers.
First Approach
Differentiation using length, length property exist on Array but doesn't exist on Object
var arr = [1,2,3]; arr.length => 3
var obj = {name: 'name'}; obj.length => undefined
Note: This approach fails when someone declares object like below, we can use this approach only when we are sure we will not get any object having length property
var rectangle = {length: 50, width: 50}; rectangle.length => 50
Second Approach
Using instanceof Array
var arr = [1,2,3]; arr instanceof Array => true
var obj = {name: 'name'}; ojb instanceof Array => false
Third Approach
Using Array.isArray, this is most preferable approach and is supported by most of browser now
Note: Array.isArray is preferred over instanceof because it works
through iframes.
Array.isArray(arr) => true
true
Array.isArray(obj) => false
If you want to support i.e 8 browser the use Object.prototype.toString
We can write polyfill for i.e 8
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
Object.prototype.toString.call(arr); =>"[object Array]"
Object.prototype.toString.call(obj); =>"[object Object]"
Reference: isArray

Categories