77

Say i have the following object array, lets name it itemArray;

{
  "totalItems": 2,
  "items": [
    {
      "id": 1,
      "name": "foo"

    },
    {
      "id": 2,
      "name": "bar"
    },
    ]
}

And i have a subscription that returns the updated result of only id 2. How would i update the object array without looping through the entire array?

What i would like is something like the example below;

updateUser(user){
    this.myservice.getUpdate(user.id)
    .subscribe(newitem => {
      this.updateArray(newitem);
    });
}

  updateArray(newitem){
    this.itemArray.items[newitem.id].name = newitem.name
  }

or even better, replacing the entire object;

  updateArray(newitem){
    this.itemArray.items[newitem.id] = newitem
  }

This example however updates the array based on the index of the array. So how do i instead update based on newitem.id?

Template requested in comment:

<tr *ngFor="let u of itemsArray.items; let i = index">
  <td>{{ u.id }}</td>
  <td>{{ u.name }}</td>
  <td>
    <input type="checkbox" checked="u.accepted" [(ngModel)]="itemsArray.items[i].accepted" (ngModelChange)="updateUser(u)">
    <label for="singleCheckbox-{{i}}"></label>
  </td>
</tr>
6
  • Are you using this object for rendering the output from a loop on this itemArray (by id of 2)? What I'm getting at, is could you just write a custom pipe for this filter? Commented May 22, 2017 at 19:26
  • "This example however updates the array based on the index of the array. So how do i instead update based on newitem.id?". Why don't you want to update an item from an index? It will certainly be faster than looping the array searching for a specific id... Commented May 22, 2017 at 19:26
  • updating from index would be fine. How would i find the right index? Now the index does not correspond with the newitem.id Commented May 22, 2017 at 19:27
  • although this is a response from the back-end it does not have the index. Could i lookup the index based on the newitem.id in this.itemArray.items? Commented May 22, 2017 at 19:30
  • Can you include your template in question? Commented May 22, 2017 at 19:41

11 Answers 11

118

Update:

  showUpdatedItem(newItem){
    let indexToUpdate = this.itemArray.items.findIndex(item => item.id === newItem.id);
    this.itemArray.items[indexToUpdate] = newItem;

   // some angular libraries require breaking the array reference
   // to pick up the update in the array and trigger change detection.
   // In that case, you can do following

   this.itemArray.items = Object.assign([], this.itemArray.items);
  }

Stackblitz Demo

Original Answer:

I have created this Plunker based on your example that updates the object equal to newItem.id

Here's the snippet of my functions:

showUpdatedItem(newItem){
    let updateItem = this.itemArray.items.find(this.findIndexToUpdate, newItem.id);

    let index = this.itemArray.items.indexOf(updateItem);

    this.itemArray.items[index] = newItem;

  }

  findIndexToUpdate(newItem) { 
        return newItem.id === this;
  }
Sign up to request clarification or add additional context in comments.

5 Comments

Minor typo at the end of the second line. )); should be );
excellent! only part i am confused on is how to refresh the array on the screen.
Correct answer, but could be shortened by using findIndex instead.
Thanks Nahal. I used your code here - stackblitz.com/edit/angular-item-edit-jk7b9k
Back then this was probably the way to go. But Angular digest prefers a new array to make it easier for them to see that it was updated. I was looking for the correct splice syntax. But splice isn't the answer either. The best way to do this is by using map this.itemArray = this.itemArray.map((entry) => entry.id === newItem.id ? newItem : entry); (You can write this multiline if you want to make it prettier)
44

Updating directly the item passed as argument should do the job, but I am maybe missing something here ?

updateItem(item){
  this.itemService.getUpdate(item.id)
  .subscribe(updatedItem => {
    item = updatedItem;
  });
}

EDIT : If you really have no choice but to loop through your entire array to update your item, use findIndex :

let itemIndex = this.items.findIndex(item => item.id == retrievedItem.id);
this.items[itemIndex] = retrievedItem;

Comments

35

Another approach could be:

let myList = [{id:'aaa1', name: 'aaa'}, {id:'bbb2', name: 'bbb'}, {id:'ccc3', name: 'ccc'}];
let itemUpdated = {id: 'aaa1', name: 'Another approach'};

myList.find(item => item.id == itemUpdated.id).name = itemUpdated.name;

Comments

9

You can try this also to replace existing object

toDoTaskList = [
                {id:'abcd', name:'test'},
                {id:'abcdc', name:'test'},
                {id:'abcdtr', name:'test'}
              ];

newRecordToUpdate = {id:'abcdc', name:'xyz'};
this.toDoTaskList.map((todo, i) => {
         if (todo.id == newRecordToUpdate .id){
            this.toDoTaskList[i] = updatedVal;
          }
        });

1 Comment

This is not the purpose of an array's map() function.
8

Try Array.ForEach() method.

itemArray.ForEach(item =>{
    if(item.id == newitem.id){
        item.name = newitem.name
    }
});

Comments

3

I would rather create a map

export class item{
    name: string; 
    id: string
}

let caches = new Map<string, item>();

and then you can simply

this.caches[newitem.id] = newitem; 

even

this.caches.set(newitem.id, newitem); 

array is so 1999. :)

1 Comment

I'm not sure but I believe a Map is not iterable in an *ngFor loop. I believe I had that issue before in Angular with maps.
3

You can update one item like.

let item = list.find(item => item.id === id);

if( item ) {

  item.name = 'other name';

} 

Comments

2
updateValue(data){    
     // retriving index from array
     let indexValue = this.items.indexOf(data);
    // changing specific element in array
     this.items[indexValue].isShow =  !this.items[indexValue].isShow;
}

Comments

0

You can use for loop to find your element and update it:

updateItem(newItem){
  for (let i = 0; i < this.itemsArray.length; i++) {
      if(this.itemsArray[i].id == newItem.id){
        this.users[i] = newItem;
      }
    }
}

1 Comment

You can simply use this.users.push( itemsArray.find( el => el.id == newItem.id ) )
0

In angular/typescript we can avoid mutation of the objects in the array.

An example using your item arr as a BehaviorSubject:

// you can initialize the items$ with the default array
this.items$ = new BehaviorSubject<any>([user1, user2, ...])

updateUser(user){
   this.myservice.getUpdate(user.id).subscribe(newitem => {

     // remove old item
     const items = this.items$.value.filter((item) => item.id !== newitem.id);

     // add a the newItem and broadcast a new table
     this.items$.next([...items, newItem])
   });
}

And in the template you can subscribe on the items$

<tr *ngFor="let u of items$ | async; let i = index">
   <td>{{ u.id }}</td>
   <td>{{ u.name }}</td>
   <td>
        <input type="checkbox" checked="u.accepted" (click)="updateUser(u)">
        <label for="singleCheckbox-{{i}}"></label>
   </td>
</tr>

Comments

0

Find that item index and then update it.

public editData(key: any, Value: any) {
    const index = this.EmpData.findIndex((item) => item.id === key.id);

    console.log(index);
    
    if (index !== -1) {
        Value.value.id = key.id;
        this.EmpData[index] = Value.value;
        const jsonData = JSON.stringify(this.EmpData);
        localStorage.setItem('EmpData', jsonData);
    } 
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.