loading...

Get immutable versions of Array methods

azu profile image azu ・3 min read

Have you ever thought about immutable version of Array.prototype methods?
For example, Array.prototype.push return a new Array instead of number of items.

I've created immutable-array-prototype package.

Why immutable?

ECMAScript Array has some mutable methods.

Mutable vs Immutable

For example, Redux require Immutable Update on your Store implementation.

Object.assign or object spread({ ...object }) can realize immutable updating for Object. But, immutale updating on Array is difficult.

Of course, Immutable.js is useful. But Immutable.js is very large library.

So, I want to get minimal and easy to use library.

@immutable-array/prototype

@immutable-array/prototype includes immutable version of theses Array.prototype methods.

npm install @immutable-array/prototype

Or, If you want to a single method, you can use a method as independent package.

npm install @immutable-array/pop
npm install @immutable-array/push
npm install @immutable-array/shift
npm install @immutable-array/unshift
npm install @immutable-array/sort
npm install @immutable-array/reverse
npm install @immutable-array/fill
npm install @immutable-array/splice
npm install @immutable-array/copy-within

Example

This library provide same API and behavior without first argument.

import {
  sort,
  unshift,
  push,
  fill,
  splice,
  pop,
  reverse,
  copyWithin,
  shift
} from '@immutable-array/prototype';
describe('prototype', () => {
  it('shift', () => {
    assert.deepStrictEqual(shift(['a', 'b', 'c', 'd', 'e']), [
      'b',
      'c',
      'd',
      'e'
    ]);
  });
  it('unshift', () => {
    assert.deepStrictEqual(unshift(['a', 'b', 'c', 'd', 'e'], 'x'), [
      'x',
      'a',
      'b',
      'c',
      'd',
      'e'
    ]);
  });
  it('pop', () => {
    assert.deepStrictEqual(pop(['a', 'b', 'c', 'd', 'e']), [
      'a',
      'b',
      'c',
      'd'
    ]);
  });
  it('push', () => {
    assert.deepStrictEqual(push(['a', 'b', 'c', 'd', 'e'], 'x'), [
      'a',
      'b',
      'c',
      'd',
      'e',
      'x'
    ]);
  });
  it('splice', () => {
    assert.deepStrictEqual(splice(['a', 'b', 'c', 'd', 'e'], 0, 1, 'x'), [
      'x',
      'b',
      'c',
      'd',
      'e'
    ]);
  });
  it('sort', () => {
    assert.deepStrictEqual(sort(['e', 'a', 'c', 'b', 'd']), [
      'a',
      'b',
      'c',
      'd',
      'e'
    ]);
  });
  it('reverse', () => {
    assert.deepStrictEqual(reverse(['a', 'b', 'c', 'd', 'e']), [
      'e',
      'd',
      'c',
      'b',
      'a'
    ]);
  });
  it('fill', () => {
    assert.deepStrictEqual(fill(new Array(5), 'x'), ['x', 'x', 'x', 'x', 'x']);
  });
  it('copyWithin', () => {
    assert.deepStrictEqual(copyWithin(['a', 'b', 'c', 'd', 'e'], 0, 3, 4), [
      'd',
      'b',
      'c',
      'd',
      'e'
    ]);
  });
});

Use Case

Faao that is GitHub Issue application use @immutable-array/prototype for creating domain model.
Faao apply DDD/CQRS pattern using Almin. Immutable domain model help to work application in safety.

Policy

@immutable-array/prototype has a support policy.

Do

  • Provide immutable version of Array.prototype method
  • Provide each method as an module
    • For exaple, import push from "@immutable-array/push"
    • All prototype method: import { push } from "@immutable-array/prototype"
  • ECMAScript compatible API without first arguments

For example, @immutable-array/* method should return same result with native API.

import { splice } from '@immutable-array/splice';
var array = [1, 2, 3];
// immutable
var resultArray = splice(array, -1, 1, 'x');
// native
array.splice(-1, 1, 'x');
assert.deepStrictEqual(array, resultArray);

Do not

  • Should not add non-standard method in ECMAScript
    • For example, It does not provide update, delete, merge methods.
  • Each method should not depended on other method

Finally

Pull requests and stars are always welcome :)

Discussion

pic
Editor guide
Collapse
prodigalknight profile image
RevanProdigalKnight

pop, push, shift, unshift, and splice are not, and never were intended to be, immutable methods. The intended use of these is to allow JavaScript arrays to emulate a Stack or Queue object without needing to actually define a separate object in the language. Notably, pop, shift, and splice return the element(s) removed from the array, if any, so that they can be used in certain kinds of loops, i.e.:

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

while (arr.length > 0) {
  const element = arr.pop();

  if (element % 2 === 0) {
    arr.push(element + 1);
  }
}

Now, this is a rather simplistic example, but when trying to navigate the layers of a tree in a dynamic fashion (rather than using recursion), the pattern becomes very useful:

const nodes = [node];

while (nodes.length > 0) {
  const n = nodes.pop();

  /* do something with the node */

  if (node.hasChildren()) {
    nodes.push(...node.getChildren());
  }
}

Your immutable replacements for this would not be usable in this case as they are now.

I would suggest that your immutable replacements for these functions return an array consisting of the new array in the first element and then what the result of the non-immutable original function would be in the second element, e.g.:

import { pop } from '@immutable-array/pop';
import { push } from '@immutable-array/push';
const arr = [1, 2, 3, 4, 5];

const popResult = pop(arr);

console.log(popResult); // [[1, 2, 3, 4], 5]

const pushResult = push(arr, 6);

console.log(pushResult); // [[1, 2, 3, 4, 5, 6], 6]