Free email newsletter: “ES.next News

2014-06-14

Multiple return values in ECMAScript 6

If you combine the features “property value shorthand” and “destructuring”, ECMAScript 6 gives you an elegant way to handle multiple return values. This blog post explains why that is useful and how it works.

Destructuring

Destructuring is an ECMAScript 6 feature which lets you use patterns to extract values from an object:

    let obj = { first: 'Jane', last: 'Doe' };
    
    let { first: f, last: l } = obj;
    // f = 'Jane', l = 'Doe'

Destructuring also works for arrays:

    let [x, y] = ['a', 'b']; // x='a', y='b'
    
    [a, b] = [b, a];  // swap values
    
    let [all, year, month, day] =
        /^(\d\d\d\d)-(\d\d)-(\d\d)$/
        .exec('2999-12-31');

That is, object literals and array literals enable us to construct compound values and destructuring enables us to take them apart. It can be used in the following locations:

    // Variable declarations:
    let [x] = ['a'];
    
    // Assignments:
    [x] = ['a'];
    
    // Parameter definitions:
    function f([x]) { ... }
    f(['a']);

Multiple return values

To see the usefulness of multiple return values, let’s implement a function findElement(a, p) that searches for the first element in the array a for which the function p returns true. The question is: what should that function return? Sometimes one is interested in the element itself, sometimes in its index, sometimes in both. The following is an implementation of the last use case.

    function findElement(array, predicate) {
        for (var index=0; index < array.length; index++) {
            var element = array[index];
            if (predicate(element)) {
                return { element: element, index: index };
            }
        }
        return { element: undefined, index: -1 };
    }

I’ve decided in favor of returning the element and the index via an object and against returning them via an array, because the labeling of the values in an object makes things more self-descriptive. Let’s rewrite the function and use some ECMAScript 6 features:

    function findElement(array, predicate) {
        for (let [index, element] of array.entries()) { // (1)
            if (predicate(element)) {
                return { element, index }; // (2)
            }
        }
        return { element: undefined, index: -1 };
    }

In line (1), we have used:

  • the new for-of loop [1]
  • a block-scoped let variable declaration
  • the method Array.prototype.entries() which returns a sequence of [index, element] pairs
  • array destructuring to conveniently access the components of those pairs

In line (2), we have used so-called property value shorthands. The following two expressions are equivalent.

    { element,          index        }
    { element: element, index: index }

Now it is time to use the function. In ECMAScript 5, finding the first even number in an array looks as follows.

    var a = [7, 8, 6];
    var result = findElement(a, function (x) {
        return x % 2 === 0
    });
    var element = result.element; // 8
    var index = result.index; // 1

In ECMAScript 6, less code is needed:

    let a = [7, 8, 6];
    let {element, index} = findElement(a, x => x % 2 === 0);
    // element = 8, index = 1

We use property value shorthands and object destructuring to assign values to element and index. Additionally, the predicate is less verbosely specified via an arrow function [2].

Due to index and element also referring to property keys, the order in which we mention them doesn’t matter:

    let {index, element} = findElement(...);

We have successfully handled the case of needing both index and element. What if we are only interested in one of them? It turns out that, thanks to ECMAScript 6, our implementation can take care of that, too, with minimal syntactic overhead:

    let a = [7, 8, 6];
    
    let {element} = findElement(a, x => x % 2 === 0);
    // element = 8
    
    let {index} = findElement(a, x => x % 2 === 0);
    // index = 1

Each time, we only extract the value of the one property that we need.

References

  1. Iterators and generators in ECMAScript 6
  2. ECMAScript.next: arrow functions and method definitions

No comments: