Home » Javascript » javascript – Why Splice removes only last element when array elements are the same

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

Posted by: admin February 24, 2020 Leave a comment

Questions:

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.

How to&Answers:

And
deleting button only deletes from the last index instead of providing
row number index though.

No. To delete a specific element from an array using splice you need to pass in the index along with 1 as the second parameter. That’s what you are doing in the example that doesn’t work. More info can be read up in the MDN splice docs.

Also, to ensure that there is no ambiguity add a new number everytime. Now in the deleteRow() add console logs to see what is happening as illustrated in this forked stackblitz.

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

You can see the contents of the array (before and after) whenever an element is deleted.

Answer:

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 are 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.