Javascript 在任意数量的数组之间查找公共项的最有效方法

Javascript 在任意数量的数组之间查找公共项的最有效方法,javascript,arrays,optimization,unique,Javascript,Arrays,Optimization,Unique,我需要能够在任意数量的数组之间找到一个公共项。例如,假设有这样一个对象: var obj = { a: [ 15, 23, 36, 49, 104, 211 ], b: [ 9, 12, 23 ], c: [ 11, 17, 18, 23, 38 ], d: [ 13, 21, 23, 27, 40, 85] }; 我需要确定每个数组之间的公共项。(在本例中为23) 我的解决方案是找到最短的数组,并遍历其中的每个项,检查其他数组的索引 var shortest = {}; va

我需要能够在任意数量的数组之间找到一个公共项。例如,假设有这样一个对象:

var obj = {
  a: [ 15, 23, 36, 49, 104, 211 ],
  b: [ 9, 12, 23 ],
  c: [ 11, 17, 18, 23, 38 ],
  d: [ 13, 21, 23, 27, 40, 85]
};
我需要确定每个数组之间的公共项。(在本例中为23)

我的解决方案是找到最短的数组,并遍历其中的每个项,检查其他数组的索引

var shortest = {};
var keys = [];
for ( var key in obj ) {

  if ( obj.hasOwnProperty( key ) && Array.isArray( obj[ key ] ) ) {
    keys.push( key );

    if ( !shortest.hasOwnProperty( 'length' ) || obj[ key ].length < shortest.length ) {
      shortest.name = key;
      shortest.length = obj[ key ].length;
    }
  }
}



var res = obj[ shortest.name ].filter(function ( v ) {

  for ( var i = 0; i < keys.length; i++ ) {

    if ( obj[ keys[ i ] ].indexOf( v ) === -1 ) {
      return false;
    }

    return true;
  }
};
var-shortest={};
var键=[];
for(obj中的var键){
if(obj.hasOwnProperty(key)和&Array.isArray(obj[key])){
按键。按(键);
if(!shortest.hasOwnProperty('length')| | obj[key].length

然而,这似乎效率极低,我正在尝试确定是否有更好的方法,最好不必多次循环。

我认为不可能在
O(N)之内完成这项工作
,其中
N
是所有数组中的项目数。您当前的解决方案效率较低,因为
indexOf
对于每个数组是
O(N)
,您可以在最短的数组中针对每个项目运行所有这些解决方案

我认为基于地图的选项应该是
O(N)

var计数={};
var keys=Object.keys(obj);
var-arr,el;
对于(var k=0;k
这里有一些警告:

  • 这假设元素可以用作对象键(即,它们可以唯一地转换为字符串),并且在不同的数组中没有像
    1
    “1”
    这样令人讨厌的边缘情况

  • 这假定每个数组的数组值都是唯一的

  • 这假设交叉点中只有一个元素

  • function common(o) {
      // Map each of the object key arrays to a set.
      return Object.keys(o).map(function(k) {
        return o[k].reduce(function(a, e) {
          a[e] = 1;
          return a;
        }, {});
      }).reduce(function(a, e) {
        // Perform a set union.
        Object.keys(e).forEach(function(k) {
          if (!a[k]) {
            delete e[k];
          }
        });
        return e;
      })
    }
    
    var obj = {
      a: [ 15, 23, 36, 49, 104, 211 ],
      b: [ 9, 12, 23 ],
      c: [ 11, 17, 18, 23, 38 ],
      d: [ 13, 21, 23, 27, 40, 85]
    };
    
    common(obj);
    

另一个
O(n)
集合并集解决方案,功能性更强。作为对象键的元素警告仍然适用,尽管这将返回交集中所有共享元素的集合(数组)

function common(o) {
  // Map each of the object key arrays to a set.
  return Object.keys(o).map(function(k) {
    return o[k].reduce(function(a, e) {
      a[e] = 1;
      return a;
    }, {});
  }).reduce(function(a, e) {
    // Perform a set union.
    Object.keys(e).forEach(function(k) {
      if (!a[k]) {
        delete e[k];
      }
    });
    return e;
  })
}

var obj = {
  a: [ 15, 23, 36, 49, 104, 211 ],
  b: [ 9, 12, 23 ],
  c: [ 11, 17, 18, 23, 38 ],
  d: [ 13, 21, 23, 27, 40, 85]
};

common(obj);

我将通过发明
Array.prototype.intersect()
来完成这项工作。当数组中存在重复项时,它不会失败。如果在每个数组中复制相同的项,则会在交叉点中得到它应该得到的副本。让我们看看它将如何工作

Array.prototype.intersect=函数(…a){
返回[this,…a].reduce((p,c)=>p.filter(e=>c.includes(e));
};
var obj={
答:[15、23、36、49、104、211],
b:[9,12,23],
c:[11,17,18,23,38],
d:[13,21,23,27,40,85]
},
arrs=Object.keys(obj.map)(e=>obj[e]);

console.log(JSON.stringify(arrs.pop().intersect(…arrs));//取出最后一个来调用剩余的方法
只是ES5中的另一个解决方案,在O(n)中有一个临时对象

var obj={a:[15,23,36,49,104,211],b:[9,12,23,15],c:[11,17,18,23,38],d:[13,21,23,27,40,85]},
结果=对象。键(obj)。减少(函数(o){
返回函数(r,k,i){
var t=o;
o=Object.create(空);
返回obj[k]。过滤器(函数(a){
返回(!i|t[a])&&(o[a]=true);
});
};
}(Object.create(null)),[]);

console.log(结果)
所以所有的任意数组都在您的示例代码中进行了排序。这只是偶然还是事实?@Quirk happenstance。我相信数据源会返回sorted,但我不敢指望它。如果所有数组都进行了排序,那么最坏情况下的运行时可能是它们大小之和的线性,即
O(n1+n2+…)
请参阅检查您不能在小于
O(N)
的时间内完成此操作。您必须至少读取所有值,并且这已经是
O(N)
。最坏的情况总是
O(N)
,但如果知道交集中只有一个元素,则不必读取常见情况下的所有值。这似乎正是我所需要的。如果每个数组项都不唯一,则此解决方案将失败。如果数组中存在重复项,则会导致错误计数,从而欺骗算法。@Redu-true,添加到ca中Veats这看起来不像O(n)。当您将Object.keys映射到对象中,然后执行更多reduces和forEach操作时,您的O(n)时间就完成了。在我看来像O(2n)。此外,此算法对于重复条目也会失败,例如,如果每个数组中有两个或多个条目为23,则只会找到其中一个。是的,这确实设置了交叉点。如果要计数倍数(即行李),可以在累加器中保留计数(将
a[e]=1
替换为
a[e]+
,根据需要初始化)取
min(a[k],e[k])
on
reduce
O(n)
简单地表示线性渐近增长。使用这种表示法,
O(2n)=O(n)
,因为两者都是线性的。有关更多信息,请参阅。这是在O(n)中执行此任务的唯一算法在给出的答案中。我想我必须修改我的
数组.prototype.intersect()
,以利用过滤器回调作为你的方法,即使
includes()
看起来很酷。你可能应该重新考虑你与Edge的浪漫关系。我不知道Edge是如何申请拉取请求的