Как найти первый элемент массива, соответствующий логическому условию в JavaScript?



мне интересно, есть ли известный, встроенный / элегантный способ найти первый элемент массива JS, соответствующий заданному условию. Эквивалент C# будет список.Найти.



до сих пор я использовал комбинацию из двух функций, как это:



// 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, может быть, там уже есть что-то подобное? Я представляю себе много люди должны реализовывать такие вещи все время...

1135   10  

10 ответов:

так как ES6 есть родной find метод для массивов.


Я должен отправить ответ, чтобы остановить эти filter пожелания :-)

поскольку в ECMAScript так много методов массива функционального стиля, возможно, что-то там уже есть?

можно использовать some метод выбора для итерации массива до тех пор, пока условие не будет выполнено (а затем остановится). К сожалению, это будет возвращает только то, было ли условие выполнено один раз, а не каким элементом (или по какому индексу) оно было выполнено. Поэтому мы должны немного изменить его:

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

начиная с 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 или других неподдерживаемых браузеров, вы можете использовать прокладку. Я рекомендую es6-shim. MDN также предлагает клин если по какой-то причине вы не хотите помещать всю ES6-прокладку в свой проект. Для максимальной совместимости вы хотите es6-shim, потому что в отличие от версии MDN он обнаруживает багги собственных реализаций find и перезаписывает их (см. комментарий, который начинается с "работа вокруг ошибок в массиве#найти и массив#findIndex" и строки сразу после него).

Как насчет использования фильтр и получение первого индекса из результирующего массива?

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

теперь должно быть ясно, что JavaScript не предлагает такого решения изначально; вот ближайшие две производные, наиболее полезные первые:

  1. Array.prototype.some(fn) предлагает желаемое поведение остановки при выполнении условия, но возвращает только наличие элемента; нетрудно применить некоторые хитрости, такие как решение, предлагаемое ответ Берги.

  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

если вы используете underscore.js можно использовать find и indexOf функции, чтобы получить то, что вы хотите:

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

документы:

по состоянию на ES 2015,Array.prototype.find() обеспечивает эту точную функциональность.

для браузеров, которые не поддерживают эту функцию, сеть разработчиков Mozilla предоставила полифилл (вставленный ниже):

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

массив.прототип.find () делает именно это, больше информации: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

резюме:

  • для нахождения первого элемента в массиве, который соответствует логическому условию, мы можем использовать ES6find()
  • find() расположена на Array.prototype так что он может быть использован на каждом массиве.
  • find() принимает обратный вызов, где a boolean условие проверяется. Функция возвращает стоимостью (не Индекс!)

пример:

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

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

console.log(found);   //  50

в Javascript нет встроенной функции для выполнения этого поиска.

Если вы используете jQuery, вы можете сделать jQuery.inArray(element,array).

менее элегантный способ, который будет 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.

Comments

    Ничего не найдено.