如何检索所有数组中至少一次出现的所有对象?

时间:2018-08-23 16:51:52

标签: javascript

我有多个对象数组:

var lists = [];

lists.push([{label: 'a'}, {label: 'b'}, {label: 'c'}]);
lists.push([{label: 'b'}, {label: 'a'}, {label: 'e'}]);
lists.push([{label: 'a'}, {label: 'e'}, {label: 'b'}]);

我想检索所有列表中至少出现一次的所有对象,在这种情况下:{label: 'a'}{label: 'b'}

这是我的代码:

var lists = [];

lists.push([{label: 'a'}, {label: 'b'}, {label: 'c'}]);
lists.push([{label: 'b'}, {label: 'a'}, {label: 'e'}]);
lists.push([{label: 'a'}, {label: 'e'}, {label: 'b'}]);


var tmp = [];
for(var i = 0; i < lists.length; i++){
  var list = lists[i];
  for(var j = 0; j < list.length; j++){
    var obj = list[j];
    if( !tmp[obj.label] ){
        obj.inList = [];
        tmp[obj.label] = obj;
    } 
    tmp[obj.label].inList[i] = true;
  }
}

var result = [];
for(var key in tmp){
    result.push(tmp[key]);
}

var result = result.filter(function(obj){
  return Object.keys(obj.inList).length === lists.length;
});

// expected result [{label: 'a'}, {label: 'b'}]
console.log(result);

我的代码可以运行,但是在大型数组下性能较差。有最快的方法吗?

4 个答案:

答案 0 :(得分:2)

您可以遍历所有数组一次并创建一个映射。将每个标签映射到一个集合,其中包含该标签所在的所有子数组的索引。如果set的大小是父数组大小的=(在您的情况下为lists),则意味着标签出现在所有子数组中,因为set不会包含重复的条目,并且size等于lists.length,这意味着标签存在于所有子数组中(可以肯定的是,我们将每个子数组的索引添加到集合中)。请尝试以下操作:

var lists = [];

lists.push([{label: 'a'}, {label: 'b'}, {label: 'c'}]);
lists.push([{label: 'b'}, {label: 'a'}, {label: 'e'}]);
lists.push([{label: 'a'}, {label: 'e'}, {label: 'b'}]);

let map = {};

lists.forEach((list, index)=>{
  list.forEach((label)=>{
    map[label.label]  = map[label.label] || new Set();
    map[label.label].add(index);
  });
});

let result = [];
Object.keys(map).forEach((key)=>{
  if(map[key].size == lists.length)
    result.push({label : key});
});
console.log(result);

答案 1 :(得分:2)

一种方法是遍历每个列表,并在每次看到对象时为每个标签增加一个计数器。然后,一旦处理完所有列表,所有计数等于列表数量的标签都必须出现在所有列表中。

例如:

function OmnipresentObjectTracker(key) {
  var arrayCount = 0
  var presentCounts = {}
  var objectsByKeyname = {}

  this.add = function(arr) {
    arrayCount++
    var alreadyCounted = {}
    arr.forEach(function(obj) {
      if (key in obj) {
        const value = obj[key]
        objectsByKeyname[value] = obj
        if (!alreadyCounted[value]) {
          presentCounts[value] = (presentCounts[value]||0) + 1
          alreadyCounted[value] = true;
        }
      }
    })
  }

  this.getObjects = function() {
    return Object.keys(presentCounts).reduce(function(memo, k) {
      if (presentCounts[k] === arrayCount) {
        memo.push(objectsByKeyname[k])
      }
      return memo
    }, [])
  }
}

var oot = new OmnipresentObjectTracker('label')

oot.add([{label: 'a'}, {label: 'b'}, {label: 'c'}]);
oot.getObjects() // => [{label: "a"}, {label: "b"}, {label: "c"}]

oot.add([{label: 'b'}, {label: 'a'}, {label: 'e'}]);
oot.add([{label: 'a'}, {label: 'e'}, {label: 'b'}]);
oot.getObjects() // => [{label: "a"}, {label: "b"}]

oot.add([{label: 'a'}, {label: 'a'}])
oot.getObjects() // => [{label: "a"}]

答案 2 :(得分:1)

您可以使用array#reduce将所有唯一键及其计数添加到对象累加器中。然后使用array#filter筛选出所有等于您的数组长度的键,然后重新生成对象数组。

var lists = [];
lists.push([{label: 'a'}, {label: 'b'}, {label: 'c'}]);
lists.push([{label: 'b'}, {label: 'a'}, {label: 'e'}]);
lists.push([{label: 'a'}, {label: 'e'}, {label: 'b'}]);

var common = lists.reduce((r, a) => {
  var unique = [...new Set(a.map(({label}) => label))];
  unique.forEach(v => {
    v in r ? r[v]++: r[v] = 1;
  });
  return r;
},{});

var result = Object.keys(common).filter(k => common[k] === lists.length).map(label => ({label}));
console.log(result);

答案 3 :(得分:0)

我要稍微修改一下并丢弃每个数组中未找到的元素。这样,每个数组的迭代次数都会减少。像这样:

var lists = [];

lists.push([{label: 'a'}, {label: 'b'}, {label: 'c'}]);
lists.push([{label: 'b'}, {label: 'a'}, {label: 'e'}]);
lists.push([{label: 'a'}, {label: 'e'}, {label: 'b'}]);

var final = [];

// Add every element from the first list
for(var e in lists[0]) {
    final.push(lists[0][e]);
}

// Iterate every list item in the main array
for(var list in lists) {
    // Iterate only the remaining elements in the final array
    for(var f in final) {
        var exists = false; // Sentinel that checks if the element exists
        
        for(var e in lists[list]) {
            if(final[f].label == lists[list][e].label) {
                exists = true; // Change the flag if the element is found
            }
        }
        
        // If the element was not found, remove the element from the final array
        if(!exists) {
            final.splice(f,1);
        }
    }
}

console.log(final);

/*
[
  {
    "label": "a"
  },
  {
    "label": "b"
  }
]
*/