在多个 JavaScript 数组之间查找匹配项

IT技术 javascript jquery arrays
2021-02-04 02:19:28

我有多个带有字符串值的数组,我想比较它们并且只保留所有数组之间相同的匹配结果

鉴于此示例代码:

var arr1 = ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2 = ['taco', 'fish', 'apple', 'pizza'];
var arr3 = ['banana', 'pizza', 'fish', 'apple'];

我想生成以下包含来自所有给定数组的匹配项的数组:

['apple', 'fish', 'pizza']

我知道我可以将所有数组组合在一起,var newArr = arr1.concat(arr2, arr3);但这只是给我一个包含所有内容的数组,再加上重复项。这是否可以轻松完成而无需 underscore.js 等库的开销?

(太好了,现在我也饿了!)

编辑我想我应该提到可能有未知数量的数组,我只是以 3 为例。

6个回答
var result = arrays.shift().filter(function(v) {
    return arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    });
});

演示: http : //jsfiddle.net/nWjcp/2/

您可以先对外部 Array 进行排序以在开始时获得最短的 Array ......

arrays.sort(function(a, b) {
    return a.length - b.length;
});

为了完整起见,这里有一个处理数组中重复项的解决方案。它使用.reduce()代替.filter()...

var result = arrays.shift().reduce(function(res, v) {
    if (res.indexOf(v) === -1 && arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    })) res.push(v);
    return res;
}, []);

演示: http : //jsfiddle.net/nWjcp/4/

@ChrisBarr:为了涵盖所有基础,我添加了一个处理重复项的相同风格的解决方案。它在底部。
2021-03-13 02:19:28
@Derek:: 这对我来说也是一个相对较新的发现。查看 MDN 文档。第二个论点真的很巧妙。此外,您不需要传递数字。它可以是将用作缩进字符的字符串。
2021-03-23 02:19:28
@amnotiam 是的,它们嵌套在另一个数组中。我想我需要变得更好并提供示例代码......哈!
2021-03-27 02:19:28
@TomB。:您的意思是1)保留未出现在任何其他数组中的数组中的项目,还是2)保留未出现在至少一个其他数组中的项目?因此,如果“pizza”在第一个和第二个数组中但不在第三个数组中,则不会根据解释1将其包括在内,而是根据2
2021-04-03 02:19:28
@amnotiam 那是规则,非常感谢!我真的需要更多地了解这些内置方法,这些方法很强大。
2021-04-08 02:19:28

假设有一个数组数组,我们想要找到它们的交集,一个最简单的单线性方法可能是

var arr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7]],
    int = arr.reduce((p,c) => p.filter(e => c.includes(e)));

document.write("<pre>" + JSON.stringify(int) + "</pre>");

完美 - 对于这样的事情,总是有一个很棒的 1 班轮。我唯一的建议是让更多的有意义的名字到PARAMS,因为我不知道是什么pce代表。
2021-03-20 02:19:28
有没有办法修复发生的异常,arr = []或者必须事先检查它?
2021-03-23 02:19:28
我不知道为什么这不是正确答案——我知道;4 年后给予...但它应该!发送@Redu!
2021-03-30 02:19:28
@Edmund Reed 谢谢...p先前和c当前的变量对于reduce没有初始值的操作是常规的efor element 是所有数组方法回调的一个非常常见的变量名。
2021-04-02 02:19:28
@Otto Abnormalverbraucher 正如我在回答中提到的,我们假设有一个数组数组,其基本情况为[[]]. 然而......足够公平的评论。当输入一个空数组时,异常提到,.reduce()这里不使用初始值开始。因此,事不宜迟,也许这样做arr.length ? arr.reduce((p,c) => p.filter(e => c.includes(e))) : [];就足够了。
2021-04-11 02:19:28

现在,您已经向问题添加了不确定数量的数组,这是另一种方法,将每个项目的计数收集到一个对象中,然后整理具有最大计数的项目。

这种方法的优点:

  1. 如果数组更大,则蛮力搜索选项(由其他答案使用)快 15 倍
  2. 不需要 ES5 或 ES5 垫片(适用于所有浏览器)
  3. 完全无损(根本不改变源数据)
  4. 处理源数组中的重复项
  5. 处理任意数量的输入数组

这是代码:

function containsAll(/* pass all arrays here */) {
    var output = [];
    var cntObj = {};
    var array, item, cnt;
    // for each array passed as an argument to the function
    for (var i = 0; i < arguments.length; i++) {
        array = arguments[i];
        // for each element in the array
        for (var j = 0; j < array.length; j++) {
            item = "-" + array[j];
            cnt = cntObj[item] || 0;
            // if cnt is exactly the number of previous arrays, 
            // then increment by one so we count only one per array
            if (cnt == i) {
                cntObj[item] = cnt + 1;
            }
        }
    }
    // now collect all results that are in all arrays
    for (item in cntObj) {
        if (cntObj.hasOwnProperty(item) && cntObj[item] === arguments.length) {
            output.push(item.substring(1));
        }
    }
    return(output);
}    

