检查一个数组是否包含 JavaScript 中另一个数组的任何元素

Check if an array contains any element of another array in JavaScript

本文关键字:数组 JavaScript 另一个 元素 任何 包含 是否 一个 检查      更新时间:2023-09-26

我有一个目标数组["apple","banana","orange"],我想检查其他数组是否包含任何一个目标数组元素。

例如:

["apple","grape"] //returns true;
["apple","banana","pineapple"] //returns true;
["grape", "pineapple"] //returns false;

如何在 JavaScript 中做到这一点?

香草 JS

ES2016:

const found = arr1.some(r=> arr2.includes(r))

ES6:

const found = arr1.some(r=> arr2.indexOf(r) >= 0)

工作原理

some(..) 根据测试函数检查数组的每个元素,如果数组的任何元素通过测试函数,则返回 true,否则返回 false。 如果数组中存在给定的参数,则indexOf(..) >= 0includes(..)都返回 true。

vanilla js

/**
 * @description determine if an array contains one or more items from another array.
 * @param {array} haystack the array to search.
 * @param {array} arr the array providing items to check for in the haystack.
 * @return {boolean} true|false if haystack contains at least one item from arr.
 */
var findOne = function (haystack, arr) {
    return arr.some(function (v) {
        return haystack.indexOf(v) >= 0;
    });
};

如@loganfsmyth所述,您可以在ES2016中将其缩短为

/**
 * @description determine if an array contains one or more items from another array.
 * @param {array} haystack the array to search.
 * @param {array} arr the array providing items to check for in the haystack.
 * @return {boolean} true|false if haystack contains at least one item from arr.
 */
const findOne = (haystack, arr) => {
    return arr.some(v => haystack.includes(v));
};

或者干脆arr.some(v => haystack.includes(v));

如果要确定数组是否包含其他数组中的所有项,请将some()替换为every()或者arr.every(v => haystack.includes(v));

ES6 解决方案:

let arr1 = [1, 2, 3];
let arr2 = [2, 3];
let isFounded = arr1.some( ai => arr2.includes(ai) );

与它不同的是:必须包含所有值。

let allFounded = arr2.every( ai => arr1.includes(ai) );

如果您不反对使用库,http://underscorejs.org/有一个交集方法,可以简化此操作:

var _ = require('underscore');
var target = [ 'apple', 'orange', 'banana'];
var fruit2 = [ 'apple', 'orange', 'mango'];
var fruit3 = [ 'mango', 'lemon', 'pineapple'];
var fruit4 = [ 'orange', 'lemon', 'grapes'];
console.log(_.intersection(target, fruit2)); //returns [apple, orange]
console.log(_.intersection(target, fruit3)); //returns []
console.log(_.intersection(target, fruit4)); //returns [orange]

交集函数将返回一个新数组,其中包含它匹配的项目,如果不匹配,则返回空数组。

