ターゲット配列["apple","banana","orange"]
があり、他の配列にターゲット配列要素のいずれかが含まれているかどうかを確認したい。
例えば:
["apple","grape"] //returns true;
["apple","banana","pineapple"] //returns true;
["grape", "pineapple"] //returns false;
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 を返します。指定された引数が配列に存在する場合、両方とも true を返しますindexOf(..) >= 0
。includes(..)
/**
* @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()
toevery()
または asに置き換えます。arr.every(v => haystack.includes(v));
型強制が必要ない場合 ( を使用するため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
、2 番目の配列にターゲットとの一致が少なくとも 1 つあったかどうかが表示されます。
もちろん、使用したいものと数値を入れ替えることができます - あなたの例のように、文字列は問題ありません。
そして私の特定の例では、結果はtrue
2番目の配列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 オブジェクト リテラル) を使用して簡単に「修正」できます。これにより、各配列に対して 2 つの単純なループが可能になります。次に例を示します。
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/
このソリューションの欠点は、値が (暗黙的に) 文字列に変換され、ルックアップ マップのキーとして設定されるため、数値と文字列 (およびブール値) のみを (正しく) 使用できることです。これは、非リテラル値に対して正確に/可能/簡単に実行できるわけではありません。
lodashを使用して、次のことを行うことができます。
_.intersection(originalTarget, arrayToCheck).length > 0
セット交差は両方のコレクションで行われ、同一の要素の配列が生成されます。
const areCommonElements = (arr1, arr2) => {
const arr2Set = new Set(arr2);
return arr1.some(el => arr2Set.has(el));
};
または、これら 2 つの配列のどちらが長いかを最初に見つけて、最も長い配列を作成し、最も短い配列にメソッド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));
};
ネストされた Array.prototype.some 呼び出しを使用できます。これには、ネストされた完全なループを実行する他のソリューションではなく、最初の一致で救済されるという利点があります。
例えば。
var arr = [1, 2, 3];
var match = [2, 4];
var hasMatch = arr.some(a => match.some(m => a === m));
これがパフォーマンスの点でどれほど効率的かはわかりませんが、これは私が配列の分割を使用してすべてを簡潔に保つために使用するものです。
const shareElements = (arr1, arr2) => {
const typeArr = [...arr1, ...arr2]
const typeSet = new Set(typeArr)
return typeArr.length > typeSet.size
}
セットは配列とは異なり重複する要素を持つことができないため、両方の入力配列を組み合わせてセットに変換し、セットのサイズと配列の長さを比較すると、それらが要素を共有しているかどうかがわかります。
.find() へのネストされた呼び出しを持つ配列 .filter() は、2 番目の配列のメンバーである最初の配列のすべての要素を返します。返された配列の長さを調べて、2 番目の配列のいずれかが最初の配列にあったかどうかを判断します。
getCommonItems(firstArray, secondArray) {
return firstArray.filter((firstArrayItem) => {
return secondArray.find((secondArrayItem) => {
return firstArrayItem === secondArrayItem;
});
});
}
@Paul Grimshaw の回答を更新し、より読みやすくするためにincludes
insteed を使用しますindexOf
見つかった = arr1.some(r=> arr2.indexOf(r) >= 0)
見つかった = arr1.some(r=> arr2.includes(r))
一部の以前のアプローチの問題は、すべての単語が完全に一致する必要があることです。しかし、部分一致の結果を提供したい場合はどうでしょうか?
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"]
これは、ユーザーが単語を入力すると、結果にそれらの単語が任意の順序、位置、大文字と小文字で表示される検索ボックスを提供する場合に便利です。
次のようなアンダースコア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()」メソッドは、常にコンマを使用して値を区切ります。プリミティブ型でのみ実際に機能します。