JavaScriptで文字列がリスト内にあるかどうかを判断する 質問する

JavaScriptで文字列がリスト内にあるかどうかを判断する 質問する

SQL では、次のようにして文字列がリスト内にあるかどうかを確認できます。

Column IN ('a', 'b', 'c')

これを JavaScript で行う良い方法は何でしょうか? これを行うのは非常に面倒です:

if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
   // do something
}

これのパフォーマンスや明瞭性についてはよくわかりません。

if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
   // do something
}

または、スイッチ関数を使用することもできます。

var str = 'a',
   flag = false;

switch (str) {
   case 'a':
   case 'b':
   case 'c':
      flag = true;
   default:
}

if (expression1 || expression2 || flag) {
   // do something
}

しかし、それはひどい混乱です。何かアイデアはありますか?

この場合、企業のイントラネット ページなので、Internet Explorer 7 を使用する必要があります。そのため、['a', 'b', 'c'].indexOf(str) !== -1構文糖なしではネイティブには動作しません。

ベストアンサー1

ES6 (ES2015) 以上

ECMAScript 6(別名ES2015)以上を使用している場合、最もクリーンな方法は、アイテムの配列を構築して使用することです。Array.includes:

['a', 'b', 'c'].includes('b')

indexOfこれは、リスト内のの存在を適切にテストし、 の中央の要素などの欠落した配列要素を に一致させることができるため、よりNaNも本質的に有利です。また、と を同等として扱います。にも機能します。[1, , 2]undefined+0-0includesJavaScript 型付き配列のようなUint8Array

ブラウザのサポート(IEやEdgeなど)が気になる場合は、Array.includesCanIUse.Comで確認、不足しているブラウザまたはブラウザバージョンをターゲットにしたい場合はincludes、Babelなどのツールを使用してより低いECMAScriptバージョンにトランスパイルするか、ブラウザにポリフィルスクリプトを含める必要があります。ポリフィル

より高いパフォーマンス

実行時間が配列の要素数に比例しないという保証はないことに注意してくださいArray.includes()。パフォーマンスはO(n)になる可能性があります。より高いパフォーマンスが必要で、アイテムのセットを繰り返し構築しない(ただし、アイテムに何らかの要素が含まれているかどうかを繰り返しチェックする)場合は、SetES 仕様では、Set(Mapおよび) の実装が読み取りに対してサブ線形であることが要求されているためです。

仕様では、セットは「平均して、コレクション内の要素の数に対して線形以下のアクセス時間を提供する」ように実装する必要があります。したがって、複雑さが O(N) 未満である限り、ハッシュ テーブル (O(1) の検索)、検索ツリー (O(log(N)) の検索)、またはその他のデータ構造として内部的に表現できます。

const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')

コンストラクタには任意の反復可能な項目Set)または を展開して をSet配列に変換することもできます。Array.from(set)[...set]

配列なし

isInListこれはあまりお勧めできませんが、次のように文字列に新しいプロパティを追加できます。

if (!String.prototype.isInList) {
  Object.defineProperty(String.prototype, 'isInList', {
    get: () => function(...args) {
      let value = this.valueOf();
      for (let i = 0, l = args.length; i < l; i += 1) {
        if (arguments[i] === value) return true;
      }
      return false;
    }
  });
}

次のように使用します:

'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false

についても同じことができますNumber.prototype

Object.definePropertyは、IE8 以前、または他のブラウザの非常に古いバージョンでは使用できないことに注意してください。ただし、これは よりもはるかに優れたソリューションです。String.prototype.isInList = function() { ... }なぜなら、このような単純な割り当てを使用すると、 に列挙可能なプロパティが作成されString.prototype、コードが壊れる可能性が高くなるためです。

配列のインデックス

最新のブラウザを使用している場合は、indexOf常に動作します。ただし、IE8 以前の場合は、ポリフィルが必要になります。

が -1 を返す場合indexOf、項目はリスト内にありません。ただし、このメソッドは を適切にチェックしないことに注意してください。また、NaN明示的な と一致させることはできますが、配列 内の のundefined欠落した要素と一致させることはできません。undefined[1, , 2]

