Search code examples
javascriptp5.jsmutability

A copy of an array changes the original array even with slice


I'm trying to create a copy of an array so I can make changes to the copy while still saving the original array. I am using the slice function to copy the array but for some reason it is still changing both arrays. The code embedded will not run because I am using a custom font which I cannot import

let font;
let num1;

function preload() {
  font = loadFont("Pacifico-Regular.ttf")
}
function setup() {
  createCanvas(350, 350);
  num1 = new num("1", 350 / 2, 350 / 2, 200)
  num1.randomize(-5, 5)
  num1.show()
}
function draw() {}

class num {
  constructor(text, centerX, centerY, fontSize) {
    this.text = text;
    this.centerX = centerX;
    this.centerY = centerY;
    this.fontSize = fontSize;
    this.oldArray = font.textToPoints(this.text, this.centerX, this.centerY, this.fontSize);
    this.newArray = this.oldArray.slice(); //Here is where I make a copy of the Array
  }
  //Randomizes old array
  randomize(min, max) {
    for (let i = 0; i < this.oldArray.length; i++) {
      this.oldArray[i].x = this.oldArray[i].x + random(min, max);
      this.oldArray[i].y = this.oldArray[i].y + random(min, max);
    }
  }
  //Draws new array
  show() {
    beginShape()
    for (let i = 0; i < this.newArray.length; i++) {
      vertex(this.newArray[i].x, this.newArray[i].y)
    }
    endShape(CLOSE)
  }
  //Moves new array
  move(x, y) {
    for (let i = 0; i < this.newArray.length; i++) {
      this.newArray[i].x = this.newArray[i].x + x;
      this.newArray[i].y = this.newArray[i].y + y;
    }
  }
  //Returns old array
  return() {
    this.newArray = this.oldArray.slice();
  }
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>


Solution

  • You aren't changing the array, you are mutating the objects within the array. You don't want that.

    const myObj = {
      x:1,
      y:1
    };
    
    const myArray = [myObj];
    const myNewArray = [...myArray];
    
    myNewArray[0].x = 2;
    console.log(myArray[0].x); // 2!!!!!
    

    If you are changing an object within the array and you don't want it to mutate, then do this:

    const myObj = {
      x:1,
      y:1
    };
    
    const myArray = [myObj];
    
    // helper function that will perform a mutation at a given position
    // and ignore all other positions
    const mutateObjectAtPosition = (pos,mutation) => [...myArray].map((o,i) => i === pos ? mutation(o) : o)
    
    // at position 0, return a **new object** with x set to 2
    const myNewArray = mutateObjectAtPosition(0,(o) => ({...o,x:2}));
    
    console.log(myNewArray !== myArray); // true, different references
    console.log(myNewArray[0].x); // 2
    console.log(myArray[0].x); // 1