Why Splice removes only last element when array elements are the same

Go to this StackBlitz Link (Not Working)

Go to this StackBlitz Link (Working Fine)

My question is about Array.splice.
Array.splice is working fine when array data are different but when data of array are of the same kind, Array.splice removes only the last index. In this case, splice ignores provided index to remove element from array.

Both example has common HTML Template

<div class="container">
    <table class="table table-dark table-striped table-hover">
        <thead>
           <tr>
               <td scope="col"> Index </td>
               <td scope="col"> Value </td>
               <td scope="col"> Action </td>
           </tr>
        </thead>
        <tbody>
            <tr *ngFor="let row of rows; let i = index"  [@itemAnim]>
                <td> {{i}} </td>
                <td>{{row}}</td>
                <td><button class="btn btn-danger" (click)="deleteRow(i)">Delete</button></td>
           </tr>
        </tbody>
    </table>
    <button class="btn btn-primary " (click)="addRow()">Add row</button>
</div>

See this example working as expected:

index = 1;
rows = [1]; 

addRow() {   
   this.rows.length > 0 ?  this.index ++ : this.index = 1;
   this.rows.push(this.index)
}

deleteRow(row: number) {
   return this.rows.length > 0 ? this.rows.splice(row, 1) : this.index = 1;
}

And this is not working:

rows = [1]; 

addRow() {   
  this.rows.push(1)
}

deleteRow(row: number) {
   this.rows.splice(row, 1) ;
}

In the above code, I am pushing only 1 when addButton is clicked. And deleting button only deletes from the last index instead of providing row number index though.

My question is why this is happening? The above stackBlitz link shows about same ambiguity.

1 thought on “Why Splice removes only last element when array elements are the same”

  1. Because you are actually removing the selected index, but Angular does not have any information about the difference between values, let’s see an example:

    We have this array: [1, 1, 1, 1]
    and you remove the second item, then angular before remove will see:

    [1, 1, 1, 1]
    

    and after:

    [1, 1, 1]
    

    at this point angular only knows there is one item less, but not know wish item, then angular just removes the last one.

    You need to ensure you are passing diffenrent values or references(recommended), then angular will know how to differentiate array items.

    You can fix it by using objects like: { data: 1 }, your array will looks:

    [
      { data: 1 },
      { data: 1 },
      { data: 1 },
      { data: 1 }
    ]
    

    And why will this work? because references are not the same (Value vs Reference Types) and angular will see diffenrent references even if the data is the same.

    This will work for you:

    rows = [{ data: 1 }]; 
    
    addRow() {   
      this.rows.push({ data: 1 });
    }
    
    deleteRow(row: number) {
      this.rows.splice(row, 1);
    }
    

    In this example you are always passing a new object with its own reference.


    This another example will not work

    sameReference = { data: 1 };
    
    rows = [this.sameReference]; 
    
    addRow() {   
      this.rows.push(this.sameReference);
    }
    
    deleteRow(row: number) {
      this.rows.splice(row, 1);
    }
    

    because sameReference variable stores the reference, not the object, and angular will not know (again) differences between items, and additionally when you change the data value of one element, all elements will get that value, because there is only one object referenced N times within the array.


    And this will work too

    sameReference = { data: 1 };
    
    rows = [{ ...this.sameReference }]; 
    
    addRow() {   
      this.rows.push({ ...this.sameReference });
    }
    
    deleteRow(row: number) {
      this.rows.splice(row, 1);
    }
    

    because here we are copying the values of the object into a new object with different reference.

    Reply

Leave a Comment