indexOfIEまたはincludesサポートされていない他のブラウザ/バージョン用のポリフィル

次のようなサービスを使いたくない場合はポリフィル上で述べたように、ソースコードに標準に準拠したカスタムポリフィルをいつでも組み込むことができます。たとえば、CoreJsライブラリには次の実装があります。indexOf

indexOf()Internet Explorer 7 用のソリューションを作成する必要があったこの状況では、標準に準拠していない関数のよりシンプルなバージョンを「独自に作成しました」 。

if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(item) {
      var i = this.length;
      while (i--) {
         if (this[i] === item) return i;
      }
      return -1;
   }
}

オブジェクトプロトタイプの変更に関する注意事項

String.prototypeただし、またはを変更するArray.prototypeことは長期的には良い戦略ではないと思います。JavaScript でオブジェクト プロトタイプを変更すると、重大なバグが発生する可能性があります。自分の環境で変更しても安全かどうかを判断する必要があります。最も重要なのは、配列を反復処理すると (Array.prototype にプロパティが追加されている場合)、for ... inキーの 1 つとして新しい関数名が返されることです。

Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!

コードは今は動作するかもしれませんが、将来誰かが継承されたキーを厳重に保護していないサードパーティの JavaScript ライブラリまたはプラグインを追加した瞬間に、すべてが壊れる可能性があります。

この破損を回避する従来の方法は、列挙中に各値をチェックして、オブジェクトが実際にその値を非継承プロパティとして持っているかどうかを確認しif (arr.hasOwnProperty(x))、その値のみを処理するというものでしxた。

この余分なキーの問題を回避するための新しい ES6 の方法は次のとおりです。

  1. of、の代わりに を使用してくださいinfor (let x of arr)ただし、出力ターゲットとダウンレベル トランスパイラの正確な設定/機能によっては、これが信頼できない場合があります。さらに、すべてのコードとサードパーティ ライブラリがこのメソッドに厳密に従っていることを保証できない限り、この質問の目的上、おそらくincludes上記の を使用することになります。

  2. を使用してプロトタイプに新しいプロパティを定義しますObject.defineProperty()。これにより、プロパティは (デフォルトで) 列挙不可能になります。使用するすべての JavaScript ライブラリまたはモジュールもこれを実行する場合にのみ、問題が本当に解決されます。

最後にもう一つ注意すべき問題

最後に、ポリフィルは理にかなっており、オブジェクト プロトタイプを変更することは便利な戦略ですが、そのアプローチではスコープの問題が発生する場合があることに注意してください。

ブラウザでは、各documentオブジェクトはそれぞれ独自の新しいグローバル スコープを持ち、ブラウザ JS では新しいドキュメント (オフスクリーン レンダリング用やドキュメント フラグメントの作成用など) を作成したり、別のページのdocumentオブジェクトへの参照を取得したり (名前付きターゲット リンクを使用したページ間通信経由など) することが可能です。そのため、特定の (まれな?) 状況では、オブジェクト プロトタイプに期待するメソッドがない可能性があります。ただし、新しいグローバル オブジェクトに対してポリフィルを再度実行することはいつでも可能です...

Node.js では、globalオブジェクトのプロトタイプを変更することは安全かもしれませんが、グローバルでないインポートされたオブジェクトのプロトタイプを変更すると、同じパッケージの 2 つのバージョンが要求/インポートされることになった場合に破損につながる可能性があります。これは、2 つのバージョンのインポートでは同じオブジェクトが公開されないため、同じオブジェクト プロトタイプを持たないためです。つまり、依存関係またはサブ依存関係が予期したものとは異なるバージョンを使用するまで、コードは正常に動作しますが、独自のコードに変更を加えずに、単純なnpm installまたは によってこの問題が発生する可能性があります。( の yarn のプロパティyarn installなど、これに対処するオプションはありますが、他のオプションがある場合はこれに頼るのは得策ではありません。)resolutionspackage.json

おすすめ記事