【问题标题】:Remove all elements contained in another array删除另一个数组中包含的所有元素
【发布时间】:2013-11-26 06:21:23
【问题描述】:

我正在寻找一种有效的方法来从 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 的方式来做到这一点而无需循环和拼接?

【问题讨论】:

标签: javascript arrays


【解决方案1】:

使用Array.filter() 方法:

myArray = myArray.filter( function( el ) {
  return toRemove.indexOf( el ) < 0;
} );

小幅改进,因为浏览器对Array.includes() 的支持有所增加:

myArray = myArray.filter( function( el ) {
  return !toRemove.includes( el );
} );

下一次适配使用arrow functions

myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );

【讨论】:

  • OP:如果您使用的是Underscore.js,那么.difference() 基本上就是这样做的。
  • @AlecRust 将toRemove()的所有元素转换为大写,并将回调从el更改为el.toUpperCase()
  • 或更短:myArray = myArray.filter( el =&gt; !toRemove.includes( el ) );
  • 这个顺序不是n^2吗?
  • 有没有办法按照 n 的顺序实现这个?
【解决方案2】:

filter 方法应该可以解决问题:

const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = ['b', 'c', 'g'];

// ES5 syntax
const filteredArray = myArray.filter(function(x) { 
  return toRemove.indexOf(x) < 0;
});

如果您的toRemove 数组很大,这种查找模式可能效率低下。创建一个映射以便查找是O(1) 而不是O(n) 会更高效。

const toRemoveMap = toRemove.reduce(
  function(memo, item) {
    memo[item] = memo[item] || true;
    return memo;
  },
  {} // initialize an empty object
);

const filteredArray = myArray.filter(function (x) {
  return toRemoveMap[x];
});

// or, if you want to use ES6-style arrow syntax:
const toRemoveMap = toRemove.reduce((memo, item) => ({
  ...memo,
  [item]: true
}), {});

const filteredArray = myArray.filter(x => toRemoveMap[x]);

【讨论】:

  • 我真的很喜欢这个答案...我只想指出应该返回的最后一行 const filteredArray = myArray.filter(x => !toRemoveMap[x]); // 而不是 toRemoveMap[x]
【解决方案3】:

如果你不能使用新的 ES5 东西,比如 filter,我想你会被两个循环困住:

for( var i =myArray.length - 1; i>=0; i--){
  for( var j=0; j<toRemove.length; j++){
    if(myArray[i] === toRemove[j]){
      myArray.splice(i, 1);
    }
  }
}

【讨论】:

  • 过滤器不是“新的 HTML5 东西”
  • 我应该写“ES5 的东西”。它不适用于 ES3
【解决方案4】:

如果您使用的是对象数组。那么下面的代码应该会发挥作用,其中一个对象属性将成为删除重复项的标准。

在下面的示例中,已删除重复项比较每个项目的名称。

试试这个例子。 http://jsfiddle.net/deepak7641/zLj133rh/

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

for( var i=myArray.length - 1; i>=0; i--){
 	for( var j=0; j<toRemove.length; j++){
 	    if(myArray[i] && (myArray[i].name === toRemove[j].name)){
    		myArray.splice(i, 1);
    	}
    }
}

alert(JSON.stringify(myArray));

【讨论】:

    【解决方案5】:

    Lodash 也有一个实用功能: https://lodash.com/docs#difference

    【讨论】:

      【解决方案6】:

      我刚刚实现为:

      Array.prototype.exclude = function(list){
              return this.filter(function(el){return list.indexOf(el)<0;})
      }
      

      用作:

      myArray.exclude(toRemove);
      

      【讨论】:

      • 扩展原生对象的prototypes 不是好的做法,例如Array。这可能与语言的未来发展产生长期冲突(see the flatten case
      【解决方案7】:

      现在是单线风格:

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

      可能不适用于旧浏览器。

      【讨论】:

        【解决方案8】:

        ECMAScript 6 sets 可以更快地计算一个数组中不在另一个数组中的元素:

        const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
        const toRemove = new Set(['b', 'c', 'g']);
        
        const difference = myArray.filter( x => !toRemove.has(x) );
        
        console.log(difference); // ["a", "d", "e", "f"]

        由于目前V8引擎浏览器使用的lookup complexity是O(1),所以整个算法的时间复杂度是O(n)。

        【讨论】:

        • 很好的答案。我完全忘记了这个
        • 与使用带有.includes 的数组相比,这非常非常快。对于大型阵列(约 50,000 个),它从大约 79 秒变为不到 1 秒。感谢发帖。
        【解决方案9】:

        您可以使用来自 lodash 的_.differenceBy

        const myArray = [
          {name: 'deepak', place: 'bangalore'}, 
          {name: 'chirag', place: 'bangalore'}, 
          {name: 'alok', place: 'berhampur'}, 
          {name: 'chandan', place: 'mumbai'}
        ];
        const toRemove = [
          {name: 'deepak', place: 'bangalore'},
          {name: 'alok', place: 'berhampur'}
        ];
        const sorted = _.differenceBy(myArray, toRemove, 'name');
        

        此处的示例代码:CodePen

        【讨论】:

        • 如果属性嵌套在对象内部怎么办?在您的情况下进行一些测试 {name: 'deepak', place: 'bangalore' , nested : { test : 1}}
        【解决方案10】:

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

        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 是的

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

        当您在没有大量观察者和重新加载的情况下更新集合时,使用 Angular 框架是保持指向源对象的指针的最佳方式。

        【讨论】:

        • 这个答案很糟糕,因为它使最佳实践无效。具体来说,永远不要修改您不拥有的对象。在这种情况下,您正在修改 Array 对象,这是一个很大的禁忌。
        【解决方案11】:

        我没有使用任何内置方法构建逻辑,请让我知道任何优化或修改。 我在 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;
                }
        

        【讨论】:

          【解决方案12】:
          var myArray = [
            {name: 'deepak', place: 'bangalore'}, 
            {name: 'chirag', place: 'bangalore'}, 
            {name: 'alok', place: 'berhampur'}, 
            {name: 'chandan', place: 'mumbai'}
          ];
          var toRemove = [
            {name: 'deepak', place: 'bangalore'},
            {name: 'alok', place: 'berhampur'}
          ];
          
          
          
          myArray = myArray.filter(ar => !toRemove.find(rm => (rm.name === ar.name && ar.place === rm.place) ))
          

          【讨论】:

          • 您介意对这样一个广受欢迎的问题添加一些解释吗?
          • 我搜索了几个小时来寻找问题的解决方案,结果发现了,太棒了。非常感谢!
          • 你帮我在 1 天内完成了我的 jira,谢谢好友
          • 这对于具有唯一属性的对象数组很有用。 +1
          【解决方案13】:

          最简单的怎么样:

          var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
          var toRemove = ['b', 'c', 'g'];
          
          var myArray = myArray.filter((item) => !toRemove.includes(item));
          console.log(myArray)

          【讨论】:

          • 请记住 includes 在 ES7 之前不可用。
          【解决方案14】:

          如果您使用 Typescript 并希望匹配单个属性值,则应该根据上面的 Craciun Ciprian's answer 工作。

          您还可以通过允许非对象匹配和/或多属性值匹配来使其更通用。

          /**
           *
           * @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])
            );
          }
          

          【讨论】:

            【解决方案15】:

            这已经很晚了,但添加这个来解释@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)

            【讨论】:

              猜你喜欢
              • 2014-06-07
              • 2011-05-20
              • 2016-03-15
              • 2021-12-30
              • 1970-01-01
              • 1970-01-01
              • 2019-05-05
              相关资源
              最近更新 更多