Как найти первый элемент массива, соответствующий логическому состоянию в JavaScript?
Мне интересно, есть ли известный встроенный/элегантный способ найти первый элемент массива JS, соответствующий данному условию. С# эквивалент был бы List.Find.
До сих пор я использовал комбинацию с двумя функциями:
// 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, возможно, там что-то уже такое? Я предполагаю, что многие люди должны постоянно реализовывать такие вещи...
Ответы
Ответ 1
Начиная с ES6, существует собственный метод find
для массивов.
const result = someArray.find(isNotNullNorUndefined);
Я должен опубликовать ответ, чтобы остановить эти предложения filter
:-)
поскольку в ECMAScript так много методов массива в функциональном стиле, может быть, есть что-то подобное?
Вы можете использовать метод some
Array для итерации массива до тех пор, пока не будет выполнено условие (и затем остановка). К сожалению, он вернет только то, было ли условие выполнено один раз, а не по какому элементу (или по какому индексу) оно было выполнено. Поэтому мы должны немного его изменить:
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);
Ответ 2
Как и в 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
и перезаписывает их (см. Комментарий, который начинается "Работа над ошибками в Array # find и Array # findIndex", а строки сразу после него).
Ответ 3
Как насчет использования filter и получения первого индекса из результирующего массива?
var result = someArray.filter(isNotNullNorUndefined)[0];
Ответ 4
Теперь должно быть ясно, что JavaScript не предлагает такого решения изначально; вот самые близкие две производные, наиболее полезные сначала:
-
Array.prototype.some(fn)
предлагает желаемое поведение остановки при выполнении условия, но возвращает только, присутствует ли элемент; не сложно применить некоторые обманы, такие как решение, предлагаемое ответом Берги.
-
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
Ответ 5
Если вы используете underscore.js
, вы можете использовать его функции find
и indexOf
, чтобы получить именно то, что вы хотите:
var index = _.indexOf(your_array, _.find(your_array, function (d) {
return d === true;
}));
Документация:
Ответ 6
Резюме:
- Для нахождения первого элемента в массиве, который соответствует логическому условию, мы можем использовать
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
Ответ 7
Начиная с ES 2015, Array.prototype.find()
обеспечивает эту точную функциональность.
Для браузеров, которые не поддерживают эту функцию, Mozilla Developer Network предоставила polyfill (вставить ниже):
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;
};
}
Ответ 8
Array.prototype.find() делает именно это, больше информации: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
Ответ 9
foundElement = myArray[myArray.findIndex(element => //condition here)];
Ответ 10
У меня есть вдохновение из нескольких источников в Интернете, чтобы получить решение ниже. Хотел принять во внимание как некоторые значения по умолчанию, так и предоставить способ сравнения каждой записи для общего подхода, который решается.
Использование: (давая значение "Второй")
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;
}
Ответ 11
В Javascript нет встроенной функции для выполнения этого поиска.
Если вы используете jQuery, вы можете сделать jQuery.inArray(element,array)
.
Ответ 12
Менее элегантным способом, который будет 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
.