Table of Contents#
- What is _.last()?
- Basic Syntax and Parameters
- Return Values and Behavior
- Basic Usage Examples
- Advanced Usage Patterns
- Common Use Cases
- Performance Considerations
- Comparison with Native JavaScript
- Best Practices
- Common Pitfalls and How to Avoid Them
- Conclusion
- References
What is _.last()?#
_.last() is a utility function provided by the Underscore.js library that returns the last element(s) of an array. It's part of Underscore's collection functions, designed to work seamlessly with arrays and array-like objects.
The function serves as a more readable and robust alternative to manual array indexing, especially when dealing with edge cases like empty arrays or when you need multiple elements from the end.
Basic Syntax and Parameters#
The _.last() function has two possible signatures:
_.last(array)
_.last(array, n)Parameters:
array(Array): The array to query (required)n(Number): The number of elements to return from the end (optional)
Return Values and Behavior#
The return value of _.last() depends on the arguments provided:
- When only
arrayis provided: Returns the last element of the array - When both
arrayandnare provided: Returns an array containing the lastnelements - Edge cases:
- Empty array returns
undefined nis 0 returns an empty array[]nexceeds array length returns the entire arraynis negative returns an empty array[]
- Empty array returns
Basic Usage Examples#
Getting the Last Element#
const _ = require('underscore');
// Basic usage
const numbers = [1, 2, 3, 4, 5];
const lastNumber = _.last(numbers);
console.log(lastNumber); // Output: 5
// With strings
const fruits = ['apple', 'banana', 'orange'];
const lastFruit = _.last(fruits);
console.log(lastFruit); // Output: 'orange'
// With objects
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const lastUser = _.last(users);
console.log(lastUser); // Output: { id: 3, name: 'Charlie' }Getting Multiple Last Elements#
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Get last 3 elements
const lastThree = _.last(numbers, 3);
console.log(lastThree); // Output: [8, 9, 10]
// Get last 5 elements
const lastFive = _.last(numbers, 5);
console.log(lastFive); // Output: [6, 7, 8, 9, 10]
// Edge case: n exceeds array length
const allElements = _.last(numbers, 15);
console.log(allElements); // Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Advanced Usage Patterns#
Chaining with Other Underscore Functions#
const data = [10, 25, 3, 42, 15, 8, 30, 7];
// Get the last 3 even numbers
const result = _.chain(data)
.filter(num => num % 2 === 0) // [10, 42, 8, 30]
.last(3) // [42, 8, 30]
.value();
console.log(result); // Output: [42, 8, 30]
// Get the last element after sorting
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 }
];
const oldestUser = _.chain(users)
.sortBy('age')
.last()
.value();
console.log(oldestUser); // Output: { name: 'Charlie', age: 35 }Working with Array-Like Objects#
// With function arguments (array-like object)
function processArgs() {
const lastArg = _.last(arguments);
console.log(lastArg);
}
processArgs('first', 'second', 'third'); // Output: 'third'
// With NodeList (from DOM)
// Assuming we have: <div class="item">1</div><div class="item">2</div><div class="item">3</div>
const items = document.querySelectorAll('.item');
const lastItem = _.last(items);
console.log(lastItem.textContent); // Output: '3'Safe Array Access Pattern#
function safeLastElement(arr, defaultValue = null) {
if (!Array.isArray(arr) || arr.length === 0) {
return defaultValue;
}
return _.last(arr);
}
// Safe usage examples
console.log(safeLastElement([1, 2, 3])); // Output: 3
console.log(safeLastElement([])); // Output: null
console.log(safeLastElement(null, 'Not found')); // Output: 'Not found'
console.log(safeLastElement(undefined, 0)); // Output: 0Common Use Cases#
1. Pagination and Data Display#
// Display last page of results
function getLastPageItems(items, itemsPerPage) {
const totalPages = Math.ceil(items.length / itemsPerPage);
const startIndex = (totalPages - 1) * itemsPerPage;
return _.last(items, items.length - startIndex);
}
const allItems = Array.from({length: 47}, (_, i) => `Item ${i + 1}`);
const lastPageItems = getLastPageItems(allItems, 10);
console.log(lastPageItems); // Last 7 items2. Recent Activity Tracking#
class ActivityLogger {
constructor(maxEntries = 100) {
this.activities = [];
this.maxEntries = maxEntries;
}
logActivity(activity) {
this.activities.push({
timestamp: new Date(),
activity: activity
});
// Keep only recent activities
if (this.activities.length > this.maxEntries) {
this.activities = _.last(this.activities, this.maxEntries);
}
}
getRecentActivities(count = 10) {
return _.last(this.activities, count);
}
}
const logger = new ActivityLogger(5);
logger.logActivity('User logged in');
logger.logActivity('Data updated');
logger.logActivity('File uploaded');
logger.logActivity('Settings changed');
logger.logActivity('Report generated');
console.log(logger.getRecentActivities(3));
// Last 3 activities3. Queue Implementation#
class FixedSizeQueue {
constructor(size) {
this.size = size;
this.items = [];
}
enqueue(item) {
this.items.push(item);
if (this.items.length > this.size) {
this.items = _.last(this.items, this.size);
}
}
getLast(n) {
return _.last(this.items, n);
}
getLastItem() {
return _.last(this.items);
}
}
const queue = new FixedSizeQueue(3);
queue.enqueue('Task 1');
queue.enqueue('Task 2');
queue.enqueue('Task 3');
queue.enqueue('Task 4'); // Pushes out 'Task 1'
console.log(queue.getLast(2)); // Output: ['Task 3', 'Task 4']Performance Considerations#
Time Complexity#
_.last() has a time complexity of O(1) when retrieving a single element and O(n) when retrieving multiple elements, where n is the number of elements requested.
Memory Efficiency#
// Efficient: Direct access for single element
const lastElement = _.last(largeArray); // O(1), memory efficient
// Less efficient: Creating new array for multiple elements
const lastHundred = _.last(largeArray, 100); // O(n), creates new array
// Alternative for very large arrays when you need multiple elements
function getLastNEfficient(arr, n) {
if (n <= 0) return [];
if (n >= arr.length) return arr.slice(); // Return copy
const result = new Array(n);
for (let i = 0; i < n; i++) {
result[i] = arr[arr.length - n + i];
}
return result;
}Comparison with Native JavaScript#
Native Alternatives#
const array = [1, 2, 3, 4, 5];
// Single element - Native vs Underscore
const nativeLast = array[array.length - 1];
const underscoreLast = _.last(array);
// Multiple elements - Native vs Underscore
const nativeLastN = array.slice(-3); // Last 3 elements
const underscoreLastN = _.last(array, 3);
// Edge case handling
const emptyArray = [];
console.log(emptyArray[emptyArray.length - 1]); // undefined
console.log(_.last(emptyArray)); // undefined
console.log(array.slice(-10)); // Entire array (when n > length)
console.log(_.last(array, 10)); // Entire arrayWhen to Use _.last() vs Native JavaScript#
Use _.last() when:
- You want more readable, self-documenting code
- You need consistent behavior across different data types
- You're already using Underscore.js in your project
- You want to chain with other Underscore functions
Use native JavaScript when:
- Performance is critical and you're dealing with very large arrays
- You want to avoid additional dependencies
- You're working in an environment where Underscore isn't available
Best Practices#
1. Always Validate Input#
// Good practice
function getSafeLast(array, n) {
if (!Array.isArray(array)) {
throw new TypeError('Expected an array');
}
if (typeof n !== 'undefined' && (typeof n !== 'number' || n < 0)) {
throw new TypeError('n must be a non-negative number');
}
return _.last(array, n);
}2. Use with Default Values#
// Provide sensible defaults
function getRecentItems(items, count = 5) {
return _.last(items, count) || [];
}
// Handle undefined results gracefully
const lastItem = _.last(someArray) || defaultValue;3. Combine with Other Utilities for Robust Code#
// Comprehensive data processing
function processRecentData(data, options = {}) {
const {
count = 10,
filterFn = null,
sortFn = null
} = options;
let result = data;
if (filterFn) {
result = _.filter(result, filterFn);
}
if (sortFn) {
result = _.sortBy(result, sortFn);
}
return _.last(result, count);
}Common Pitfalls and How to Avoid Them#
1. Assuming Array Input#
// ❌ Dangerous - will throw error if not array
function badGetLast(arr) {
return _.last(arr); // Throws if arr is null/undefined
}
// ✅ Safe - validate input
function safeGetLast(arr) {
if (!Array.isArray(arr)) {
return undefined;
}
return _.last(arr);
}2. Misunderstanding Return Types#
const array = [1, 2, 3];
// ❌ Confusing return types
const single = _.last(array); // number: 3
const multiple = _.last(array, 2); // array: [2, 3]
// ✅ Be explicit about what you expect
function getLastElement(arr) {
return _.last(arr);
}
function getLastElements(arr, count) {
return _.last(arr, count);
}3. Modifying Original Array#
const original = [1, 2, 3, 4, 5];
const lastThree = _.last(original, 3);
// ❌ This modifies the original array!
lastThree[0] = 999;
console.log(original); // [1, 2, 3, 4, 5] - Wait, it's not modified!
// Actually, _.last() returns a new array for multiple elements
// But be careful with object references:
const objects = [{id: 1}, {id: 2}, {id: 3}];
const lastObject = _.last(objects);
lastObject.id = 999; // This modifies the original object!
console.log(objects[2].id); // 999 - Original object modified!Conclusion#
The _.last() function in Underscore.js is a deceptively simple utility that offers significant benefits in terms of code readability, maintainability, and robustness. While it might seem trivial compared to native array access, its consistent behavior, edge case handling, and integration with Underscore's functional programming style make it a valuable tool in any JavaScript developer's toolkit.
Remember that the choice between _.last() and native alternatives depends on your specific context, performance requirements, and existing codebase. When used appropriately and following the best practices outlined in this guide, _.last() can help you write cleaner, more expressive, and more reliable code.
References#
- Underscore.js Official Documentation - _.last()
- MDN Web Docs - Array
- Underscore.js GitHub Repository
- JavaScript: The Good Parts by Douglas Crockford
- Functional Programming in JavaScript by Luis Atencio
Further Reading:
- Underscore.js source code for
_.last() - ECMAScript specification for Array methods
- Functional programming principles in JavaScript