Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify original array with Javascript Array.Filter

I want to use Javascript's array.filter to remove items from an array, because the syntax is elegant and readable. However, it seems that filter doesn't modify the original array, it just returns a new array, filtered as you requested. My question is, why doesn't the following work as I expect?

$scope.clearList = function () {
  this.list = this.list.filter(function (item) {
    return item.checked == true;
  });

  //...
}

I would expect that after returning the newly filtered array, this.list would now hold just the filtered set. It does not work like this however. this.list ends up containing exactly the same items. Altering the code to save out the filtered array in an intermediate variable shows that it is indeed filtering correctly.

I've done a workaround for now, looping through the filtered version and splicing items out of the original list that should be filtered, but this is inelegant. Am I just thinking about it the wrong way?


Sidenote: I'm using Angular.js. I'm unsure it matters at all, but the list comes from the following:

  <div class="list" ng-repeat="list in lists">
    <!-- ... -->
    <ul>
      <li ng-repeat="item in list">
        <div>
          <label>
            <input type="checkbox" ng-model="item.checked"/>
            {{item.name}}
          </label>
          <!-- ... -->
        </div>
      </li>
    </ul>
    <button class="btn clear-selected" ng-click="clearList()">
      Remove Selected
    </button>
  </div>

Edit to add debugging info: I've introduced a temp variable just to see whats going on in the debugger.

var temp = this.list.filter(function (item) {
  return item.checked == true;
});

this.list = temp;

Before execution, this.List has 5 items, temp is undefined. After the first line is executed, this.List has 5 items, and temp has 2 items. After the last line is executed, this.List has 2 items, temp has 2 items.

However, it seems after this that the UI which is bound to this.list does not update. So something unrelated to filter does seem to be going on.

like image 355
jtheis Avatar asked Dec 14 '12 20:12

jtheis


2 Answers

In angular you modify data using the special $scope variable, and while inside your controller this points to the $scope as the executing context, $scope is preferred.

When the UI doesn't update, it's usually because changes in "models" (or properties of a given scope) are done outside angular. In this case a call to $apply is needed. This notifies angular that something has changed and to update the views.

However, this doesn't seem to be your issue. I have a working list with minimal changes here http://plnkr.co/edit/Cnj0fEHSmi2L8BjNRAf5?p=preview

Here's the contents of the controller and when you call clearList() from the UI, only checked items are left in the list.

$scope.list = [
  {name: 'one', checked: true},
  {name: 'two', checked: false},
  {name: 'three', checked: true},
  {name: 'four', checked: false}
];

$scope.clearList = function () {
  $scope.list = $scope.list.filter(function(item) {
    return item.checked === true;
  });
};  

Now, I'd recommend passing a list to clearList clearList(list) or even better use Angular filters.

like image 74
jaime Avatar answered Oct 26 '22 18:10

jaime


window.list = [1,2,3,4,5,6];
var clearList = function () {
    this.list = this.list.filter(function (item) { return item % 2 === 0; });
};
clearList();
console.log(window.list);

Logs [2, 4, 6] as expected, so I think whatever your bug is is unrelated to filter.

Are you sure that the array you're modifying with this.list is the same array that you later come to check?

like image 43
RichardTowers Avatar answered Oct 26 '22 17:10

RichardTowers