Back

Как получить последнее совпадающее значение в массиве JavaScript

Как получить последнее совпадающее значение в массиве JavaScript

Вам нужно найти последний элемент в массиве, который соответствует условию. Возможно, это самая последняя запись об ошибке, последний валидный ввод формы или последний активный пользователь. До ES2023 это требовало неудобных обходных решений — разворота массивов, фильтрации всего содержимого или написания ручных циклов. Теперь есть более чистый способ.

Ключевые моменты

  • Используйте findLast() для получения последнего элемента массива, соответствующего условию, без изменения исходного массива.
  • Используйте findLastIndex(), когда вам нужна позиция, а не само значение.
  • Избегайте использования reverse().find(), так как это изменяет исходный массив и приводит к трудноуловимым ошибкам.
  • Помните о различных возвращаемых значениях: findLast() возвращает undefined, когда совпадение не найдено, тогда как findLastIndex() возвращает -1.

Современное решение: Array.prototype.findLast()

Array.prototype.findLast() перебирает массив в обратном порядке и возвращает первый элемент, который удовлетворяет вашему предикату. Это зеркальное отражение find(), который ищет с начала.

const logs = [
  { type: 'info', message: 'Started' },
  { type: 'error', message: 'Connection failed' },
  { type: 'info', message: 'Retrying' },
  { type: 'error', message: 'Timeout' }
]

const lastError = logs.findLast(log => log.type === 'error')
// { type: 'error', message: 'Timeout' }

Если ни один элемент не совпадает, findLast() возвращает undefined. Это делает безопасным использование с optional chaining:

const lastError = logs.findLast(log => log.type === 'critical')?.message ?? 'No critical errors'

findLast против findLastIndex: когда нужна позиция

Иногда вам нужен индекс, а не значение. Для этого предназначен findLastIndex(). Он возвращает индекс последнего совпадающего элемента или -1, если ничего не найдено.

const numbers = [1, 5, 3, 8, 2, 9, 4]
const lastLargeIndex = numbers.findLastIndex(n => n > 7)
// 5 (индекс элемента 9)

Используйте findLastIndex(), когда вам нужно выполнить splice, slice с этой позиции или обратиться к соседним элементам.

Совместимость с браузерами и fallback-решения

Оба метода findLast() и findLastIndex() являются частью ES2023 и имеют широкую поддержку в современных браузерах и последних версиях Node.js. Однако старые окружения могут их не поддерживать.

Если TypeScript выдает ошибки при использовании этих методов, вероятно, в вашем tsconfig.json нужно добавить "lib": ["ES2023"] или новее в опции компилятора.

Для окружений без нативной поддержки вот чистое fallback-решение, которое не изменяет исходный массив:

function findLast(arr, predicate) {
  for (let i = arr.length - 1; i >= 0; i--) {
    if (predicate(arr[i], i, arr)) {
      return arr[i]
    }
  }
  return undefined
}

Этот обратный цикл эффективен — он останавливается при первом совпадении и не создает промежуточные массивы.

Распространенные ошибки, которых следует избегать

Не разворачивайте массив для использования find():

// ❌ Изменяет исходный массив!
const last = arr.reverse().find(x => x > 5)

// ✅ Используйте findLast вместо этого
const last = arr.findLast(x => x > 5)

Вызов reverse() изменяет ваш массив на месте. Это создает трудноуловимые ошибки, которые сложно отлаживать.

Не путайте возвращаемые значения:

  • findLast() возвращает элемент или undefined
  • findLastIndex() возвращает индекс или -1

Проверка на истинность работает для findLast(), но с findLastIndex() необходимо явно проверять на -1:

const index = arr.findLastIndex(x => x > 100)
if (index !== -1) {
  // Найдено
}

Остерегайтесь ложных совпадений:

Если ваш массив содержит 0, false или пустые строки, убедитесь, что ваш предикат корректно их обрабатывает:

const values = [1, 2, 0, false, 'last']
values.findLast(x => x)        // 'last' (пропускает ложные значения)
values.findLast(() => true)    // 'last' (получает фактически последний элемент)

Когда выбирать каждый подход

ТребуетсяМетод
Последний элемент, соответствующий условиюfindLast()
Индекс последнего совпадающего элементаfindLastIndex()
Просто последний элемент (без условия)arr.at(-1) или arr[arr.length - 1]
Поиск точного значенияlastIndexOf()

Заключение

Чтобы получить последнее совпадающее значение в массиве JavaScript, в первую очередь используйте findLast(). Он читаем, не изменяет ваши данные и останавливается, как только находит совпадение. Когда вам нужна позиция, а не значение, используйте findLastIndex(). Для старых окружений простой обратный цикл обеспечивает надежное fallback-решение без рисков мутации, связанных с reverse(). Делайте ваши предикаты явными и помните о различных возвращаемых значениях — undefined против -1 — чтобы избежать скрытых ошибок.

Часто задаваемые вопросы

Да, findLast() работает с разреженными массивами. Он перебирает массив в обратном порядке и пропускает пустые ячейки, вызывая предикат только для элементов, которые фактически существуют. Это поведение аналогично тому, как find() обрабатывает разреженные массивы при прямом переборе.

findLast() более эффективен, потому что он прекращает итерацию, как только находит совпадение. Использование filter().pop() сначала обрабатывает весь массив, создавая новый массив в памяти, а затем извлекает последний элемент. Для больших массивов findLast() обеспечивает значительный прирост производительности.

Да, findLast() поддерживает дженерики TypeScript и сужение типов. Когда вы используете type guard в качестве предиката, TypeScript корректно выводит суженный тип для возвращаемого элемента. Убедитесь, что ваш tsconfig.json включает ES2023 в массиве lib для правильных определений типов.

Да, вы можете добавить полифил в Array.prototype, если метод не существует. Сначала проверьте наличие метода, затем назначьте функцию, которая перебирает массив в обратном порядке. Библиотеки Core-js и другие библиотеки полифилов также предоставляют готовые реализации для более широкой совместимости.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay