簡體   English   中英

檢查一個數組是否包含 JavaScript 中另一個數組的任何元素

[英]Check if an array contains any element of another array in JavaScript

我有一個目標數組["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。

香草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.
 */
var 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) );

希望,會有所幫助。

如果你不反對使用 libray, 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作為參數傳入,而不是在閉包中進行硬編碼。


更新2:

雖然我上面的解決方案可能有效並且(希望更多)可讀,但我相信處理我描述的概念的“更好”方法是做一些不同的事情。 上述解決方案的“問題”是循環內的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/

此解決方案的缺點是只能(正確)使用數字和字符串(和布爾值),因為這些值(隱式)轉換為字符串並設置為查找映射的鍵。 對於非文字值,這不是很好/可能/容易做到。

使用過濾器/ 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就返回true 我寫了 3 個解決方案只是為了展示 3 種不同的做事方式。 現在,這取決於你更喜歡什么。 您可以使用performance.now()來檢查一種解決方案或另一種解決方案的性能。 在我的解決方案中,我還檢查哪個數組最大,哪個數組最小,以提高操作效率。

第 3 種解決方案可能不是最可愛的,但很有效。 我決定添加它是因為在某些編碼面試中不允許使用內置方法。

最后,當然……我們可以想出一個帶有 2 個 NESTED for 循環(蠻力方式)的解決方案,但您想避免這種情況,因為時間復雜度很糟糕O(n^2)

筆記:

而不是使用.includes()像其他一些人一樣,你可以使用.indexOf() 如果你只是檢查值是否大於 0。如果值不存在會給你 -1。 如果它確實存在,它會給你大於 0。

indexOf() 與包含()

哪個性能更好? indexOf()有點,但在我看來包括更具可讀性。

如果我沒有記錯的話.includes()indexOf()在幕后使用循環,所以當你將它們與.some()一起使用時,你將處於O(n^2 .some()

使用循環

 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;
};

我的代碼: stackblitz

我不是表演專家,也不是 BigO,所以如果我說的有誤,請告訴我。

我發現這個簡短而甜蜜的語法可以匹配兩個數組之間的所有或部分元素。 例如

// 或操作。 查找 array1 中是否存在任何 array2 元素。 這將在第一次匹配時返回,因為當函數返回 TRUE 時某些方法會中斷

let array1 = ['a', 'b', 'c', 'd', 'e'], array2 = ['a', 'b'];

console.log(array2.some(ele => array1.includes(ele)));

// 打印真

// AND 運算。 查找 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));

還有一種解決方案

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'] }
// ]

我的解決方案應用了Array.prototype.some()Array.prototype.includes()數組助手,它們的工作效率也很高

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 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
}

由於集合不能有重復元素而數組可以,因此組合兩個輸入數組,將其轉換為集合,然后比較集合大小和數組長度會告訴您它們是否共享任何元素。

下划線

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

添加到數組原型

免責聲明:許多人強烈建議不要這樣做。 唯一真正有問題的時候是,如果一個庫添加了一個同名的原型函數(行為不同)或類似的東西。

代碼:

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

帶有對 .find() 的嵌套調用的數組 .filter() 將返回第一個數組中作為第二個數組成員的所有元素。 檢查返回數組的長度以確定第二個數組中是否有任何一個在第一個數組中。

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

具有部分匹配和不區分大小寫的 Vanilla 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 Grimshaw 的答案,使用includes indexOf替代以提高可讀性

讓發現 = arr1.some(r=> arr2.indexOf(r) >= 0)
讓找到 = arr1.some(r=> arr2.includes(r))

當我看了你的答案時,我找不到我想要的答案。 我自己做了一些事情,我想與你分享。

僅當輸入的單詞(數組)正確時才為真。

 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

我在 node 中使用下划線 js 提出了一個解決方案,如下所示:

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

就個人而言,我會使用以下功能:

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);
var target = ["apple","banana","orange"];
var checkArray = ["apple","banana","pineapple"];

var containsOneCommonItem = target.some(x => checkArray.some(y => y === x));`

["apple","grape"] //returns true;

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

["grape", "pineapple"] //returns false;

你可以做這樣的事情

let filteredArray = array.filter((elm) => {
   for (let i=0; i<anotherAray.length; i++) {
      return elm.includes(anotherArray[i])
    }
  })

良好的性能解決方案:

我們應該將數組之一轉換為對象。

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

您可能會面臨通過in 運算符檢查的一些缺點(例如 {} // => true 中的 'toString'),因此您可以將解決方案更改為 obj[key] 檢查器

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM