Copy without keeping reference [duplicate] - javascript

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.
So I have a variable called elements that hold a bunch of data. Let's say, for example, that this is it:
{
name: "Jeff",
age: "18",
carList: [
"car1": "...",
"car2": "..."
],
surname: "Matt"
}
I also have an empty array, at the start, declared this way:
public myArray: Model[] = [];
Model is just a class that contains the definition of the elements.
What I'm trying to achive is to have a copy of elements without keeping any reference to the original variable. but I'm missing something. Here's a pseudo-code that do the trick (almost) :
myfunction(){
//Do stuff that change `car1` from `...` to `hello`
myArray.push(Object.assign({}, this.elements));
}
And finnally a I have a function that print everything in the myArray variable:
function print(){
this.myArray.forEach(el => {
console.log(el);
});
}
The problem is that every element of the array has the latest update done by me.
So if myFunction was called several time doing something like:
Change "Jeff" to "Jack"
Change age "18" to "25"
Change surname to "Seth"
what I see is three log showing this data:
{
name: "Jeff",
age: "25",
carList: [
"car1": "...",
"car2": "..."
],
surname: "Seth"
}
My question is: Why even using Object.asign it still keeping the reference to it's original object? I should keep the history of each modification, instead of having just the last edit in both of three edits I've made.
Thank you.

Try the following:
JSON.parse(JSON.stringify(element));
It keeps no refrences, as you can observe in the snippet below:
let element = { name: "Jeff", age: "18", carList: [ {"car1": "x"}, {"car2": "y"} ], surname: "Matt" };
let result = JSON.parse(JSON.stringify(element));
result.name = "x";
console.log(result);
console.log("****element*****");
console.log(element);

Related

Check if an array of object is exactly equal to another array that contains one of its property [duplicate]

This question already has answers here:
Check if an array contains any element of another array in JavaScript
(32 answers)
Closed 1 year ago.
I have these 2 arrays
The first one is fixed and does not change and contains the id of the 2nd array:
fixed=["123","456","789"]
The second can change
variableArray=[{name:"Joe",id:"123"},{name:"Joe",id:"456"},{name:"Joe",id:"789"}]
I want to return true if, even if there were some changes at the end the variable array is the same length and contains exactly the same keys of the "fixed"
NOT VALID:
fixed=["123","456","789"]
variableArray=[{name:"Joe",id:"456"},{name:"Joe",id:"789"}]
return false because is missing the id "123" (and the length is also different so is excluded by default)
NOT VALID:
fixed=["123","456","789"]
variableArray=[{name:"Joe",id:"123"},{name:"Joe",id:"456"},{name:"Joe",id:"001"}]
this will return false because, even if contains 3 elements as there are in the "fixed" is missing the id "789" and have another "001" instead
as #mplungjan mentiond, you can use Every:
let fixed = ["123", "456", "789"];
let variableArray1 = [{
name: "Joe",
id: "123"
}, {
name: "Joe",
id: "456"
}, {
name: "Joe",
id: "789"
}];
let variableArray2 = [{
name: "Joe",
id: "123"
}, {
name: "Joe",
id: "456"
}, {
name: "Joe",
id: "001"
}]
let containsAll1 = variableArray1.every(elem => fixed.includes(elem.id));
let containsAll2 = variableArray2.every(elem => fixed.includes(elem.id));
console.log(containsAll1, containsAll2);

Javascript - Access nested elements of JSON object

So I have a series of 4 JSON objects with nested data inside each of them. Each of these objects are stored in an array called classes. Here is an example of how one of the class objects is formatted:
let class_A = {
professor: "Joey Smith",
numberStudents: 25,
courseCode: "COMS 2360",
seating: {
"FirstRow": {
0: {
firstName: "Sarah",
collegeMajor: "English",
},
1: {
firstName: "Bob",
collegeMajor: "Computer Engineering",
},
2: {
firstName: "Dylan",
collegeMajor: "Mathematics",
}
},
"SecondRow": {
3: {
firstName: "Molly",
collegeMajor: "Music"
}
}
}
};
I'm struggling to figure out how to access the very last fields within each class object (firstName and collegeMajor). The furthest I was able to get was the indexes beneath each row number.
let classes = [class_A, class_B, class_C, class_D];
let classesAvailable = document.getElementById('classes');
let class = classes[classesAvailable.value];
for(rowNum in class.seating){
for(index in class.seating[rowNum]){
console.log(index);
//console.log(class.seating[rowNum[index]].firstName);
}
}
So in this example, console.log(index) prints out:
0
1
2
3
but I'm unable to print the first name and college major of each student in each row. I was trying to follow a similar logic and do console.log(class.seating[rowNum[index]].firstName) but I get the error:
Cannot read properties of undefined (reading 'firstName')
Was wondering if anyone knows what's wrong with my logic here?
console.log(class.seating[rowNum][index])

How to create self-executing anonymous function in Object?

