1. ホーム
  2. ジャバスクリプト

[解決済み】JavaScriptで文字列がリストに入っているかどうかを判断する

2022-03-25 14:04:57

質問

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
}

あるいは、switch関数を使うこともできる。

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 は、何らかのシンタックスシュガーがなければ、ネイティブでは動作しません。

どのように解決するのですか?

EcmaScript 6 以上

ES6以上を使用している場合、最もクリーンな方法は、アイテムの配列を構築して Array.includes :

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

これは、以下のような利点があります。 indexOf があるかどうかを適切にテストできるからです。 NaN の真ん中の要素のような、欠落している配列要素にマッチさせることができます。 [1, , 2] から undefined . includes も動作します。 JavaScript の型付き配列 のような Uint8Array .

ブラウザのサポートが心配な場合(IEやEdgeなど)、次のようにします。 チェック Array.includes CanIUse.Comにて がないブラウザやブラウザのバージョンを対象にする場合は includes をお勧めします。 ポリフィル.io は、ポリフィリング用です。

より高いパフォーマンス

注意点 Array.includes() は、配列の要素数に応じて処理時間が変化し、その性能は O(n) となります。より高いパフォーマンスが必要で、かつアイテムのセットを繰り返し構築する必要がない場合 (ただし、アイテムが何らかの要素を含んでいるかどうかを繰り返しチェックする必要がある場合) は、 配列を構成するために Set .

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

には、任意の反復可能な項目を渡すことができることに注意してください。 Set コンストラクタをサポートするものであれば の...のための ). また Set を使って配列に変換します。 Array.from(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

モダンブラウザをお使いの場合。 indexOf は常に動作します。しかし、IE8以前の場合はポリフィルが必要です。

もし indexOf は -1 を返すので、その項目はリストにはありません。しかし、このメソッドでは NaN にマッチし、明示的な undefined にはマッチしませんが、欠落している要素にはマッチします。 undefined のように、配列 [1, , 2] .

のポリフィル indexOf または includes IE、またはサポートがないその他のブラウザ/バージョンでは

のようなサービスを利用したくない場合。 polyfill.io 上記のように、標準に準拠したカスタムポリフィルを自分のソースコードに含めることはいつでも可能です。例えば、Mozilla Developer Network には、以下のようなものがあります。 indexOf .

Internet Explorer 7に対応するために、よりシンプルな形で indexOf() 関数は、標準に準拠していない。

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 の代わりに in , for (let x of arr) . しかし、出力ターゲットとダウンレベル トランスパイラの正確な設定/能力によっては、これは信頼できないかもしれません。さらに、あなたのコードとサードパーティライブラリのすべてがこの方法に厳密に従うことを保証できるのでなければ、この質問の目的には、おそらく includes 上記のように

  2. プロトタイプに新しいプロパティを定義するには、次のようにします。 Object.defineProperty() これは、そのプロパティを(デフォルトで)非列挙型にするためです。これは、あなたが使っているすべてのJavaScriptライブラリやモジュールが同じように動作している場合にのみ、問題を解決することができます。

最後の注意点

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

ブラウザでは、それぞれの異なる document オブジェクトはそれ自身の新しいグローバルスコープであり、ブラウザJSでは、新しいドキュメントを作成したり(オフスクリーンレンダリングやドキュメントフラグメントの作成に使われるものなど)、他のページの document オブジェクトのプロトタイプが、期待するメソッドを備えていない可能性があります。

nodeの場合、プロトタイプを変更することで global オブジェクトのプロトタイプは安全かもしれませんが、グローバルではない、インポートされたオブジェクトのプロトタイプを変更すると、同じパッケージの2つのバージョンが要求/インポートされることになった場合、破損につながる可能性があります。つまり、依存関係やサブ依存関係が期待したものと異なるバージョンを使用するまで、あなたのコードは問題なく動作し、あなた自身のコードを変更することなく、単純な npm install または yarn install がこの問題の引き金になる可能性があります。(この問題に対処するためのオプションとして、yarnの resolutions プロパティで package.json が、他に選択肢があるのであれば、それに頼るのは得策ではありません)。