Array.create

Abstract

JavaScript doesn't have a canonical function to create an array. This is a pity, because it discourages developers from adopting a functional style of programming. I propose that we add an Array.create function to the language. This will lead to cleaner code and the building of better abstractions.

Background

Traditionally, creation of arrays in JavaScript was done using procedural code. Then, with ES6 we got the Array.from function which made it easier to create arrays in a functional style. However, the canonical purpose of Array.from is converting iterables into arrays. Therefore, using it to create arrays is wonky.

// Array.create :: (Int, Int -> a) -> Array a
Array.create = (length, mapping) =>
    Array.from({ length }, (value, index) => mapping(index));

It would be nicer to have a canonical function to create arrays, such as the Array.create function shown above. Due to a lack of such a canonical function, we currently have packages such as create-array in the NPM ecosystem. This leads to package bloat and possible code duplication as developers reinvent the wheel in every new project.

Motivation

Another reason to include the Array.create function would be to encourage developers to build better abstractions. For example, we can use Array.create to create and transpose matrices easily.

class Matrix {
    // new Matrix :: (Int, Int, Array (Array a)) -> Matrix a
    constructor(rows, cols, data) {
        this.rows = rows;
        this.cols = cols;
        this.data = data;
    }

    // Matrix.create :: (Int, Int, (Int, Int) -> a) -> Matrix a
    static create(rows, cols, mapping) {
        const data = Array.create(rows, row =>
            Array.create(cols, col => mapping(row, col)));
        return new Matrix(rows, cols, data);
    }

    // Matrix#transpose :: Matrix a ~> () -> Matrix a
    transpose() {
        const { rows, cols, data } = this;
        return Matrix.create(cols, rows, (row, col) => data[col][row]);
    }
}

Conclusion

The Array.create function is very useful. It encourages a functional style of programming, we can use it to build better abstractions, and it would prevent developers from reinventing the wheel in every project.

2 Likes

Alternative iterator based proposals: GitHub - tc39/proposal-Number.range: A proposal for ECMAScript to add a built-in Number.range() and GitHub - tc39/proposal-iterator-helpers: Methods for working with iterators in ECMAScript would also allow this.

They could also be used to implement Array.create

Array.create = (length, mapping) =>
    [...Number.range(0, length).map(mapping)]

there’s also the old pre-ES6 proposal http://array.build

2 Likes

@aclaymore I would argue that it still makes sense to add Array.create (or Array.build as @ljharb shared) to the language for the following reasons.

  1. It's such a primitive operation that it warrants a language level implementation. There's no reason why we can't have both Array.create and Number.range in the language.
  2. By adding Array.create to the language, we won't require 3rd party packages like create-array and we won't have to keep reinventing the wheel in every project.
  3. By adding Array.create to the language, more developers will become aware of it and use it in their programs. Hence, it will promote a functional style of programming.
  4. Implementing Array.create in the user space (whether using Array.from or Number.range) doesn't address any of the above issues.

The motivation for adding Array.create to the language is the same as the motivation for adding Number.range to the language, as stated by the proposal that you shared.

  • because we don't have it yet™

Both Array.create and Number.range are primitive operations that can be implemented in the user space, but should be provided by the language itself.

There's an advantage to using your method, though: engines can pre-allocate the full length in one go, making it slightly faster.