ES6 (最快(

const a = ['a', 'b', 'c'];
const b = ['c', 'a', 'd'];
a.some(v=> b.indexOf(v) !== -1)

ES2016

const a = ['a', 'b', 'c'];
const b = ['c', 'a', 'd'];
a.some(v => b.includes(v));

强调

const a = ['a', 'b', 'c'];
const b = ['c', 'a', 'd'];
_.intersection(a, b)

演示:https://jsfiddle.net/r257wuv5/

jsPerf: https://jsperf.com/array-contains-any-element-of-another-array

如果你不需要类型强制(因为使用了indexOf(,你可以尝试如下方法:

var arr = [1, 2, 3];
var check = [3, 4];
var found = false;
for (var i = 0; i < check.length; i++) {
    if (arr.indexOf(check[i]) > -1) {
        found = true;
        break;
    }
}
console.log(found);

其中arr包含目标项。最后,found将显示第二个数组是否至少与目标匹配。

当然,你可以用任何你想使用的数字来交换数字 - 字符串很好,就像你的例子一样。

在我的具体示例中,结果应该是true的,因为第二个数组的3存在于目标中。


更新:

以下是我如何将其组织成一个函数(与以前相比有一些小的更改(:

var anyMatchInArray = (function () {
    "use strict";
    var targetArray, func;
    targetArray = ["apple", "banana", "orange"];
    func = function (checkerArray) {
        var found = false;
        for (var i = 0, j = checkerArray.length; !found && i < j; i++) {
            if (targetArray.indexOf(checkerArray[i]) > -1) {
                found = true;
            }
        }
        return found;
    };
    return func;
}());

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

在这种情况下,可以修改函数,使其targetArray作为参数传入,而不是在闭包中进行硬编码。


UPDATE2:

虽然我上面的解决方案可能有效并且(希望更多(可读性,但我相信处理我所描述的概念的"更好"方法是做一些不同的事情。上述解决方案的"问题"是循环内部的indexOf导致目标数组完全循环用于其他数组中的每个项目。这可以通过使用"查找"(地图...一个 JavaScript 对象文字(。这允许在每个数组上进行两个简单的循环。下面是一个示例:

var anyMatchInArray = function (target, toMatch) {
    "use strict";
    var found, targetMap, i, j, cur;
    found = false;
    targetMap = {};
    // Put all values in the `target` array into a map, where
    //  the keys are the values from the array
    for (i = 0, j = target.length; i < j; i++) {
        cur = target[i];
        targetMap[cur] = true;
    }
    // Loop over all items in the `toMatch` array and see if any of
    //  their values are in the map from before
    for (i = 0, j = toMatch.length; !found && (i < j); i++) {
        cur = toMatch[i];
        found = !!targetMap[cur];
        // If found, `targetMap[cur]` will return true, otherwise it
        //  will return `undefined`...that's what the `!!` is for
    }
    return found;
};

演示:http://jsfiddle.net/5Lv9v/

此解决方案的缺点是只能(正确(使用数字和字符串(以及布尔值(,因为这些值(隐式(转换为字符串并设置为查找映射的键。对于非文字值,这并不完全好/可能/容易做到。

Using filter/indexOf:

function containsAny(source,target)
{
    var result = source.filter(function(item){ return target.indexOf(item) > -1});   
    return (result.length > 0);  
}    
//results
var fruits = ["apple","banana","orange"];
console.log(containsAny(fruits,["apple","grape"]));
console.log(containsAny(fruits,["apple","banana","pineapple"]));
console.log(containsAny(fruits,["grape", "pineapple"]));

您可以使用 lodash 并执行以下操作:

_.intersection(originalTarget, arrayToCheck).length > 0

设置交集在两个集合上完成,生成相同元素的数组。

const areCommonElements = (arr1, arr2) => {
    const arr2Set = new Set(arr2);
    return arr1.some(el => arr2Set.has(el));
};

或者,如果您首先找出这两个数组中的哪一个更长,并Set最长的数组,同时对最短的数组应用some方法,您甚至可以获得更好的性能:

const areCommonElements = (arr1, arr2) => {
    const [shortArr, longArr] = (arr1.length < arr2.length) ? [arr1, arr2] : [arr2, arr1];
    const longArrSet = new Set(longArr);
    return shortArr.some(el => longArrSet.has(el));
};

我写了 3 个解决方案。从本质上讲,他们做同样的事情。他们一true就返回真.我写了 3 个解决方案只是为了展示 3 种不同的做事方式。现在,这取决于你更喜欢什么。您可以使用 performance.now(( 来检查一个解决方案或另一个解决方案的性能。在我的解决方案中,我还检查哪个阵列最大,哪个阵列最小,以提高操作效率。

第三种解决方案可能不是最可爱的,但很有效。我决定添加它,因为在某些编码面试中,您不允许使用内置方法。

最后,当然...我们可以想出一个带有 2 个 NESTED for 循环的解决方案(蛮力方式(,但你想避免这种情况,因为时间复杂度是坏的 O(n^2(。

注意:

而不是像其他人那样使用.includes(),您可以使用 .indexOf() .如果您这样做,只需检查该值是否大于 0。如果 值不存在会给你 -1。如果它确实存在,它会给你 大于 0。

indexOf(( vs include((

哪一个性能更好? indexOf()一点,但在我看来,包含更具可读性。

如果我没记错的话.includes()并且indexOf()幕后使用循环,那么将它们与.some()一起使用时,您将处于 O(n^2( 处。

使用循环

 const compareArraysWithIncludes = (arr1, arr2) => {
     const [smallArray, bigArray] =
        arr1.length < arr2.length ? [arr1, arr2] : [arr2, arr1];
     for (let i = 0; i < smallArray.length; i++) {
       return bigArray.includes(smallArray[i]);
     }
      return false;
    };

使用 .some((

const compareArraysWithSome = (arr1, arr2) => {
  const [smallArray, bigArray] =
    arr1.length < arr2.length ? [arr1, arr2] : [arr2, arr1];
  return smallArray.some(c => bigArray.includes(c));
};

使用地图 时间复杂度 O(2n(=>O(n(

const compararArraysUsingObjs = (arr1, arr2) => {
  const map = {};
  const [smallArray, bigArray] =
    arr1.length < arr2.length ? [arr1, arr2] : [arr2, arr1];
  for (let i = 0; i < smallArray.length; i++) {
    if (!map[smallArray[i]]) {
      map[smallArray[i]] = true;
    }
  }
  for (let i = 0; i < bigArray.length; i++) {
    if (map[bigArray[i]]) {
      return true;
    }
  }
  return false;
};

我的代码:斯塔克闪电战

我不是性能专家,也不是BigO,所以如果我说错了,请告诉我。

我发现这个简短而甜美的语法可以匹配两个数组之间的所有或部分元素。例如

查找 array1 中是否存在任何 array2 元素。一旦有第一个匹配项,这将返回,因为当函数返回 TRUE 时某些方法会中断

let array1 = ['a', 'b', 'c', 'd', 'e'], array2 = ['a', 'b'];
console.log(array2.some(ele => array1.includes(ele)));

打印真

查找 array1 中是否存在所有 array2 元素。一旦没有第一个匹配项,这将返回,因为当函数返回 TRUE 时某些方法会中断

let array1 = ['a', 'b', 'c', 'd', 'e'], array2 = ['a', 'x'];
console.log(!array2.some(ele => !array1.includes(ele)));

打印假

希望将来对某人有所帮助!

您可以使用嵌套的 Array.prototype.some 调用。这样做的好处是,它将在第一场比赛中保释,而不是通过整个嵌套循环运行的其他解决方案。

例如。

var arr = [1, 2, 3];
var match = [2, 4];
var hasMatch = arr.some(a => match.some(m => a === m));
<</div> div class="answers">

再多一个解决方案

var a1 = [1, 2, 3, 4, 5]
var a2 = [2, 4]

检查 a1 是否包含 a2 的所有元素

var result = a1.filter(e => a2.indexOf(e) !== -1).length === a2.length
console.log(result)

使用 some/findIndex 和 indexOf 的组合怎么样?

所以像这样:

var array1 = ["apple","banana","orange"];
var array2 = ["grape", "pineapple"];
var found = array1.some(function(v) { return array2.indexOf(v) != -1; });

为了使其更具可读性,您可以将此功能添加到 Array 对象本身。

Array.prototype.indexOfAny = function (array) {
    return this.findIndex(function(v) { return array.indexOf(v) != -1; });
}
Array.prototype.containsAny = function (array) {
    return this.indexOfAny(array) != -1;
}

注意:如果你想用谓词做一些事情,你可以用另一个findIndex和一个谓词替换内部indexOf

这是一个有趣的案例,我认为我应该分享。

假设您有一个对象数组和一个选定筛选器的数组。

let arr = [
  { id: 'x', tags: ['foo'] },
  { id: 'y', tags: ['foo', 'bar'] },
  { id: 'z', tags: ['baz'] }
];
const filters = ['foo'];

要将选定的过滤器应用于此结构,我们可以

if (filters.length > 0)
  arr = arr.filter(obj =>
    obj.tags.some(tag => filters.includes(tag))
  );
// [
//   { id: 'x', tags: ['foo'] },
//   { id: 'y', tags: ['foo', 'bar'] }
// ]

写这个的简短方法: const found = arr1.some(arr2.includes)

良好的性能解决方案:

我们应该将数组中的一个转换为对象。

const contains = (arr1, mainObj) => arr1.some(el => el in mainObj);
const includes = (arr1, mainObj) => arr1.every(el => el in mainObj);

用法:

const mainList = ["apple", "banana", "orange"];
// We make object from array, you can use your solution to make it
const main = Object.fromEntries(mainList.map(key => [key, true]));
contains(["apple","grape"], main) // => true
contains(["apple","banana","pineapple"], main) // =>  true
contains(["grape", "pineapple"], main) // =>  false
includes(["apple", "grape"], main) // => false
includes(["banana", "apple"], main) // =>  true

您可能会遇到一些通过运算符检查的缺点(例如 {}//=> true 中的"toString"(,因此您可以将解决方案更改为 obj[key] 检查器

嵌套

调用 .find() 的数组.filter()将返回第一个数组中属于第二个数组的所有元素。检查返回的数组的长度,以确定第一个数组中是否有第二个数组。

getCommonItems(firstArray, secondArray) {
  return firstArray.filter((firstArrayItem) => {
    return secondArray.find((secondArrayItem) => {
      return firstArrayItem === secondArrayItem;
    });
  });
}

添加到数组原型

免责声明:许多人强烈建议不要这样做。唯一真正有问题的时候是,如果一个库添加了一个同名(行为不同(的原型函数或类似的东西。

法典:

Array.prototype.containsAny = function(arr) {
    return this.some(
        (v) => (arr.indexOf(v) >= 0)
    )
}

不使用大箭头函数:

Array.prototype.containsAny = function(arr) {
    return this.some(function (v) {
        return arr.indexOf(v) >= 0
    })
}

用法

var a = ["a","b"]
console.log(a.containsAny(["b","z"]))    // Outputs true
console.log(a.containsAny(["z"]))    // Outputs false

我的解决方案应用了Array.prototype.some((和Array.prototype.include((数组助手,它们的工作也非常高效

ES6

const originalFruits = ["apple","banana","orange"];
const fruits1 = ["apple","banana","pineapple"];
const fruits2 = ["grape", "pineapple"];
const commonFruits = (myFruitsArr, otherFruitsArr) => {
  return myFruitsArr.some(fruit => otherFruitsArr.includes(fruit))
}
console.log(commonFruits(originalFruits, fruits1)) //returns true;
console.log(commonFruits(originalFruits, fruits2)) //returns false;

当我看着你的答案时,我找不到我想要的答案。我自己做了一些事情,我想和你分享。

仅当输入的单词(数组(正确时,它才会为真。

function contains(a,b) {
    let counter = 0;
    for(var i = 0; i < b.length; i++) {;
        if(a.includes(b[i])) counter++;
    }
    if(counter === b.length) return true;
    return false;
}
let main_array = ['foo','bar','baz'];
let sub_array_a = ['foo','foobar'];
let sub_array_b = ['foo','bar'];
console.log(contains(main_array, sub_array_a)); // returns false
console.log(contains(main_array,sub_array_b )); // returns true

可以通过简单地遍历主数组并检查其他数组是否包含任何目标元素来完成。

试试这个:

function Check(A) {
    var myarr = ["apple", "banana", "orange"];
    var i, j;
    var totalmatches = 0;
    for (i = 0; i < myarr.length; i++) {
        for (j = 0; j < A.length; ++j) {
            if (myarr[i] == A[j]) {
                totalmatches++;
            }
        }
    }
    if (totalmatches > 0) {
        return true;
    } else {
        return false;
    }
}
var fruits1 = new Array("apple", "grape");
alert(Check(fruits1));
var fruits2 = new Array("apple", "banana", "pineapple");
alert(Check(fruits2));
var fruits3 = new Array("grape", "pineapple");
alert(Check(fruits3));

JSFIDDLE 的演示

不确定这在性能方面有多高效,但这就是我使用数组解构来保持一切美好和简短的方法:

const shareElements = (arr1, arr2) => {
  const typeArr = [...arr1, ...arr2]
  const typeSet = new Set(typeArr)
  return typeArr.length > typeSet.size
}

由于集合不能有重复的元素,而数组可以,因此组合两个输入数组,将其转换为集合,并比较集合大小和数组长度将告诉您它们是否共享任何元素。

带下划线js

var a1 = [1,2,3];
var a2 = [1,2];
_.every(a1, function(e){ return _.include(a2, e); } ); //=> false
_.every(a2, function(e){ return _.include(a1, e); } ); //=> true

具有部分匹配和不区分大小写的香草 JS

以前的一些方法的问题在于它们需要每个单词的精确匹配。但是,如果您想提供部分匹配的结果怎么办?

function search(arrayToSearch, wordsToSearch) {
    arrayToSearch.filter(v => 
        wordsToSearch.every(w => 
            v.toLowerCase().split(" ").
                reduce((isIn, h) => isIn || String(h).indexOf(w) >= 0, false)
            )
        )
}
//Usage
var myArray = ["Attach tag", "Attaching tags", "Blah blah blah"];
var searchText = "Tag attach";
var searchArr = searchText.toLowerCase().split(" "); //["tag", "attach"]
var matches = search(myArray, searchArr);
//Will return
//["Attach tag", "Attaching tags"]

当您想要提供一个搜索框时,这很有用,用户可以在其中键入单词,并且结果可以以任何顺序、位置和大小写显示这些单词。

更新@Paul格里姆肖答案,使用includes indexOf的本能以提高可读性

let found = arr1.some(r=> arr2.indexOf(r(>= 0(
let Found = arr1.some(r=> arr2.include(r((

我在节点中使用下划线 js 提出了一个解决方案,如下所示:

var checkRole = _.intersection(['A','B'], ['A','B','C']);
if(!_.isEmpty(checkRole)) { 
     next();
}

您正在寻找两个数组之间的交集。您有两种主要的交叉点类型:"每个"和"一些"。让我给你一个很好的例子:

let brands1 = ['Ford', 'Kia', 'VW', 'Audi'];
let brands2 = ['Audi', 'Kia'];
// Find 'every' brand intersection. 
// Meaning all elements inside 'brands2' must be present in 'brands1':
let intersectionEvery = brands2.every( brand => brands1.includes(brand) );
if (intersectionEvery) {
    const differenceList = brands1.filter(brand => !brands2.includes(brand));
    console.log('difference list:', differenceList);
    const commonList = brands1.filter(brand => brands2.includes(brand));
    console.log('common list:', commonList);
}

如果不满足条件(例如,如果您将"梅赛德斯"放在品牌2中(,则"intersectionEvery"将不会得到满足 - 将是布尔值错误。

如果满足条件,它将记录["福特","大众"]作为差异,["起亚","奥迪"]作为通用列表。

沙盒:https://jsfiddle.net/bqmg14t6/

一些

let brands1 = ['Ford', 'Kia', 'VW', 'Audi'];
let brands2 = ['Audi', 'Kia', 'Mercedes', 'Land Rover'];
// Find 'some' brand intersection. 
// Meaning some elements inside 'brands2' must be also present in 'brands1':
let intersectionSome = brands2.some( brand => brands1.includes(brand) );
if (intersectionSome) {
    const differenceList = brands1.filter(brand => !brands2.includes(brand));
    console.log('difference list:', differenceList);
    const commonList = brands1.filter(brand => brands2.includes(brand));
    console.log('common list:', commonList);
}

在这里,我们正在寻找一些常见的品牌,不一定是全部。

它将["福特","大众"]记录为差异,["起亚","奥迪"]作为常见品牌。

沙盒:https://jsfiddle.net/zkq9j3Lh/

就个人而言,我会使用以下函数:

var arrayContains = function(array, toMatch) {
    var arrayAsString = array.toString();
    return (arrayAsString.indexOf(','+toMatch+',') >-1);
}

"toString((" 方法将始终使用逗号来分隔值。只会真正适用于基元类型。

console.log("searching Array: "+finding_array);
console.log("searching in:"+reference_array);
var check_match_counter = 0;
for (var j = finding_array.length - 1; j >= 0; j--) 
{
    if(reference_array.indexOf(finding_array[j]) > 0)
    {
        check_match_counter = check_match_counter + 1;
    }
}
 var match = (check_match_counter > 0) ? true : false;
console.log("Final result:"+match);