阿里云主机折上折
  • 微信号
Current Site:Index > Array.prototype.findLast() and findLastIndex()

Array.prototype.findLast() and findLastIndex()

Author:Chuan Chen 阅读数:26877人阅读 分类: JavaScript

ECMAScript 13 introduced two new array methods: Array.prototype.findLast() and Array.prototype.findLastIndex(). These methods address the inefficiency of traversing an array from front to back when trying to retrieve the last matching element, providing developers with more flexible array manipulation capabilities.

Array.prototype.findLast() Method

The findLast() method searches the array from the end to the beginning and returns the first element that satisfies the condition. If no matching element is found, it returns undefined. Its syntax is similar to the find() method:

arr.findLast(callback(element[, index[, array]])[, thisArg])

Basic Usage Example

const numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

// Find the last element greater than 3
const lastGreaterThanThree = numbers.findLast(num => num > 3);
console.log(lastGreaterThanThree); // Output: 4

// Find the last element equal to 10 (does not exist)
const lastEqualsTen = numbers.findLast(num => num === 10);
console.log(lastEqualsTen); // Output: undefined

Complex Object Lookup

const users = [
  { id: 1, name: 'Alice', active: true },
  { id: 2, name: 'Bob', active: false },
  { id: 3, name: 'Charlie', active: true },
  { id: 4, name: 'David', active: false },
  { id: 5, name: 'Eve', active: true }
];

// Find the last user with active set to true
const lastActiveUser = users.findLast(user => user.active);
console.log(lastActiveUser); 
// Output: { id: 5, name: 'Eve', active: true }

Performance Considerations

When working with large arrays, findLast() is more efficient than reversing the array and then using find():

// Inefficient approach
const reversedFind = [...largeArray].reverse().find(callback);

// Efficient approach
const optimizedFind = largeArray.findLast(callback);

Array.prototype.findLastIndex() Method

The findLastIndex() method is similar to findLast(), but it returns the index of the element instead of the element itself. If no matching element is found, it returns -1.

arr.findLastIndex(callback(element[, index[, array]])[, thisArg])

Basic Usage Example

const numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];

// Find the index of the last element greater than 3
const lastIndexGreaterThanThree = numbers.findLastIndex(num => num > 3);
console.log(lastIndexGreaterThanThree); // Output: 5

// Find the index of the last element equal to 10 (does not exist)
const lastIndexEqualsTen = numbers.findLastIndex(num => num === 10);
console.log(lastIndexEqualsTen); // Output: -1

Practical Application Scenario

const logEntries = [
  { timestamp: '2023-01-01', event: 'login', success: true },
  { timestamp: '2023-01-02', event: 'purchase', success: true },
  { timestamp: '2023-01-03', event: 'login', success: false },
  { timestamp: '2023-01-04', event: 'logout', success: true },
  { timestamp: '2023-01-05', event: 'login', success: true }
];

// Find the index of the last failed login
const lastFailedLoginIndex = logEntries.findLastIndex(
  entry => entry.event === 'login' && !entry.success
);
console.log(lastFailedLoginIndex); // Output: 2

Comparison with Existing Methods

Differences from find() and findIndex()

const array = ['a', 'b', 'c', 'b', 'a'];

console.log(array.find(item => item === 'b'));      // 'b' (first)
console.log(array.findLast(item => item === 'b'));  // 'b' (last)

console.log(array.findIndex(item => item === 'b'));      // 1
console.log(array.findLastIndex(item => item === 'b'));  // 3

Differences from lastIndexOf()

const array = [1, 2, 3, 2, 1];

// lastIndexOf can only perform simple value comparisons
console.log(array.lastIndexOf(2)); // 3

// findLastIndex can handle complex conditions
console.log(array.findLastIndex(x => x > 1 && x < 3)); // 3

Advanced Usage

Usage in Method Chaining

const products = [
  { id: 1, name: 'Laptop', price: 999, inStock: true },
  { id: 2, name: 'Phone', price: 699, inStock: false },
  { id: 3, name: 'Tablet', price: 499, inStock: true },
  { id: 4, name: 'Monitor', price: 299, inStock: true },
  { id: 5, name: 'Keyboard', price: 99, inStock: false }
];

// Get the last in-stock product priced below 500
const lastAffordableInStock = products
  .filter(product => product.inStock)
  .findLast(product => product.price < 500);

console.log(lastAffordableInStock); 
// Output: { id: 4, name: 'Monitor', price: 299, inStock: true }

Interaction with Sparse Arrays

const sparseArray = [1, , 3, , 5, , 7];

