web-dev-qa-db-fra.com

Comment trouver le premier élément du tableau correspondant à une condition booléenne en JavaScript?

Je me demande s’il existe un moyen connu, intégré et élégant de trouver le premier élément d’un tableau JS correspondant à une condition donnée. Un équivalent C # serait List.Find .

Jusqu'ici, j'ai utilisé un combo à deux fonctions comme celui-ci:

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

Et puis je peux utiliser:

var result = someArray.findFirst(isNotNullNorUndefined);

Mais puisqu'il y a tant de méthodes de tableaux de style fonctionnel dans ECMAScript , il y a peut-être déjà quelque chose comme ça? J'imagine que beaucoup de gens doivent mettre en œuvre des choses comme ça tout le temps ...

171
Jakub P.

Depuis ES6, il existe la méthode native find pour les tableaux.

const result = someArray.find(isNotNullNorUndefined);

Je dois poster une réponse pour arrêter ces suggestions de filter :-)

puisqu'il y a tellement de méthodes de tableaux de style fonctionnel dans ECMAScript, il y a peut-être déjà quelque chose comme ça?

Vous pouvez utiliser la méthode de tableau some jusqu'à ce qu'une condition soit remplie (puis s'arrête). Malheureusement, il ne retournera que si la condition a été remplie une fois, pas par quel élément (ou à quel index) elle a été remplie. Nous devons donc le modifier un peu:

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);
165
Bergi

A partir de ECMAScript 6, vous pouvez utiliser Array.prototype.find pour cela. Ceci est implémenté et fonctionne dans Firefox (25.0), Chrome (45.0), Edge (12) et Safari (7.1), mais pas dans Internet Explorer ou un tas d’autres vieux ou peu communs plates-formes .

Par exemple, l'expression ci-dessous est évaluée à _106_.

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

Si vous voulez utiliser ceci maintenant mais que vous avez besoin de support pour IE ou d'autres navigateurs non pris en charge, vous pouvez utiliser un shim. Je recommande le es6-shim . MDN propose également ne cale si, pour une raison quelconque, vous ne souhaitez pas intégrer la totalité de es6-cession à votre projet. Es6-shim, pour une compatibilité maximale, car contrairement à la version MDN, il détecte les implémentations natives erronées de find et les écrase (voir le commentaire qui commence "Contournez les bogues dans Array #find and Array # findIndex " et les lignes qui le suivent immédiatement).

93
Mark Amery

Qu'en est-il d'utiliser filter et d'obtenir le premier index du tableau résultant?

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

Il devrait être clair maintenant que JavaScript n'offre pas une telle solution de manière native; voici les deux dérivés les plus proches, le plus utile en premier:

  1. Array.prototype.some(fn) offre le comportement souhaité d'arrêt lorsqu'une condition est remplie, mais renvoie uniquement si un élément est présent; il n'est pas difficile d'appliquer des astuces, comme la solution proposée par réponse de Bergi .

  2. Array.prototype.filter(fn)[0] constitue un excellent choix sur une ligne mais est le moins efficace, car vous jetez N - 1 éléments uniquement pour obtenir ce dont vous avez besoin.

Les méthodes de recherche traditionnelles en JavaScript sont caractérisées par le renvoi de l'index de l'élément trouvé à la place de l'élément lui-même ou -1. Cela évite de devoir choisir une valeur de retour dans le domaine de tous les types possibles; un index ne peut être qu'un nombre et les valeurs négatives ne sont pas valides.

Les deux solutions ci-dessus ne supportent pas la recherche offset, donc j'ai décidé d'écrire ceci:

(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
15
Ja͢ck

Si vous utilisez underscore.js, vous pouvez utiliser ses fonctions find et indexOf pour obtenir exactement ce que vous voulez:

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

Documentation:

8
Matt Woelk

Sommaire:

  • Pour trouver le premier élément d'un tableau qui correspond à une condition booléenne, nous pouvons utiliser le ES6find()
  • find() est situé sur Array.prototype afin qu'il puisse être utilisé sur chaque tableau.
  • find() prend un rappel où une condition boolean est testée. La fonction retourne le valeur (pas l'index!)

Exemple:

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

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

console.log(found);   //  56
5
Willem van der Veen

À compter de l'ES 2015, Array.prototype.find() fournit cette fonctionnalité exacte.

Pour les navigateurs qui ne prennent pas en charge cette fonctionnalité, le réseau de développeurs Mozilla a fourni un polyfill (collé ci-dessous):

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

Array.prototype.find () fait justement cela, plus d’informations: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

3
Dan Ochiana

Je me suis inspiré de multiples sources sur Internet pour trouver la solution ci-dessous. Je voulais prendre en compte à la fois certaines valeurs par défaut et fournir un moyen de comparer chaque entrée pour une approche générique que cela résout.

Utilisation: (donnant la valeur "seconde")

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

La mise en oeuvre:

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;
}
0
Henrik
foundElement = myArray[myArray.findIndex(element => //condition here)];
0
dotista2008