Como encontrar o primeiro elemento de array correspondente a uma condição booleana em JavaScript?

Pergunto-me se existe uma forma conhecida e elegante de encontrar o primeiro elemento de uma matriz JS correspondente a uma dada condição. Um equivalente C# seria lista.Encontre.

até agora tenho usado uma combinação de duas funções como esta:

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

e depois posso usar:

var result = someArray.findFirst(isNotNullNorUndefined);

mas já que existem tantos métodos de matriz funcional em ECMAScript, talvez já haja algo por aí como isto? Imagino que muita gente tenha de implementar coisas. sempre assim...

Author: Jakub P., 2012-05-05

10 answers

Uma vez que o ES6 é o nativo find Método para matrizes.

Eu tenho que postar uma resposta para parar estas filter sugestões: -)

Como há tantos métodos de matriz funcional em ECMAScript, talvez já haja algo assim?

Pode usar o some método Array para iterar o array até que uma condição seja cumprida (e então parar). Infelizmente, só voltará a verificar se a condição foi cumprida uma vez., não com que elemento (ou em que índice) foi satisfeito. Por isso, temos de O alterar um pouco.

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;
}
 123
Author: Bergi, 2016-08-27 20:05:37

A partir do ECMAScript 6, pode utilizar Array.prototype.find por isto. Isto é implementado e trabalhando no Firefox (25.0), Chrome (45.0), Edge (12), e Safari (7.1), mas não no Internet Explorer ou um monte de outras plataformas antigas ou incomuns.

Por exemplo, a expressão abaixo avalia-se em 106.

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

Se quiser usar isto agora, mas precisar de suporte para o IE ou outros navegadores sem suporte, pode usar um shim. Recomendo o es6-shim. MDN também oferece Um shim, se por alguma razão não quiseres colocar todo o es6-shim no teu projecto. Para a compatibilidade máxima, você quer o es6-shim, porque ao contrário da versão MDN, ele detecta implementações nativas buggy de find e os reescreve (veja o comentário que começa "trabalhar em torno de bugs na lista#find and Array#findIndex" e as linhas imediatamente a seguir).

 78
Author: Mark Amery, 2016-12-14 18:24:21

Que tal usar o filtro e obter o primeiro índice da matriz resultante?

var result = someArray.filter(isNotNullNorUndefined)[0];
 49
Author: Phil Mander, 2014-11-28 02:04:37

Já deve estar claro que o JavaScript não oferece tal solução nativamente; Aqui estão as duas derivadas mais próximas, as mais úteis primeiro:

  1. Array.prototype.some(fn) oferece o comportamento desejado de parar quando uma condição é cumprida, mas retorna apenas se um elemento está presente; não é difícil aplicar algumas artimanhas, como a solução oferecida pela resposta de Bergi.

  2. Array.prototype.filter(fn)[0] faz para um grande one-liner, mas é o menos eficiente, porque você joga fora N - 1 elementos apenas para obter o que você precisa.

Os métodos tradicionais de pesquisa em JavaScript são caracterizados por devolver o índice do elemento encontrado em vez do próprio elemento ou -1. Isso evita ter que escolher um valor de retorno do domínio de todos os tipos possíveis; um índice só pode ser um número e os valores negativos são inválidos.

Ambas as soluções acima também não suportam a procura por offset, por isso decidi escrever isto:

(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
Author: Ja͢ck, 2017-05-23 11:47:26

Se estiver a utilizar {[1] } pode usar as suas funções find e indexOf para obter exactamente o que quer:

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

Documentação:

 7
Author: Matt Woelk, 2014-02-26 17:00:19

A partir de ES 2015, Array.prototype.find() prevê esta funcionalidade exacta.

Para navegadores que não suportam esta funcionalidade, a rede de desenvolvimento do Mozilla forneceu um polifill (colado abaixo):

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
Author: Kevin Lee Garner, 2016-05-02 18:48:41
 2
Author: Dan Ochiana, 2016-02-22 16:28:03

Resumo:

  • para encontrar o primeiro elemento numa matriz que corresponda a uma condição booleana, podemos usar o ES6 find()
  • find() está localizado em {[4] } para que possa ser usado em todos os array.
  • find() faz um callback onde uma condição boolean é testada. A função devolve o valor (não o índice!)

Exemplo:

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

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

console.log(found);   //  50
 0
Author: Willem van der Veen, 2018-08-19 11:46:10

Não existe nenhuma função incorporada no Javascript para realizar esta pesquisa.

Se estiver a usar jQuery, pode fazer um jQuery.inArray(element,array).

 -1
Author: PedroSena, 2012-07-10 09:49:05

Uma forma menos elegante que irá throw todas as mensagens de erro certas (com base em Array.prototype.filter) mas vai parar de iterar no primeiro resultado é

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

Então os exemplos são

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

Funciona terminando filter usando throw.

 -1
Author: Paul S., 2013-08-29 18:46:06