Skip to content

JavaScript implementation of Rust's scan method for lists.

License

Notifications You must be signed in to change notification settings

icyJoseph/array-scan

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Scan

Inspired by Rust's iterator scan method.

scan, unlike reduce, does a fold where on every iteration, a piece of state is avaiable. This piece of state is not necessarily equal to the returned structural transformation.

This implementation of scan requires the fold function to return an object with keys, state, and value.

The value key is what the iteration returns at each step, while the state is available only to the fold.

This is similar to the way, the iterator protocol works, by requiring a next function that returns an object with keys done and value.

For example:

const arr = [0, 1, 2, 3];
const iterator = scan<State, Node, number>(
  arr,
  (prevState, current) => {
    const nextState = current % 2 === 0;
    const nextValue = prevState ? current : 2 * current;
    return { state: nextState, value: nextValue };
  },
  true
);

for (const element of iterator) {
  console.log(element);
} // 0, 1, 4, 3

The code above doubles an element if the previous element was not a multiple of two.

A more complicated use case, taken from the tests of this library.

Notice that scan supports method chaining with native map, filter and reduce.

enum Print {
  Comma,
  StartRange,
  EndRange,
  Skip,
  First,
}

type Node = {
  value: number;
  print: Print;
};

test('Range Extraction', () => {
  // assuming range is pre-sorted
  const range = [-6, -3, -2, -1, 0, 1, 4, 8, 14, 15, 17, 18, 19, 20];
  const expected = '-6,-3..1,4,8,14,15,17..20';

  const result = scan<boolean, Node, number>(
    range,
    (prevState, current, index, src) => {
      const prev = src[index - 1] ?? current;
      const next = src[index + 1] ?? current;

      const nextState = next - prev === 2;

      let print;

      if (nextState) {
        print = prevState ? Print.Skip : Print.StartRange;
      } else if (prevState) {
        print = Print.EndRange;
      } else {
        print = index === 0 ? Print.First : Print.Comma;
      }

      return {
        state: nextState,
        value: {
          value: current,
          print,
        },
      };
    },
    false
  ).reduce((acc, curr) => {
    switch (curr?.print) {
      case Print.First:
        return `${curr.value}`;

      case Print.Comma:
        return `${acc},${curr.value}`;

      case Print.StartRange:
        return `${acc}..`;

      case Print.EndRange:
        return `${acc}${curr.value}`;

      case Print.Skip:
      default:
        return acc;
    }
  }, '');

  expect(result).toEqual(expected);
});

About

JavaScript implementation of Rust's scan method for lists.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published