1

I'm getting some unexpected behaviours (according to my concept) while executing some Array.prototype functions (such as .map, .reduce, etc). The problem is that this functions are changing the values of the model variables. I created a piece of code to reproduce that:

import { Component, OnInit } from '@angular/core';

const getServerData = [
  { key: 'test1', value: 1 },
  { key: 'test2', value: 2 },
  { key: 'test3', value: 3 },
  { key: 'test4', value: 4 },
];

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {

  // docs receive values of const getServerData
  docs = getServerData;

  ngOnInit() {
    const test = this.mapFunctionTest(this.docs);
    console.log(this.docs);
  }

  // Here is the problem. I don't know why mapFunctionTest modifies the values 
  // of docs (value = 1000). What I expect is that the constant test should 
  // receive the returned array from mapFunctionTest and the value of docs 
  // keep unalterated (and wasn't assigned to value = 1000)
  mapFunctionTest(array: any[]): any[] {
    const testedArray = array.map((element) => {
      element.value = 1000;
      return element;
    });
    return testedArray;
  }
}

What I intend with this code is that the constant "test" receives the array returned from the function mapFunctionTest with the values:

[
    {key: "test1", value: 1000},
    {key: "test2", value: 1000},
    {key: "test3", value: 1000},
    {key: "test4", value: 1000}
]

while the docs variable stays with its content unchanged (which is not happening):

[
    {key: "test1", value: 1},
    {key: "test2", value: 2},
    {key: "test3", value: 3},
    {key: "test4", value: 4}
]

How to use Array.prototype functions without change the value of its original array?

2 Answers2

1

You should do this instead:

array.map(el => ({key: el.key, value: 1000}))

Although map does create a new array, it will not create new instances of its elements. So if you modify an element, it will be modified in the original array as well. That's why you need to create a new object.


To create a shallow copy of a larger object, you can do:

Object.assign({}, el, { value: 1000 })

This will assign all properties of el to a new object and then assign value to the new object. If value already exists it will be overridden.

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • Thanks for your answer and make things clear. But what if I have a big JSON, with multiple properties (a big `el` in your example), what is the best way to instantiate a new object with the same key-values of the mapped element? I tried `const newElement = {}`, `newElement.info = element` and modify key values in `newElement.info`, but didn't worked (docs changes also). – Pedro Matias Jan 31 '19 at 03:53
0

Or you could create a copy of the array first and make operation on it by using Array.slice()

So, it will create a new object of the array. And will not change values of the copy.

jithil
  • 1,098
  • 11
  • 11
  • Thanks for your answer. I tried 3 ways according to your answer: 1. Pass copied array as parameter: `const test = this.mapFunctionTest(this.docs.slice())`. 2. Make a copy inside function: `const copied = array.slice()` and `const testedArray = copied.map(...)` 3. Make a copy, execute the function with the original array and restore the original array by the copy: `const copied = this.docs.slice()`, `const test = this.mapFunctionTest(this.docs)` and `this.docs = copied`. None of them worked. Am I missing something? – Pedro Matias Jan 31 '19 at 04:02