工作演示:http : //jsfiddle.net/jfriend00/52mAP/

仅供参考,这不需要 ES5,因此可以在没有垫片的所有浏览器中使用。

在对每 1000 个长度的 15 个数组进行的性能测试中,这比 am not i am 在这个 jsperf 中使用的搜索方法快 10 倍以上:http ://jsperf.com/in-all-arrays


这是一个使用 ES6MapSet进行重复数据删除跟踪计数的版本。这具有保留数据类型并且可以是任何类型的优点(它甚至不必进行自然的字符串转换,数据甚至可以是对象,尽管将对象比较为完全相同的对象,而不具有相同的对象)属性/值)。

var arrays = [
    ['valueOf', 'toString','apple', 'orange', 'banana', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza', 1, 2, 999, 888],
    ['valueOf', 'toString','taco', 'fish', 'fish', 'apple', 'pizza', 1, 999, 777, 999, 1],
    ['valueOf', 'toString','banana', 'pizza', 'fish', 'apple', 'apple', 1, 2, 999, 666, 555]
    ];
    
// subclass for updating cnts    
class MapCnt extends Map {
    constructor(iterable) {
        super(iterable);
    }
    
    cnt(iterable) {
        // make sure items from the array are unique
        let set = new Set(iterable);
        // now update the cnt for each item in the set
        for (let item of set) {
            let cnt = this.get(item) || 0;
            ++cnt;
            this.set(item, cnt);
        }
    }
}


function containsAll(...allArrays) {
    let cntObj = new MapCnt();
    for (array of allArrays) {
        cntObj.cnt(array);
    }
    // now see how many items have the full cnt
    let output = [];
    for (var [item, cnt] of cntObj.entries()) {
        if (cnt === allArrays.length) {
            output.push(item);
        }
    }
    return(output);
}    

var result = containsAll.apply(this, arrays);

document.body.innerHTML = "<pre>[<br>    " + result.join(',<br>    ') + "<br>]</pre>";

哦,这种方法的一个问题是,至少在 IE 8 及更低版本中,属性“toString”和“valueOf”始终不可枚举,因此如果有问题的数组将这些名称作为成员值,则上述内容永远不会将它们放入结果数组。一种解决方案是item显式测试这些值
2021-03-21 02:19:28
调整算法以处理重复并添加性能测试以显示它比其他一些方法快多少(快 14 倍)。
2021-03-22 02:19:28
@RobG -我修改了代码与工作"toString""valueOf"在IE8或任何其他内置的方法。为此,我为每个键添加了一个前缀,以将其与任何内置方法区分开来。
2021-03-22 02:19:28
——另一种方法是在一个普通对象上添加一个 Object.prototype 属性的测试,看看哪些是永远不可枚举的,然后在最后的 for..in 之后测试它们。
2021-03-28 02:19:28
+1 我喜欢这种containsAll方法,我在考虑基于对象的方法,但没有使用计数器。很好地处理重复项,而无需将它们从原始阵列中删除。我认为很多速度都来自避免使用像 splice 和 slice 这样的数组方法,对象属性查找可能是高度优化的,因为它对于任何非平凡的脚本来说都是非常重要的。
2021-04-08 02:19:28

一些想法 - 您可以只比较最短数组中的项目,并防止返回数组中的重复项。

function arraysInCommon(arrays){
    var i, common,
    L= arrays.length, min= Infinity;
    while(L){
        if(arrays[--L].length<min){
            min= arrays[L].length;
            i= L;
        }
    }
    common= arrays.splice(i, 1)[0];
    return common.filter(function(itm, indx){
        if(common.indexOf(itm)== indx){
            return arrays.every(function(arr){
                return arr.indexOf(itm)!= -1;
            });
        }
    });
}

var arr1= ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2= ['taco', 'fish', 'apple', 'pizza', 'apple','apple'];
var arr3= ['banana', 'pizza', 'fish', 'apple','fish'];

var allArrays = [arr1,arr2,arr3];

arraysInCommon(allArrays).sort();

返回值: apple,fish,pizza

演示- http://jsfiddle.net/kMcud/

假设数组数组并检查所有数组:

演示:http : //jsfiddle.net/qUQHW/

var tmp = {};
for (i = 0; i < data.length; i++) {
    for (j = 0; j < data[i].length; j++) {
        if (!tmp[data[i][j]]) {
            tmp[data[i][j]] = 0;
        }
        tmp[data[i][j]]++;
    }
}

var results = $.map(tmp, function(val,key) {
    return val == data.length ? key :null;
})