JavaScriptでブール条件に一致する配列の最初の要素を見つける方法は?


218

与えられた条件に一致するJS配列の最初の要素を見つけるための既知の組み込み/エレガントな方法があるかどうか疑問に思っています。C#に相当するものはList.Findです。

これまでのところ、私は次のような2つの機能を持つコンボを使用しています。

// Returns the first element of an array that satisfies given predicate
Array.prototype.findFirst = function (predicateCallback) {
    if (typeof predicateCallback !== 'function') {
        return undefined;
    }

    for (var i = 0; i < arr.length; i++) {
        if (i in this && predicateCallback(this[i])) return this[i];
    }

    return undefined;
};

// Check if element is not undefined && not null
isNotNullNorUndefined = function (o) {
    return (typeof (o) !== 'undefined' && o !== null);
};

そして、私は使うことができます:

var result = someArray.findFirst(isNotNullNorUndefined);

しかし、ECMAScriptには非常に多くの関数型の配列メソッドがあるので、おそらくすでにこのようなものがあるのでしょうか。多くの人がいつもこのようなものを実装しなければならないと思います...


6
組み込みのメソッドはありませんが、documentcloud.github.com
underscore

Underscore.jsは本当にとても素敵に見えます!そして、find()があります。ありがとう!
Jakub P.

1
:だけ知っているので、あなたは、この減らすことができreturn (typeof (o) !== 'undefined' && o !== null);、これにダウンreturn o != null;。それらはまったく同じです。
狂気の断崖2012

1
知ってよかった。しかし、ご存知のとおり、私は!=や==のような強制演算子を信頼していません。その方法でnullに強制される他の値がないことを何らかの方法で確認する必要があるので、簡単にテストすることもできません...私はその機能を完全に削除します... :)
Jakub P.

私はこれをかなりエレガントな解決策と正直に言わなければなりません。私が見つけることができる最も近いものは、Array.prototype.someです。これは、いくつかの要素が関数の形で渡された特定の条件を満たすかどうかを見つけようとします。残念ながら、これはインデックスや要素の代わりにブール値を返します。ライブラリは非常に大きく、使用しないものを含む傾向があるため、ライブラリを使用するよりもソリューションをお勧めします。提供するスイートから1つの関数のみを使用する可能性があるため、軽量にすることをお勧めします。
グラハムロバートソン

回答:


217

ES6以降、配列にはネイティブのfindメソッドがあります。これは、最初の一致を見つけて値を返すと、配列の列挙を停止します。

const result = someArray.find(isNotNullNorUndefined);

古い答え:

これらのfilter提案をやめるには、回答を投稿する必要があります:-)

ECMAScriptには非常に多くの関数型の配列メソッドがあるので、おそらくすでにこのようなものがあるのでしょうか。

someArrayメソッドを使用して、条件が満たされるまで(そして停止するまで)配列を反復できます。残念ながら、どの要素(またはどのインデックス)で条件が満たされたのかではなく、条件が一度満たされたかどうかだけが返されます。したがって、少し修正する必要があります。

function find(arr, test, ctx) {
    var result = null;
    arr.some(function(el, i) {
        return test.call(ctx, el, i, arr) ? ((result = el), true) : false;
    });
    return result;
}

var result = find(someArray, isNotNullNorUndefined);

28
filter()に向けられたすべての嫌悪感を完全に理解しているとは言えません。遅いかもしれませんが、実際には。これがおそらく使用されるほとんどの場合、これは最初は小さなリストであり、JavaScriptアプリケーションの大部分は、このレベルでの効率を本当に心配するほど複雑ではありません。[] .filter(test).pop()または[] .filter(test)[0]はシンプルで、ネイティブで読み取り可能です。もちろん、ビジネス向けのアプリやゲームなどの集中的なアプリではなく、ウェブサイトについて話している。
Josh Mc、

11
フィルターソリューションはすべての配列/コレクションを横断しますか?その場合、見つかった値がコレクション内の最初の値であっても、すべての配列にわたって実行されるため、フィルタリングは非常に非効率的です。some()一方、すぐに戻ります。ほとんどの場合、フィルタリングソリューションよりもはるかに高速です。
AlikElzin-kilaka 2015

@ AlikElzin-kilaka:はい、その通りです。
ベルギ2015

15
@JoshMc確かですが、Stack Overflowのような単純な問題の解決策を投稿するときに、不当に非効率的ではないことは理にかなっています。多くの人がここからコードをコピーしてユーティリティ関数に貼り付けますが、一部の人は、実装を考慮せずにパフォーマンスが重要なコンテキストでそのユーティリティ関数を使用することになります。最初に効率的な実装を備えたものを提供した場合は、本来ならそうではないパフォーマンスの問題を解決するか、診断に費やす開発時間を節約しました。
Mark Amery

1
@SuperUberDuper:いいえ。以下のMark Ameryの回答を参照してください。
Bergi

104

ECMAScript 6以降では、これを使用できますArray.prototype.find。これは、Firefox(25.0)、Chrome(45.0)、Edge(12)、およびSafari(7.1)で実装されて機能します、Internet Explorerや他の古いプラットフォームや一般的でないプラットフォームでは機能しません

たとえば、次の式はに評価され106ます。

[100,101,102,103,104,105,106,107,108,109].find(function (el) {
    return el > 105;
});

今すぐこれを使用したいが、IEまたはその他のサポートされていないブラウザーのサポートが必要な場合は、shimを使用できます。es6-shimをお勧めします。MDNは何らかの理由でes6-shim全体をプロジェクトに使用したくない場合にも、shimを提供します。互換性を最大にするために、es6-shimが必要です。MDNバージョンとは異なり、バグのあるネイティブ実装を検出findして上書きします(「Array#findおよびArray#findIndexのバグの回避」で始まるコメントとその直後の行を参照してください)。 。


find条件に一致する要素が見つかるとすぐに停止し、すべての要素をループして一致するすべての要素を取得するfilterので、より優れています。findfilter
Anh Tran

59

フィルターを使用して、結果の配列から最初のインデックスを取得するのはどうですか?

var result = someArray.filter(isNotNullNorUndefined)[0];

6
es5メソッドを使い続けます。var result = someArray.filter(isNotNullNorUndefined).shift();
someyoungideas

私自身は@Bergiの上にあるfindの回答に賛成票を投じましたが、ES6の構造化を少し上回れば改善できると思います。
Nakul Manchanda

@someyoungideas .shiftここで使用する利点を説明できますか?
ジャクビゾン

3
@jakubiszonを使用する利点shiftは、「スマートに見える」ということですが、実際にはより混乱します。shift()引数なしで呼び出すことは最初の要素を取ることと同じだと誰が思うでしょうか?IMOは不明です。とにかく、配列へのアクセスがより速くなります:jsperf.com/array-access-vs-shift
Josh M.

また、角括弧表記は、ES5私の知る限りで両方のオブジェクトと配列のために利用可能であるの嗜好見たことがない.shift()上に[0]明示的に次のように述べています。それにもかかわらず、それはあなたが使用するかしないかを選択できる代替手段ですが、私は固執し[0]ます。
SidOfc

15

JavaScriptがそのようなソリューションをネイティブで提供していないことは、今では明らかです。これが最も近い2つの派生物であり、最初に最も有用です。

  1. Array.prototype.some(fn)条件が満たされたときに停止するという望ましい動作を提供しますが、要素が存在するかどうかのみを返します。Bergiの回答が提供するソリューションなど、いくつかのトリックを適用することは難しくありません。

  2. Array.prototype.filter(fn)[0]優れたワンライナーN - 1になりますが、必要なものを取得するためだけに要素を破棄するため、効率は最も低くなります。

JavaScriptの従来の検索メソッドは、要素自体または-1ではなく、見つかった要素のインデックスを返すことを特徴としています。これにより、可能なすべての型のドメインから戻り値を選択する必要がなくなります。インデックスは数値のみで、負の値は無効です。

上記のソリューションはどちらもオフセット検索をサポートしていないため、次のように書くことにしました。

(function(ns) {
  ns.search = function(array, callback, offset) {
    var size = array.length;

    offset = offset || 0;
    if (offset >= size || offset <= -size) {
      return -1;
    } else if (offset < 0) {
      offset = size - offset;
    }

    while (offset < size) {
      if (callback(array[offset], offset, array)) {
        return offset;
      }
      ++offset;
    }
    return -1;
  };
}(this));

search([1, 2, NaN, 4], Number.isNaN); // 2
search([1, 2, 3, 4], Number.isNaN); // -1
search([1, NaN, 3, NaN], Number.isNaN, 2); // 3

最も包括的な答えのように見えます。答えに3つ目のアプローチを追加できますか?
悲惨な

13

概要:

  • ブール条件に一致する配列の最初の要素を見つけるには、 ES6 find()
  • find()上に配置されているArray.prototype、それはすべてのアレイ上で使用できるように。
  • find()boolean条件がテストされるコールバックを受け取ります。関数は値を返します(インデックスではありません!)

例:

const array = [4, 33, 8, 56, 23];

const found = array.find((element) => {
  return element > 50;
});

console.log(found);   //  56


8

使用しているunderscore.js場合は、その関数findindexOf関数を使用して、必要なものを正確に取得できます。

var index = _.indexOf(your_array, _.find(your_array, function (d) {
    return d === true;
}));

ドキュメンテーション:


これだけのライブラリをロードするだけの価値はないので、必要な場合は単にunderscorejsオプションを使用してください
DannyFeliz

4

ES 2015 Array.prototype.find()では、この正確な機能を提供しています。

この機能をサポートしないブラウザーのために、Mozilla Developer Networkはポリフィル(以下に貼り付け)を提供しています:

if (!Array.prototype.find) {
  Array.prototype.find = function(predicate) {
    if (this === null) {
      throw new TypeError('Array.prototype.find called on null or undefined');
    }
    if (typeof predicate !== 'function') {
      throw new TypeError('predicate must be a function');
    }
    var list = Object(this);
    var length = list.length >>> 0;
    var thisArg = arguments[1];
    var value;

    for (var i = 0; i < length; i++) {
      value = list[i];
      if (predicate.call(thisArg, value, i, list)) {
        return value;
      }
    }
    return undefined;
  };
}

3

Array.prototype.find()はそれを行います、詳細:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find


2
foundElement = myArray[myArray.findIndex(element => //condition here)];

3
条件節といくつかの説明的な単語を含む実際の例は、あなたの答えをより価値のある、わかりやすいものにするでしょう。
SaschaM78

0

インターネット上の複数のソースからインスピレーションを得て、以下の解決策を導き出しました。いくつかのデフォルト値の両方を考慮し、これが解決する一般的なアプローチの各エントリを比較する方法を提供する必要がありました。

使用法:(値「2番目」を与える)

var defaultItemValue = { id: -1, name: "Undefined" };
var containers: Container[] = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
GetContainer(2).name;

実装:

class Container {
    id: number;
    name: string;
}

public GetContainer(containerId: number): Container {
  var comparator = (item: Container): boolean => {
      return item.id == containerId;
    };
    return this.Get<Container>(this.containers, comparator, this.defaultItemValue);
  }

private Get<T>(array: T[], comparator: (item: T) => boolean, defaultValue: T): T {
  var found: T = null;
  array.some(function(element, index) {
    if (comparator(element)) {
      found = element;
      return true;
    }
  });

  if (!found) {
    found = defaultValue;
  }

  return found;
}

-2

この検索を実行するためのJavaScriptの組み込み関数はありません。

jQueryを使用している場合は、を実行できますjQuery.inArray(element,array)


私もアンダースコア
Jakub P.

3
これは、質問者が必要とする出力(ブール値ではなく、あるインデックスの要素が必要)を満たしません。
グラハムロバートソン

@GrahamRobertson $.inArrayはブール値を返さず、(驚くべきことに!)最初に一致した要素のインデックスを返します。ただし、OPが要求したことはまだ実行されません。
Mark Amery 2015

-2

throw(に基づいてArray.prototype.filter)すべての正しいエラーメッセージを表示しますが、最初の結果で反復を停止する、あまりエレガントでない方法は次のとおりです。

function findFirst(arr, test, context) {
    var Result = function (v, i) {this.value = v; this.index = i;};
    try {
        Array.prototype.filter.call(arr, function (v, i, a) {
            if (test(v, i, a)) throw new Result(v, i);
        }, context);
    } catch (e) {
        if (e instanceof Result) return e;
        throw e;
    }
}

次に例は

findFirst([-2, -1, 0, 1, 2, 3], function (e) {return e > 1 && e % 2;});
// Result {value: 3, index: 5}
findFirst([0, 1, 2, 3], 0);               // bad function param
// TypeError: number is not a function
findFirst(0, function () {return true;}); // bad arr param
// undefined
findFirst([1], function (e) {return 0;}); // no match
// undefined

filterを使用して終了することで機能しthrowます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.