【问题标题】:Check if every element in one array is in a second array检查一个数组中的每个元素是否都在第二个数组中
【发布时间】:2012-01-27 12:31:49
【问题描述】:

我有两个数组,我想检查arr2 中的每个元素是否都在arr1 中。如果一个元素的值在arr2 中重复,则它需要在arr1 中重复相同的次数。这样做的最佳方法是什么?

arr1 = [1, 2, 3, 4]
arr2 = [1, 2]

checkSuperbag(arr1, arr2)
> true //both 1 and 2 are in arr1

arr1 = [1, 2, 3, 4]
arr2 = [1, 2, 5]

checkSuperbag(arr1, arr2)
> false //5 is not in arr1

arr1 = [1, 2, 3]
arr2 = [1, 2, 3, 3]

checkSuperbag(arr1, arr2)
> false //3 is not in arr1 twice

【问题讨论】:

  • 最后一个例子应该返回 false。如果 2 个数组的长度相同,则没有超集/子集。 mathworld.wolfram.com/Superset.html
  • 集合不能包含重复的元素,因此在这些条件下确定某事物何时是超集的概念没有多大意义。
  • 最后一个例子应该是true,有两个原因:(1) 在集合中重复并不重要:{1,1} = {1}。 (2) 一个集合是它自己的子集和超集;如果两者不应该相等,则称为“正确子集”和“正确超集”。
  • @AdamRackis 抱歉,我的意思不是超集,什么是更好的术语?
  • “Bag”有时用于指代允许重复的无序集合。

标签: javascript arrays node.js


【解决方案1】:

另一个简单的解决方案如下:

let a = [1,2,'a',3,'b',4,5]

let b = [1,2,4]

console.log(b.every((i) => a.includes(i)))

希望对你有帮助