// Find the last existing element
const lastExisting = sparseArray.findLast(x => x !== undefined);
console.log(lastExisting); // 7

// Find the index of the last existing element
const lastExistingIndex = sparseArray.findLastIndex(x => x !== undefined);
console.log(lastExistingIndex); // 6

Browser Compatibility and Polyfill

Although ECMAScript 13 is a relatively new standard, these features can be polyfilled in environments that do not support them:

findLast() Polyfill

if (!Array.prototype.findLast) {
  Array.prototype.findLast = function(callback, thisArg) {
    for (let i = this.length - 1; i >= 0; i--) {
      if (i in this && callback.call(thisArg, this[i], i, this)) {
        return this[i];
      }
    }
    return undefined;
  };
}

findLastIndex() Polyfill

if (!Array.prototype.findLastIndex) {
  Array.prototype.findLastIndex = function(callback, thisArg) {
    for (let i = this.length - 1; i >= 0; i--) {
      if (i in this && callback.call(thisArg, this[i], i, this)) {
        return i;
      }
    }
    return -1;
  };
}

Real-World Application Examples

Finding the Last Error in Form Validation

const formFields = [
  { id: 'username', value: '', isValid: false, error: 'Username cannot be empty' },
  { id: 'password', value: '123', isValid: false, error: 'Password too short' },
  { id: 'email', value: 'test@example.com', isValid: true },
  { id: 'age', value: '25', isValid: true }
];

// Get the error message of the last invalid field
const lastErrorIndex = formFields.findLastIndex(field => !field.isValid);
if (lastErrorIndex !== -1) {
  console.log(`Last error: ${formFields[lastErrorIndex].error}`);
  // Output: "Last error: Password too short"
}

Detecting Recent Events in Game Development

const gameEvents = [
  { frame: 100, type: 'enemy_spawn', enemyType: 'zombie' },
  { frame: 150, type: 'player_hit', damage: 10 },
  { frame: 180, type: 'enemy_spawn', enemyType: 'skeleton' },
  { frame: 200, type: 'player_hit', damage: 15 },
  { frame: 220, type: 'powerup_collected', powerType: 'health' }
];

// Find the frame of the last player hit
const lastHitFrame = gameEvents.findLastIndex(event => event.type === 'player_hit');
console.log(`Last hit occurred at frame: ${gameEvents[lastHitFrame].frame}`);
// Output: "Last hit occurred at frame: 200"

Performance Optimization Tips

Early Termination of Traversal

Like find() and findIndex(), findLast() and findLastIndex() terminate traversal immediately upon finding the first match:

const largeArray = new Array(1000000).fill(0);
largeArray[999999] = 1;

console.time('findLast');
largeArray.findLast(x => x === 1); // Only needs to check the last element
console.timeEnd('findLast'); // Very fast

console.time('find');
largeArray.find(x => x === 1); // Needs to check all elements until the last
console.timeEnd('find'); // Much slower

Comparison with for Loops

const array = new Array(100000).fill(0).map((_, i) => i);

// Using a for loop to find the last even number
let lastEvenFor;
console.time('for loop');
for (let i = array.length - 1; i >= 0; i--) {
  if (array[i] % 2 === 0) {
    lastEvenFor = array[i];
    break;
  }
}
console.timeEnd('for loop');

// Using findLast to find the last even number
console.time('findLast');
const lastEven = array.findLast(x => x % 2 === 0);
console.timeEnd('findLast');

// Both approaches have similar performance, but findLast is more concise

Comparison with Similar Features in Other Languages

JavaScript vs. Python

# Python equivalent
lst = [1, 2, 3, 2, 1]
last_even = next((x for x in reversed(lst) if x % 2 == 0), None)
print(last_even)  # Output: 2

JavaScript vs. C#

// C# equivalent
var list = new List<int> { 1, 2, 3, 2, 1 };
var lastEven = list.FindLast(x => x % 2 == 0);
Console.WriteLine(lastEven); // Output: 2

Usage in TypeScript

TypeScript already provides type definitions for these new methods:

interface Array<T> {
  findLast<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S,
    thisArg?: any
  ): S | undefined;
  
  findLast(
    predicate: (value: T, index: number, array: T[]) => unknown,
    thisArg?: any
  ): T | undefined;
  
  findLastIndex(
    predicate: (value: T, index: number, array: T[]) => unknown,
    thisArg?: any
  ): number;
}

// Usage example
const numbers: number[] = [1, 2, 3, 4, 5];
const lastEven = numbers.findLast((n): n is number => n % 2 === 0);
console.log(lastEven); // 4

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.