从一个数组中删除包含在另一个数组中的所有元素。

428

我正在寻找一种有效的方法,从JavaScript数组中删除所有在另一个数组中存在的元素。

// If I have this array:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

// and this one:
var toRemove = ['b', 'c', 'g'];

我希望对myArray进行操作,使其变成这样:['a', 'd', 'e', 'f']

使用jQuery,我正在使用grep()inArray(),这很有效:

myArray = $.grep(myArray, function(value) {
    return $.inArray(value, toRemove) < 0;
});

有没有一种纯JavaScript的方法来做到这一点,而不需要使用循环和切割(splicing)?


无论如何,它总是会涉及到某个层面的循环。 - Blue Skies
1
如果你真的想让它“高效”,你不会使用像.filter()这样的函数式方法。相反,你会使用for循环。如果原始顺序不需要保持,你可以避免使用.splice()。或者,如果你认为有很多项需要删除,也有办法使.splice()更加高效。 - Blue Skies
不错,你的jQuery解决方案很适合我。谢谢。 - EPurpl3
17个回答

6
这篇文章有点晚了,但需要解释一下@mojtaba roohi所回答的内容。第一个代码块是行不通的,因为每个数组都有不同的对象,即df[0] != nfl[2]。这两个对象看起来很相似,但实际上完全不同,这种情况在使用原始类型(如数字)时并不会发生。

let df = [ {'name': 'C' },{'name': 'D' }] 
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }] 
let res = nfl.filter(x => df.indexOf(x)<0)
console.log(res)

以下是可工作的代码:

let df = [{'name': 'C' },{'name': 'D' }]
let nfl = [ {'name': 'A' },{'name': 'B' },{'name': 'C' },{'name': 'D' }];
let res = nfl.filter((o1) => !df.some((o2) => o1.name === o2.name));
console.log(res)


5
现在以一行简洁的方式来表达:

console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))

可能无法在旧版浏览器上正常工作。


1

高性能且不可变的解决方案

Javascript

const excludeFromArr = (arr, exclude) => {
  const excludeMap = exclude.reduce((all, item) => ({ ...all, [item]: true }), {});
  return arr.filter((item) => !excludeMap?.[item]);
};

Typescript:

const excludeFromArr = (arr: string[], exclude: string[]): string[] => {
  const excludeMap = exclude.reduce<Record<string, boolean>>((all, item) => ({ ...all, [item]: true }), {});
  return arr.filter((item) => !excludeMap?.[item]);
};

1
这个解决方案的高性能是什么造就的? - raarts
#reduce比起是一个本地方法的#includes更慢,因为在理论复杂度 O(1) 与 O(n) 的情况下,其中 n = excludedMap.length ,后者的速度更优。相反,它应该比其他答案中提到的任何对应方法都要差,如#includes#indexOf等。 - Andre Figueiredo

1
如果您正在使用TypeScript,并且想匹配单个属性值,则可以基于上面 Craciun Ciprian的答案 进行操作。
您还可以通过允许非对象匹配和/或多属性值匹配来使其更加通用。
/**
 *
 * @param arr1 The initial array
 * @param arr2 The array to remove
 * @param propertyName the key of the object to match on
 */
function differenceByPropVal<T>(arr1: T[], arr2: T[], propertyName: string): T[] {
  return arr1.filter(
    (a: T): boolean =>
      !arr2.find((b: T): boolean => b[propertyName] === a[propertyName])
  );
}

0

我构建了逻辑而没有使用任何内置方法,请让我知道任何优化或修改。我在JS编辑器中测试过,它可以正常工作。

var myArray = [
            {name: 'deepak', place: 'bangalore'},
            {name: 'alok', place: 'berhampur'},
            {name: 'chirag', place: 'bangalore'},
            {name: 'chandan', place: 'mumbai'},

        ];
        var toRemove = [

            {name: 'chirag', place: 'bangalore'},
            {name: 'deepak', place: 'bangalore'},
            /*{name: 'chandan', place: 'mumbai'},*/
            /*{name: 'alok', place: 'berhampur'},*/


        ];
        var tempArr = [];
        for( var i=0 ; i < myArray.length; i++){
            for( var j=0; j<toRemove.length; j++){
                var toRemoveObj = toRemove[j];
                if(myArray[i] && (myArray[i].name === toRemove[j].name)) {
                    break;
                }else if(myArray[i] && (myArray[i].name !== toRemove[j].name)){
                        var fnd = isExists(tempArr,myArray[i]);
                        if(!fnd){
                            var idx = getIdex(toRemove,myArray[i])
                            if (idx === -1){
                                tempArr.push(myArray[i]);
                            }

                        }

                    }

                }
        }
        function isExists(source,item){
            var isFound = false;
            for( var i=0 ; i < source.length; i++){
                var obj = source[i];
                if(item && obj && obj.name === item.name){
                    isFound = true;
                    break;
                }
            }
            return isFound;
        }
        function getIdex(toRemove,item){
            var idex = -1;
            for( var i=0 ; i < toRemove.length; i++){
                var rObj =toRemove[i];
                if(rObj && item && rObj.name === item.name){
                    idex=i;
                    break;
                }
            }
            return idex;
        }

0

从另一个数组中删除所有包含的元素的正确方法是通过仅删除元素使源数组成为相同的对象:

Array.prototype.removeContained = function(array) {
  var i, results;
  i = this.length;
  results = [];
  while (i--) {
    if (array.indexOf(this[i]) !== -1) {
      results.push(this.splice(i, 1));
    }
  }
  return results;
};

或者 CoffeeScript 等效语句:

Array.prototype.removeContained = (array) ->
  i = @length
  @splice i, 1 while i-- when array.indexOf(@[i]) isnt -1

在 Chrome 开发者工具中进行测试:

19:33:04.447 a=1
19:33:06.354 b=2
19:33:07.615 c=3
19:33:09.981 arr = [a,b,c]
19:33:16.460 arr1 = arr

19:33:20.317 arr1 === arr
19:33:20.331 true

19:33:43.592 arr.removeContained([a,c])
19:33:52.433 arr === arr1
19:33:52.438 true

使用 Angular 框架是在更新集合时保持对源对象的指针而不需要大量的监视器和重新加载的最佳方法。


这个答案很糟糕,因为它违反了最佳实践。具体来说,永远不要修改你不拥有的对象。在这种情况下,您正在修改Array对象,这是一个大忌。 - Hybrid web dev

0

//Using the new ES6 Syntax

    console.log(["a", "b", "c", "d", "e", "f", "g"].filter(el => !["b", "c", "g"].includes(el)));

    // OR

    // Main array
    let myArray = ["a", "b", "c", "d", "e", "f", "g"];

    // Array to remove
    const toRemove = ["b", "c", "g"];

    const diff = () => (myArray = myArray.filter((el) => !toRemove.includes(el)));
  console.log(diff()); // [ 'a', 'd', 'e', 'f' ]

    // OR

    const diff2 = () => {
      return myArray = myArray.filter((el) => !toRemove.includes(el));
    };
    console.log(diff2()); // [ 'a', 'd', 'e', 'f' ]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接