in my object
let obj = {
name:["Harry","Scren","Docis","Altab"],
age:[32,44,22,55]
}
I need to write down a self executing anonymous function in object that, when i call any member of the object it will execute that self executing anonymous function and it will check whether the length of both array (name and age) are equal or not, If not equal then throw an error .....I need to have something like
let obj = {
name:["Harry","Scren","Docis","Altab"],
age:[32,44,22,55],
(function(){
if(name.length != age.length){
throw new Error('both name and age 's length are not equal')
}
}()) // But this is not possible to create in object I have just showed down what I wanted to create
}
How to create something like that in javascript object ??
I'm not sure, but I think that's not possible...
But let me ask a question: why you have an obj containing two arrays? Hasn't more sense having an array of the obj which each element of the array contains one name and one age? I'll show you
const people = [
{ name: "Pearson 1", age: 20 },
{ name: "Pearson 2", age: 20 },
{ name: "Pearson 3", age: 20 },
{ name: "Pearson 4", age: 20 },
]
So you do not have to check the length since every obj has its own name and age field.

Javascript .map() on copied array

I have noticed that invoking .map() without assigning it to a variable makes it return the whole array instead of only the changed properties:
const employees = [{
name: "John Doe",
age: 41,
occupation: "NYPD",
killCount: 32,
},
{
name: "Sarah Smith",
age: 26,
occupation: "LAPD",
killCount: 12,
},
{
name: "Robert Downey Jr.",
age: 48,
occupation: "Iron Man",
killCount: 653,
},
]
const workers = employees.concat();
workers.map(employee =>
employee.occupation == "Iron Man" ? employee.occupation = "Philantropist" : employee.occupation
);
console.log(employees);
But considering that .concat() created a copy of the original array and assigned it into workers, why does employees get mutated as well?
This is happening because your objects within the array are still being referenced by same pointers. (your array is still referring to the same objects in memory). Also, Array.prototype.map() always returns an array and it's result should be assigned to a variable as it doesn't do in-place mapping. As you are changing the object's properties within your map method, you should consider using .forEach() instead, to modify the properties of the object within the copied employees array. To make a copy of your employees array you can use the following:
const workers = JSON.parse(JSON.stringify(employees));
See example below:
const employees = [
{
name: "John Doe",
age: 41,
occupation: "NYPD",
killCount: 32,
},
{
name: "Sarah Smith",
age: 26,
occupation: "LAPD",
killCount: 12,
},
{
name: "Robert Downey Jr.",
age: 48,
occupation: "Iron Man",
killCount: 653,
},
]
const workers = JSON.parse(JSON.stringify(employees));
workers.forEach(emp => {
if(emp.occupation == "Iron Man") emp.occupation = "Philantropist";
});
console.log("--Employees--")
console.log(employees);
console.log("\n--Workers--");
console.log(workers);
Note: If your data has any methods within it you need to use another method to deep copy
Problem analysis
workers = workers.map(employee =>
employee.occupation == "Iron Man" ? (employee.occupation = "Philantropist", employee) : (employee.occupation, employee)
);
[...] why does employees get mutated as well?
array.map() calls the passed function with each element from the array and returns a new array containing values returned by that function.
Your function just returns the result of the expression
element.baz = condition ? foo : bar;
which, depending on the condition, will
evaluate to foo or bar
assign that result to baz and
return that result
Further (expression1, expression2) will evaluate both expressions and return expression2 (see the comma operator).
So, although you return employee in both cases, you modify the original object with the left expression (expression 1).
Possible solution
You might want to create a new object using Object.assign()
array.map((employee) => Object.assign({ }, employee))
instead of using that array.concat() "trick". Using that mapping, you not only create a new array but also new objects with their attributes copied. Though this would not "deep copy" nested objects like { foo: { ... } } - the object accessible via the property foo would still be the same reference as the original. In such cases you might want to take a look on deep copying modules mentioned in the other answers.
The array references change but the copied array still reference the original objects in the original array. So any change in the objects in the array are reflected across all copies of the array. Unless you do a deep copy of the array, there is a chance that the some changes in the inner objects get reflected across each copy
What is the difference between a deep copy and a shallow copy?
Deep copies can be made in several ways. This post discusses specifically that: What is the most efficient way to deep clone an object in JavaScript?
map builds up a new array from the values returned from the callback, which caj easily be used to clone the objects in the array:
const workers = employees.map(employee => ({
...employee, // take everything from employee
occupation: employee.ocupation == "Iron Man" ? "Philantropist" : employee.occupation
}));
Or you could deep clone the array, then mutate it with a simple for:
const workers = JSON.parse(JSON.stringify(workers));
for(const worker of workers)
if(worker.ocupation == "Iron Man")
worker.ocupation = "Philantropist";

Accessing infinite nested children object

Wanted to know how can we access infinitely nested object in JS?
Consider this example given to be my interviewer
You have an object with a Parent and infinitely nested children
[
{
name: "Jack",
age: "98" ,
profession: "doctor
children: [
{
name: "Varun",
age: "80"
profession: "scientist"
children: [
{
name: "Ishan"
age: "62",
profession: "teacher
children: [{....
.....
.....[{
name: "Rahul",
age: "23",
profession: "engineer"
children: [{
.....
I need to find the name corresponding where profession is "engineer" and how deep that given object is nested.
Note: Number of children to be considered here is infinite.
Question: Can someone help me in figuring out how I can do it with recursion and without recursion
update: He gave me a hint to use divide and conquer
Update: Based on the solution by Bary, I tried something like this
let infiniteArray = [
{
name: "Jack",
age: "98" ,
profession: "doctor",
children: [
{
name: "Varun",
age: "80",
profession: "scientist",
children: [
{
name: "Ishan",
age: "62",
profession: "teacher"
}
]
}
]
}
]
function isTheItem(item) {
if (item["profession"] === "teacher") return item
return false
}
function walk(collection) {
return collection.find(item => isTheItem(item) || walk(item.children));
}
But when I console.log it, i.e console.log(walk(infiniteArray)), it is logging the entire infiniteArray and I understand why but I went it to return just the name corresponding to profession. Any help on how I can achieve it?
name: "Ishan",
age: "62",
profession: "teacher"
}
There's two directions walking through your collection
* sibling
* nested
so you can iterate this sibling items with regular array iterator and recursively walk through the children of each item.
function isTheItem(item) {
return ...;
}
function walk(collection) {
return collection.find(item => isTheItem(item) || walk(item.children));
}
Divide and conquer algorithms usually have more to do with sorting values in an array (during an interview process). If you were sorting by age or something, I could see that being a hint. But there's no way to really divide and conquer when searching for a key value pair of an infinite number of nested objects. You have to search through n number of values until you find the result. Any dividing of the object doesn't necessarily improve efficiency with a random key value pair stored that you want to find.
More experienced devs correct me if I'm wrong here
Barry Gane has a good start to the code you would write for the actual application of this.

Categories

Resources