简体   繁体   中英

How to add/remove elements from array based on array contents

I've been struggling with this piece for a few days and I can't seem to find what's wrong. I have an array with a few objects:

myMainPostObj.categories = [Object, Object]

This is for add/removing categories to a post. Imagine I'm editing an existing post which is already associated with a couple of categories (as per code above).

I also have an array which has all categories in the db (including the ones which are associated with the post). On my js controller I have the following code:

$scope.addCategory = function (cat) {
    for (var i in $scope.post.category_ids) {
        if (cat._id === $scope.post.category_ids[i]) {
            $scope.post.category_ids.slice(i, 1);
        } else if (cat._id !== $scope.post.category_ids[i]) {
            $scope.post.category_ids.push(cat._id);
        }
    }
}

The above function is called every time the user click on a category. The idea is for the above function to loop through the categories within the post (associated with the post) and compares it with the category passed as argument. If it matches the function should remove the category. If it doesn't it should add.

In theory this seems straight forward enough, but for whatever reason if I tick on category that is not associated with the post, it adds two (not one as expected) category to the array. The same happens when I try to remove as well.

This is part of a Angular controller and the whole code can be found here

I guess the problem could be that you're altering the category_ids array while you're iterating over it with the for loop. You might be better off trying something like this:

$scope.addCategory = function (cat) {
var catIndex = $scope.post.category_ids.indexOf(cat._id);
if (catIndex > -1)
    $scope.post.category_ids.splice(catIndex, 1);
else
    $scope.post.category_ids.push(cat._id);
}

Note that indexOf doesn't seem to be supported in IE7-8.

Let's simplify this a bit:

const CATEGORIES = [1, 2, 3];

let addCategory = (postCategories, categoryId) => {
  CATEGORIES.forEach((cId, index) => {
    if (postCategories[index] === cId) console.log("Removing", categoryId);
    else console.log("Adding", categoryId);
  });
  return postCategories;
};

Please ignore the fact that we actually are not mutating the array being passed in.

A is either equal or not equal to B - there is no third option ( FILE_NOT_FOUND aside). So you are looping over all of your categories and every time you don't find the category ID in the array at the current index you add it to the postCategories array .

The proper solution to your problem is just to use a Set (or if you need more than bleeding edge ES6 support, an object with no prototype):

// Nicer, ES6-or-pollyfill option
var postCategories = new Set();
postCategories.add(categoryId);

// Or, in the no-prototype option:
var postCategories = Object.create(null);
postCategories[categoryId] = true;
// Then serialization needs an extra step if you need an array:
parentObject.postCategories = Object.keys(parentObject.postCategories);

The error in your code is that for each iteration of the loop you either remove or add a category. This isn't right... You should remove if the current id matches but add only if there was no match at all. Something like this:

$scope.addCategory = function (cat) {
    var found = false;
    for (var i in $scope.post.category_ids) {
        if (cat._id === $scope.post.category_ids[i]) {
            $scope.post.category_ids.splice(i, 1);   // splice, not slice
            found = true;
        } 
    }
    if (!found)  // add only if it wasn't found
        $scope.post.category_ids.push(cat._id);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM