Borislav Kiselkov
Borislav Kiselkov

Reputation: 126

Is there a way to copy an array of objects in javascript?

This may be an issue caused by my incompetence in javascript as I am new to the language but so far I haven't found a working solution.

The problem is that I want to make a copy array of an array of objects, then change the data in the copy array without changing the values of the objects in the initial array and finally assign the copy array to the initial array again so I can use it again in another function with the initial values.

I have made a simple program containing two classes - Country which holds some properties and a function to change one of these properties and World which hold an array of countries and a function to change the data of all countries in the array. I create a World instance, call the changeAllCountriesData function and at the end print the values of both the countries array and the copyData array. As you can see they have both changed and I am unsure of the reason why.

class Country{
    name; area; population;

    constructor(name, area, population){
        this.name = name;
        this.area = area;
        this.population = population;
    }

    changeData(){
        this.population += Math.round(this.population * 0.02);
    }
}

class World{
    countries = [];
    copyData = [];

    constructor(){
        this.loadData();
        this.copyData = this.countries.slice(0);
    }

    loadData(){
        this.countries.push(new Country("Denmark", 41990, 5839809));
        this.countries.push(new Country("Germany", 349360, 83159604));
        this.countries.push(new Country("Netherlands", 33690, 17342709));
    }

    changeAllCountriesData(){
        this.copyData.forEach(c => {
            c.changeData();
        })
    }
}
console.log("Initial information for the population: 5839809, 83159604, 17342709")
w = new World();
w.changeAllCountriesData();
console.log("countries array:")
console.log(w.countries);
console.log("copyData array:")
console.log(w.copyData);

Other ways I have tried instead of this.copyData = this.countries.slice(0):

1.

this.copyData = [...this.countries]

2.

this.copyArray = Array.from(this.countries);
this.copyData = copyArray[0];

3.

this.countries.forEach(c => {
      this.copyData.push({...c});
})

The one above returns NaN after each calculation.

4.

this.countries.forEach(c => {
      let obj = Object.assign({}, c);
      this.copyData.push(obj);
})

5.

this.copyData = this.countries.concat();

None of these methods has made a copy array in such a way that the initial one remains unchanged or the calculations are made without returning NaN.

Upvotes: 0

Views: 574

Answers (3)

Mahmudul Hasan
Mahmudul Hasan

Reputation: 1

You can achieve this with the Array.prototype.map() method and object spreading.

const arr = [{ a: 1 }, { b: 1 }];

const newArr = arr.map((singleObject) => {
  return { ...singleObject };
});

Upvotes: 0

jkalandarov
jkalandarov

Reputation: 685

I was about to comment, but could not as I dont have enough points yet.

  1. In changeAllCountriesData() (inside class World) you are trying to invoke changeDate() function which is a local function inside class Country. I'm not sure class World can access local function of another class directly. You can invoke function inside a class after you assigned a class into a variable var, let or const. Example:

    const codeland = new Country('USA', 123, 456);

    codeland.changeData();

Now, codeland is global variable. Now you can access changeData() function via codeland variable in your class World.

I'm not pro at JavaScript, and there must be simpler way, I believe!

As for copying arrays, method #1 and #3 are correct. (Not sure about method #2 and #4. Method #5 concatenated countries to copyData.) They return NaN, because both countries and copyData are empty while you are copying data from one array to another. Therefore, it is NaN (or undefined).

I hope it helps!

Upvotes: 0

Rajesh
Rajesh

Reputation: 24915

In JS, objects are copied using reference. So when you so .slice(0) you are partially correct but the issue is, internal values are objects. So the new array has previous values.

Now you can also try .map(obj => ({ ...obj }) ) but this will not work as object spread will create a new object with properties but not methods. So any method, like changeData will not be there on this object. Due to this, you will have to create a copy mechanism to your class.

Try updating to

cloneObject() {
  const {name, area, population} = this;
  return new Country(name, area, population)
}

...

this.copyData = this.countries.map( (obj) => obj.cloneObject() );

Note: You can extend this function's capability. You can add an argument to override the values. That will allow you to do multiple operations in one go:

cloneObject( overrides ) {
  const { name, area, population } = { ...this, ...overrides };
  return new Country(name, area, population)
}

...

this.copyData = this.countries.map( (obj) => obj.cloneObject({ name: 'Hello World' }) );

Sample Code:

class Country{
    name; area; population;

    constructor(name, area, population){
        this.name = name;
        this.area = area;
        this.population = population;
    }
    
    cloneObject() {
      const {name, area, population} = this;
      return new Country(name, area, population)
    }

    changeData(){
        this.population += Math.round(this.population * 0.02);
    }
}

class World{
    countries = [];
    copyData = [];

    constructor(){
        this.loadData();
        this.copyData = this.countries.map( (obj) => obj.cloneObject() );
    }

    loadData(){
        this.countries.push(new Country("Denmark", 41990, 5839809));
        this.countries.push(new Country("Germany", 349360, 83159604));
        this.countries.push(new Country("Netherlands", 33690, 17342709));
    }

    changeAllCountriesData(){
        this.copyData.forEach(c => {
            c.changeData();
        })
    }
}
console.log("Initial information for the population: 5839809, 83159604, 17342709")
w = new World();
w.changeAllCountriesData();
console.log("countries array:")
console.log(w.countries);
console.log("copyData array:")
console.log(w.copyData);

Upvotes: 1

Related Questions