javascriptroom blog

Mastering the _.last() Function in Underscore.js: A Comprehensive Guide

In the world of JavaScript development, working with arrays is an everyday task. Whether you're processing data, managing application state, or manipulating collections, you frequently need to access specific elements. This is where utility libraries like Underscore.js shine, providing elegant solutions to common programming challenges.

One of the most frequently used functions in Underscore.js is _.last(), a simple yet powerful utility for retrieving elements from the end of an array. While it might seem trivial at first glance, understanding _.last() thoroughly can significantly improve your code's readability, maintainability, and performance.

This comprehensive guide will take you from the basics of _.last() to advanced usage patterns, covering everything you need to know to master this essential function.

2026-06

Table of Contents#

  1. What is _.last()?
  2. Basic Syntax and Parameters
  3. Return Values and Behavior
  4. Basic Usage Examples
  5. Advanced Usage Patterns
  6. Common Use Cases
  7. Performance Considerations
  8. Comparison with Native JavaScript
  9. Best Practices
  10. Common Pitfalls and How to Avoid Them
  11. Conclusion
  12. 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 array is provided: Returns the last element of the array
  • When both array and n are provided: Returns an array containing the last n elements
  • Edge cases:
    • Empty array returns undefined
    • n is 0 returns an empty array []
    • n exceeds array length returns the entire array
    • n is negative returns an empty array []

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: 0

Common 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 items

2. 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 activities

3. 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 array

When 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#

  1. Underscore.js Official Documentation - _.last()
  2. MDN Web Docs - Array
  3. Underscore.js GitHub Repository
  4. JavaScript: The Good Parts by Douglas Crockford
  5. 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