1

I have object of type:

export interface IGroupLanguage {
  name: string;
  languages?: Language[];
}

let data = [ { "name": "Automation", "languages": [ { "Name": "English", "Lcid": 1, "RightToLeft": true, "Code": "EN", "Mapped": true } ] }, { "name": "Monitors", "languages": [ { "Name": "Russian", "Lcid": 2, "RightToLeft": true, "Code": "RU", "Mapped": true } ] } ];

Then I tried to filter object and return a new object:

 this.filteredObject = [...this.groups];
    this.filteredObject.map(item => {
      item.languages = item.languages.filter(
        lg =>
          lg.Name.toLocaleLowerCase().indexOf(searchQuery) != -1 ||
          lg.Code.toLocaleLowerCase().indexOf(searchQuery) != -1
      );
    });

Problem is that initial object this.groups is changed also. How to save initial statement of object?

In result I need to have not modified object this.groups and filtered object this.filteredObject.

I know problem because JS copy objects by reference, but I dont know how to solve it.

Full code is:

search(searchQuery: string) {
    this.filteredObject = [...this.groups];
    this.filteredObject.map(item => {
      let tempLang = [...item.languages];
      item.languages = tempLang.filter(
        lg =>
          lg.Name.toLocaleLowerCase().indexOf(searchQuery) != -1 ||
          lg.Code.toLocaleLowerCase().indexOf(searchQuery) != -1
      );
    });

    console.log(this.groups);
  }


ngOnInit() {
    this.filteredObject = [...this.groups];
  }

As result initial object console.log(this.groups); is also was modified

POV
  • 11,293
  • 34
  • 107
  • 201

3 Answers3

2

Because you have deep copied parent array of data not nested one, can you give it a try to below way -

this.filteredObject = [...this.groups];
    this.filteredObject.map(item => {
      let tempLang = [...item.languages];
      item.languages = tempLang.filter(lg =>
          lg.Name.toLocaleLowerCase().indexOf(searchQuery) != -1 ||
          lg.Code.toLocaleLowerCase().indexOf(searchQuery) != -1
      );
    });
Pardeep Jain
  • 84,110
  • 37
  • 165
  • 215
  • Is it required `this.filteredObject = [...this.groups];`? – POV Oct 31 '19 at 20:32
  • I get still modified object ` console.log(this.groups);` after filtering – POV Oct 31 '19 at 20:36
  • I would suggest to try in a fresh piece of file, because `...` will deep clone your object/array which is required in your use case, probably something else is causing issue. – Pardeep Jain Oct 31 '19 at 20:38
1

You need to deep clone your object. If you know the structure of your object, you can create a custom deep clone function with recursiveness. Otherwise, you can use the library lodash

Wandrille
  • 6,267
  • 3
  • 20
  • 43
  • So it is common problem in Angular/JS when needs filter objects? – POV Oct 31 '19 at 20:33
  • 2
    My answer is generic yes. The answer of @Pardeep Jain is better for your specific problem. This is not a problem a filter but more of clone. – Wandrille Oct 31 '19 at 20:34
1

Spread syntax performs a shallow copy of the object.

MDN documentation on copying an array with spread syntax:

Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).

This means that if there are any objects at the top level, their reference will be copied. That is the reason for this problem.

You can use JSON.parse(JSON.stringify()) to deep clone an object. But there will be data loss if the object has Dates, functions, undefined, Infinity, or other complex types. More info in this answer.

Also, when you are not returning any values use forEach() instead of a map().

this.filteredObject = JSON.parse(JSON.stringify(this.groups));

filteredObject.forEach(item => {
  item.languages = item.languages.filter(lg =>
    lg.Name.toLocaleLowerCase().indexOf(searchQuery.toLocaleLowerCase()) != -1 ||
    lg.Code.toLocaleLowerCase().indexOf(searchQuery.toLocaleLowerCase()) != -1
  );
});

This works for properties containing objects at any nested level.

Live Example:

let data = [ { "name": "Automation", "languages": [ { "Name": "English", "Lcid": 1, "RightToLeft": true, "Code": "EN", "Mapped": true } ] }, { "name": "Monitors", "languages": [ { "Name": "Russian", "Lcid": 2, "RightToLeft": true, "Code": "RU", "Mapped": true } ] } ];

let searchQuery = "English";

let filteredObject = JSON.parse(JSON.stringify(data));

filteredObject.forEach(item => {
  item.languages = item.languages.filter(lg =>
    lg.Name.toLocaleLowerCase().indexOf(searchQuery.toLocaleLowerCase()) != -1 ||
    lg.Code.toLocaleLowerCase().indexOf(searchQuery.toLocaleLowerCase()) != -1
  );
});


console.log(data);
console.log(filteredObject);
Nikhil
  • 6,493
  • 10
  • 31
  • 68