【讨论】:

    【解决方案2】:

    github lodash 图书馆找到这个。该函数使用内置函数来解决问题。 .includes().indexOf().every()

    var array1 = ['A', 'B', 'C', 'D', 'E'];
    var array2 = ['B', 'C', 'E'];
    var array3 = ['B', 'C', 'Z'];
    var array4 = [];
    
    function arrayContainsArray (superset, subset) {
      if (0 === subset.length) {
        return false;
      }
      return subset.every(function (value) {
        return (superset.includes(value));
      });
    }
    
     function arrayContainsArray1 (superset, subset) {
       if (0 === subset.length) {
         return false;
       }
       return subset.every(function (value) {
         return (superset.indexOf(value) >= 0);
       });
    }
    
    console.log(arrayContainsArray(array1,array2)); //true
    console.log(arrayContainsArray(array1,array3)); //false
    console.log(arrayContainsArray(array1,array4)); //false
    
    console.log(arrayContainsArray1(array1,array2)); //true
    console.log(arrayContainsArray1(array1,array3)); //false
    console.log(arrayContainsArray1(array1,array4)); //false

    【讨论】:

      【解决方案3】:

      这是我的解决方案:

      Array.prototype.containsIds = function (arr_ids) {
          var status = true;
          var current_arr = this;
          arr_ids.forEach(function(id) {
              if(!current_arr.includes(parseInt(id))){
                  status = false;
                  return false; // exit forEach
              }
          });
          return status;
      };
      
      // Examples
      [1,2,3].containsIds([1]); // true
      [1,2,3].containsIds([2,3]); // true
      [1,2,3].containsIds([3,4]); // false
      

      【讨论】:

        【解决方案4】:

        如果 arr2 是 arr1 的子集,则 Length of set(arr1 + arr2) == Length of set(arr1)

        var arr1 = [1, 'a', 2, 'b', 3];
        var arr2 = [1, 2, 3];
        
        Array.from(new Set(arr1)).length == Array.from(new Set(arr1.concat(arr2))).length
        

        【讨论】:

        【解决方案5】:

        至于另一种方法,你可以这样做;

        function checkIn(a,b){
          return b.every(function(e){
                           return e === this.splice(this.indexOf(e),1)[0];
                         }, a.slice()); // a.slice() is the "this" in the every method
        }
        
        var arr1  = [1, 2, 3, 4],
            arr2  = [1, 2],
            arr3  = [1,2,3,3];
        console.log(checkIn(arr1,arr2));
        console.log(checkIn(arr1,arr3));

        【讨论】:

          【解决方案6】:

          使用对象(阅读:哈希表)而不是排序应该将摊销复杂度降低到 O(m+n):

          function bagContains(arr1, arr2) {
              var o = {}
              var result = true;
          
              // Count all the objects in container
              for(var i=0; i < arr1.length; i++) {
                  if(!o[arr1[i]]) {
                      o[arr1[i]] = 0;
                  }
                  o[arr1[i]]++;
              }
          
              // Subtract all the objects in containee
              // And exit early if possible
              for(var i=0; i < arr2.length; i++) {
                  if(!o[arr2[i]]) {
                      o[arr2[i]] = 0;
                  }
                  if(--o[arr2[i]] < 0) {
                      result = false;
                      break;
                  }
              }
          
              return result;
          }
          
          console.log(bagContains([1, 2, 3, 4], [1, 3]));
          console.log(bagContains([1, 2, 3, 4], [1, 3, 3]));
          console.log(bagContains([1, 2, 3, 4], [1, 3, 7]));
          

          产生truefalsefalse

          【讨论】:

            【解决方案7】:

            一种选择是对两个数组进行排序,然后遍历这两个数组,比较元素。如果在超级包中没有找到子包候选中的元素,则前者不是子包。排序一般是 O(n*log(n)),比较是 O(max(s,t)),其中 st 是数组大小,对于总时间复杂度为 O(m*log(m)),其中 m=max(s,t)。

            function superbag(sup, sub) {
                sup.sort();
                sub.sort();
                var i, j;
                for (i=0,j=0; i<sup.length && j<sub.length;) {
                    if (sup[i] < sub[j]) {
                        ++i;
                    } else if (sup[i] == sub[j]) {
                        ++i; ++j;
                    } else {
                        // sub[j] not in sup, so sub not subbag
                        return false;
                    }
                }
                // make sure there are no elements left in sub
                return j == sub.length;
            }
            

            如果实际代码中的元素是整数,您可以使用特殊用途的整数排序算法(例如radix sort)来获得总体 O(max(s,t)) 时间复杂度,尽管如果袋子是小,内置的Array.sort 可能会比自定义整数排序运行得更快。

            时间复杂度可能较低的解决方案是创建袋子类型。整数袋特别容易。翻转袋子的现有数组:创建一个对象或数组,其中整数作为键,重复计数作为值。通过创建arrays are sparse in Javascript 使用数组不会浪费空间。您可以将包操作用于子包或超级包检查。例如,从 sub 候选中减去 super 并测试结果是否为非空。或者,contains 操作应该是 O(1)(或者可能是 O(log(n))),因此循环遍历子包候选并测试超级包的包含是否超过子包对每个子包的包含-bag 元素应该是 O(n) 或 O(n*log(n))。

            以下内容未经测试。 isInt 的实现留作练习。

            function IntBag(from) {
                if (from instanceof IntBag) {
                    return from.clone();
                } else if (from instanceof Array) {
                    for (var i=0; i < from.length) {
                        this.add(from[i]);
                    }
                } else if (from) {
                    for (p in from) {
                        /* don't test from.hasOwnProperty(p); all that matters
                           is that p and from[p] are ints
                         */
                        if (isInt(p) && isInt(from[p])) {
                            this.add(p, from[p]);
                        }
                    }
                }
            }
            IntBag.prototype=[];
            IntBag.prototype.size=0;
            IntBag.prototype.clone = function() {
                var clone = new IntBag();
                this.each(function(i, count) {
                    clone.add(i, count);
                });
                return clone;
            };
            IntBag.prototype.contains = function(i) {
                if (i in this) {
                    return this[i];
                }
                return 0;
            };
            IntBag.prototype.add = function(i, count) {
                if (!count) {
                    count = 1;
                }
                if (i in this) {
                    this[i] += count;
                } else {
                    this[i] = count;
                }
                this.size += count;
            };
            IntBag.prototype.remove = function(i, count) {
                if (! i in this) {
                    return;
                }
                if (!count) {
                    count = 1;
                }
                this[i] -= count;
                if (this[i] > 0) {
                    // element is still in bag
                    this.size -= count;
                } else {
                    // remove element entirely
                    this.size -= count + this[i];
                    delete this[i];
                }
            };
            IntBag.prototype.each = function(f) {
                var i;
                foreach (i in this) {
                    f(i, this[i]);
                }
            };
            IntBag.prototype.find = function(p) {
                var result = [];
                var i;
                foreach (i in this.elements) {
                    if (p(i, this[i])) {
                        return i;
                    }
                }
                return null;
            };
            IntBag.prototype.sub = function(other) {
                other.each(function(i, count) {
                    this.remove(i, count);
                });
                return this;
            };
            IntBag.prototype.union = function(other) {
                var union = this.clone();
                other.each(function(i, count) {
                    if (union.contains(i) < count) {
                        union.add(i, count - union.contains(i));
                    }
                });
                return union;
            };
            IntBag.prototype.intersect = function(other) {
                var intersection = new IntBag();
                this.each(function (i, count) {
                    if (other.contains(i)) {
                        intersection.add(i, Math.min(count, other.contains(i)));
                    }
                });
                return intersection;
            };
            IntBag.prototype.diff = function(other) {
                var mine = this.clone();
                mine.sub(other);
                var others = other.clone();
                others.sub(this);
                mine.union(others);
                return mine;
            };
            IntBag.prototype.subbag = function(super) {
                return this.size <= super.size
                   && null !== this.find(
                       function (i, count) {
                           return super.contains(i) < this.contains(i);
                       }));
            };
            

            如果您希望禁止元素重复,另请参阅“comparing javascript arrays”以获取一组对象的示例实现。

            【讨论】:

            • '留作练习' = '我不能被打扰' :)
            • @derekdreery:不要认为攻击我的自尊会让我回答我布置的作业;我对你的把戏很聪明;)
            【解决方案8】:

            还没有人发布递归函数,而且这些总是很有趣。称呼它为arr1.containsArray( arr2 )

            演示:http://jsfiddle.net/ThinkingStiff/X9jed/

            Array.prototype.containsArray = function ( array /*, index, last*/ ) {
            
                if( arguments[1] ) {
                    var index = arguments[1], last = arguments[2];
                } else {
                    var index = 0, last = 0; this.sort(); array.sort();
                };
            
                return index == array.length
                    || ( last = this.indexOf( array[index], last ) ) > -1
                    && this.containsArray( array, ++index, ++last );
            
            };
            

            【讨论】:

              【解决方案9】:

              你必须支持糟糕的浏览器吗?如果没有,every 函数应该让这变得简单。

              如果 arr1 是 arr2 的超集,则 arr2 中的每个成员都必须存在于 arr1 中

              var isSuperset = arr2.every(function(val) { return arr1.indexOf(val) >= 0; });
              

              这是fiddle

              编辑

              所以您要定义超集,以便对于 arr2 中的每个元素,它在 arr1 中出现的次数相同?我认为filter 将帮助您做到这一点(从前面的 MDN 链接中获取 shim 以支持旧版浏览器):

              var isSuperset = arr2.every(function (val) { 
                  var numIn1 = arr1.filter(function(el) { return el === val;  }).length;
                  var numIn2 = arr2.filter(function(el) { return el === val;  }).length;
                  return numIn1 === numIn2;   
              });
              

              Updated Fiddle

              结束编辑


              如果您确实想支持旧版浏览器,上面的 MDN 链接有一个您可以添加的 shim,为了您的方便,我在这里复制它:

              if (!Array.prototype.every)  
              {  
                Array.prototype.every = function(fun /*, thisp */)  
                {  
                  "use strict";  
              
                  if (this == null)  
                    throw new TypeError();  
              
                  var t = Object(this);  
                  var len = t.length >>> 0;  
                  if (typeof fun != "function")  
                    throw new TypeError();  
              
                  var thisp = arguments[1];  
                  for (var i = 0; i < len; i++)  
                  {  
                    if (i in t && !fun.call(thisp, t[i], i, t))  
                      return false;  
                  }  
              
                  return true;  
                };  
              }  
              

              编辑

              请注意,这将是一个 O(N2) 算法,因此请避免在大型数组上运行它。

              【讨论】:

              • @parapurarajkumar - 是的,是的。我将在我的答案警告 OP 中添加一个关于在大输入中使用它的编辑
              • 谢谢亚当,我稍微编辑了我的问题,我还需要检查多个相同成员。最后一个例子。谢谢
              • @amnotiam - 但为了清楚起见,我真的很想看看你是否有任何技巧可以更巧妙地解决这个问题;我不是在争取选票
              • @AdamRackis - 当然可以。 ;) 顺便说一句,如果您想从测试欺骗的解决方案中获得更多性能,您可以维护一个欺骗值表,以避免在第二次通过时重复相同的测试。如果我们只是处理小数组,可能不值得。
              • 我知道你不是在钓鱼……还是我?……;)
              【解决方案10】:

              如果b 的长度超过它不能是超集,则此处的快速解决方案采用两个数组,因此返回 false。然后循环遍历b 以查看 a 是否包含该元素。如果是这样,请将其从 a 中删除,如果不返回 false,则继续。更糟糕的情况是如果b 是一个子集,那么时间将是b.length

              function isSuper(a,b){
                var l=b.length,i=0,c;
                if(l>a.length){return false}
                else{
                  for(i;i<l;i++){
                    c=a.indexOf(b[i]);
                    if(c>-1){
                      a.splice(c,1);
                    }
                    else{return false}
                  }
                  return true;
                }
              }
              

              这假设输入并不总是按顺序排列,如果 a1,2,3 并且 b3,2,1 它仍然会返回 true。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-01-30
                相关资源
                最近更新 更多