Permalink
0090060 May 19, 2016
@winterbe @erosb
1144 lines (799 sloc) 25.4 KB

API Documentation

Stream.js is a lightweight dependency-free utility library for ECMAScript 5 compatible JavaScript engines, such as browsers, Node.js or the Java 8 Nashorn engine. No native JavaScript types are modified in any way. Instead Stream.js exposes a single function Stream which serves as the constructor for creating and working with streams of in-memory data. It also serves as namespace for additional types such as Stream.Optional.

Naming Conventions

Stream.js is a functional programming library and follows strict naming conventions to describe different kind of function arguments. Please keep in mind that all functions passed as stream operations must be stateless. That means those functions should not modify the internal state of the streams underlying data structure (e.g. add or remove elements from the input array). Stateful functions may result in non-deterministic or incorrect behavior.

Predicate

Predicate functions accept a single element of the stream as the first argument and return a boolean.

var predicate = function(num) {
   return num % 2 === 1;
}
Mapping Function

Mapping functions accept a single element of the stream as the first argument and transform this argument into something else, returning another object.

var mappingFn = function(num) {
   return {a: num};
}
Supplier

Supplier functions return new objects out of nowhere. No arguments are passed.

var supplier = function() {
   return Math.random();
}
Consumer

Consumer functions accept a single element of the stream as the first argument and perform some custom actions, returning nothing.

var consumer = function (obj) {
   console.log("consuming", obj);
}
Comparator

Comparator functions accept two arguments and compare those arguments for order, returning 0, 1 or -1.

var defaultComparator = function (a, b) {
   if (a === b) {
      return 0;
   }
   return a > b ? 1 : -1;
};
Accumulator

Accumulator functions accept two arguments and return an object, e.g. by merging both arguments into a single result. Normally those functions are called multiple times for all elements of the stream, passing the last accumulator result as first argument and the current element of the stream as second argument.

var accumulator = function(result, obj) {
   return result + " " + obj;
}

Stream

Constructors

The following constructor functions can be used to create different kind of streams.

Stream(array)

Returns a new stream for the given array.

Stream([1, 2, 3, 4, 5])
   .filter(function(i) {
       return i % 2 === 1;
   })
   .toArray();    // => 1, 3, 5

Alias: Stream.from

Stream(objectHash)

Returns a new stream for the given object hash by streaming upon the values of each key.

Stream({a: 1, b: 2, c: 3, d: 4, e: 5})
   .filter(function(i) {
       return i % 2 === 1;
   })
   .toArray();    // => 1, 3, 5

Alias: Stream.from

Stream(set)

Returns a new stream for the given set.

// ES6 Set
var mySet = new Set([1, 2, 3, 4, 5]);

Stream(mySet)
   .filter(function(i) {
       return i % 2 === 1;
   })
   .toArray();    // => 1, 3, 5

Alias: Stream.from

Stream(map)

Returns a new stream for the given map.

// ES6 Map
var myMap = new Map();
data.set("key1", 1);
data.set("key2", 2);
data.set("key3", 3);
data.set("key3", 4);
data.set("key3", 5);

Stream(myMap)
   .filter(function(i) {
       return i % 2 === 1;
   })
   .toArray();    // => 1, 3, 5

Alias: Stream.from

Stream(iterator)

Returns a new stream for the given iterator. The iterator must conform to the Iterator Protocol.

// ES6 Generator
function* iterator() {
     yield 1;
     yield 2;
     yield 3;
     yield 4;
     yield 5;
     yield 6;
}

Stream(iterator())
   .filter(function(i) {
       return i % 2 === 1;
   })
   .toArray();    // => 1, 3, 5

Alias: Stream.from

Stream(string)

Returns a new stream for the given string. The string will be splitted by characters.

var stream = Stream("ABCDEFG");

Alias: Stream.from

Stream(string, separator)

Returns a new stream for the given string. The string will be split by the separator string.

var stream = Stream("a,b,c,d", ","); // creates a stream with 4 items

// creates a stream with 4 items - the trailing comma is not taken into account
stream = Stream("a,b,c,d,", ",");

Alias: Stream.from

Stream.of(args...)

Returns a new stream for the given arguments.

var stream = Stream.of(1, 2, 3, 4);
Stream.empty()

Returns a new empty stream.

var stream = Stream.empty();
Stream.range(startInclusive, endExclusive)

Returns a new stream for the given number range.

var stream = Stream.range(0, 10);   // => 0..9
Stream.rangeClosed(startInclusive, endInclusive)

Returns a new stream for the given number range.

var stream = Stream.rangeClosed(0, 9);    // => 0..9
Stream.generate(supplier)

Returns a new infinite stream. The elements of the stream are generated by the given supplier function.

// generate random numbers between 1 and 10
var stream = Stream.generate(function() {
   return Math.floor(Math.random() * 10) + 1;
});
Stream.iterate(seed, fn)

Returns a new infinite stream. The elements of the stream are generated by utilizing the given iterator function, starting with seed at n=0, then calling fn(seed) for n=1, fn(fn(seed)) for n=2 etc.

// 1, 2, 4, 8, 16, 32, ...
var stream = Stream.iterate(1, function (num) {
   return num * 2;
});

Intermediate Operations

Intermediate operations all return a stream, thus enabling you to chain multiple operations one after another without using semicolons.

filter(predicate)

Filters the elements of the stream to match the given predicate function and returns the stream.

Stream([1, 2, 3, 4, 5])
   .filter(function (num) {
      return num % 2 === 1;   // => 1, 3, 5
   });
filter(regexp)

Filters the elements of the stream to match the given regular expression and returns the stream.

// starts with a
Stream(["a1", "a2", "b1"])
   .filter(/a.*/);   // => a1, a2
filter(sample)

Filters the elements of the stream to match the given sample object and returns the stream.

var data = [
  {a: 1, b: 1},
  {a: 2, b: 2},
  {a: 1, b: 3}
];

Stream(data)
   .filter({a: 1});

// => {a: 1, b: 1}, {a: 1, b: 3}
filterNull()

Filters (removes) the null values of the stream. It performs a strongly typed check, so it keeps every other falsy values.

Stream([1, null, false, NaN, undefined, 0, ""])
  .filterNull()
// => 1, false, NaN, undefined, 0, ""
filterFalsy()

Filters (removes) the falsy values of the stream.

Stream([1, false, 2, null, NaN, undefined, 0, ""])
  .filterFalsy()
// => 1, 2
map(mappingFn)

Applies the given mapping function to each element of the stream and returns the stream.

Stream(numbers)
   .map(function (num) {
      return {a: num};
   });
map(path)

Maps each element of the stream by resolving the given string path and returns the stream.

E.g. map('a.b.c') is equivalent to map(function(obj) { return obj.a.b.c; }).

Stream([{foo: {bar: 'foobar'}}, ...])
   .map('foo.bar');
flatMap(mappingFn)

Applies the given mapping function to each element of the stream and flattens the results by replacing each element with the contents of the element in case of collections, then returns the stream.

Stream([{nums: [1, 2, 3], ...])
   .flatMap(function(obj) {
      return obj.nums;
   });
flatMap(path)

Maps each element of the stream by resolving the given string path and flattens the results by replacing each elemet with the contents of the element in case of collections, then returns the stream.

E.g. flatMap('a.b.c') is equivalent to flatMap(function(obj) { return obj.a.b.c; }).

Stream([{foo: {bar: [1, 2, 3]}}, ...])
   .map('foo.bar');
sorted()

Sorts the elements of the stream according to the natural order and returns the stream.

Alias: sort

Stream([7, 3, 3, 1])
   .sorted();  // => 1, 3, 3, 7
sorted(comparator)

Sorts the elements of the stream according to the given comparator and returns the stream.

Alias: sort

Stream([{foo: 'bar'}, ...])
   .sorted(function(a, b) {
      if (a.foo === b.foo) return 0;
      if (a.foo < b.foo) return -1;
      return 1;
   });
sorted(path)

Sorts the elements of the stream according to the given string path and returns the stream.

Alias: sort

var data = [{a: 4}, {a: 1}, {a: 3}, {a: 2}];
Stream(data)
   .sorted("a");
// => {a: 1}, {a: 2}, {a: 3}, {a: 4}
shuffle()

Randomly reorder all elements of the stream.

Stream([1, 2, 3, 4])
   .shuffle();   // => 4, 1, 3, 2
reverse()

Reverse the order of each elements of the stream.

Stream([1, 2, 3, 4])
   .reverse();   // => 4, 3, 2, 1
distinct()

Returns the stream consisting of the distinct elements of this stream.

Stream([1, 1, 2, 3, 3, 4])
   .distinct();   // => 1, 2, 3, 4
limit(maxSize)

Truncates the elements of the stream to be no longer than maxSize and returns the stream.

Stream([1, 2, 3, 4])
   .limit(2);        // => 1, 2
skip(n)

Discards the first n elements and returns the stream.

Stream([1, 2, 3, 4])
   .skip(2);         // => 3, 4
slice(begin, end)

Discards all elements except those between the zero-based indices begin (included) and end (excluded).

Stream([1, 2, 3, 4])
   .slice(1, 3);         // => 2, 3
peek(consumer)

Performs the consumer function for each element and returns the stream.

Stream([1, 2, 3, 4])
   .peek(function (num) {
      console.log(num);
   });
takeWhile(predicate)

Takes all elements of the stream as long as predicate is true. All elements are rejected from the moment predicate is false for the first time.

Stream([1, 2, 3, 2, 1])
     .takeWhile(function (num) {
         return num < 3;
     })
     .toArray();  // => 1, 2
takeWhile(regexp)

Takes all elements of the stream as long as regexp matches. All elements are rejected from the moment regexp doesn't match for the first time.

Stream(["a1", "a2", "b3", "a4"])
     .takeWhile(/a.*/)
     .toArray();  // => a1, a2
takeWhile(sample)

Takes all elements of the stream as long as sample matches. All elements are rejected from the moment sample doesn't match for the first time.

var data = [
     {a: 1, b: 1},
     {a: 1, b: 2},
     {a: 2, b: 3},
     {a: 1, b: 4}
];

Stream(data)
     .takeWhile({a: 1})
     .toArray();  // => {a: 1, b: 1}, {a: 1, b: 2}
dropWhile(predicate)

Rejects all elements of the stream as long as predicate is true. All elements are accepted from the moment predicate is false for the first time.

Stream([1, 2, 3, 2, 1])
     .dropWhile(function (num) {
         return num < 3;
     })
     .toArray();  // => 3, 2, 1
dropWhile(regexp)

Rejects all elements of the stream as long as regexp matches. All elements are accepted from the moment regexp doesn't match for the first time.

Stream(["a1", "a2", "b3", "a4"])
     .dropWhile(/a.*/)
     .toArray();  // => b3, a4
dropWhile(sample)

Rejects all elements of the stream as long as sample matches. All elements are accepted from the moment sample doesn't match for the first time.

var data = [
     {a: 1, b: 1},
     {a: 1, b: 2},
     {a: 2, b: 3},
     {a: 1, b: 4}
];

Stream(data)
     .dropWhile({a: 1})
     .toArray();  // => {a: 2, b: 3}, {a: 1, b: 4}

Terminal Operations

Terminal operations return a result (or nothing), so each streaming pipeline consists of 0 to n intermediate operations followed by exactly one terminal operation.

toArray()

Returns an array containing the elements of the stream.

Alias: toList

var result = Stream([1, 2, 3, 4, 5])
   .filter(function (num) {
      return num % 2 === 1;
   })
   .toArray();   // => [1, 3, 5]
forEach(consumer)

Performs the consumer function for each element of the stream.

Alias: each

Stream([1, 2, 3, 4])
   .forEach(function (num) {
      console.log(num);
   });
findFirst()

Returns an Optional wrapping the first element of the stream or Optional.empty() if the stream is empty.

Alias: findAny

Stream([1, 2, 3, 4])
   .findFirst()
   .ifPresent(function (first) {
      console.log(first);  // => 1
   });
min()

Returns an Optional wrapping the minimum element of the stream (according the natural order) or Optional.empty() if the stream is empty.

Stream([4, 1, 3, 2])
   .min()
   .ifPresent(function (min) {
      console.log(min);  // => 1
   });
min(comparator)

Returns an Optional wrapping the minimum element of the stream (according the given comparator function) or Optional.empty() if the stream is empty.

Stream([{a: 3}, {a: 1}, {a: 2}])
   .min(function (obj1, obj2) {
      if (obj1.a === obj2.a) return 0;
      return obj1.a > obj2.a ? 1 : -1;
   })
   .ifPresent(function (min) {
      console.log(min);    // => {a: 1}
   });
min(path)

Returns an Optional wrapping the minimum element of the stream (according the given string path) or Optional.empty() if the stream is empty.

Stream([{a: 3}, {a: 1}, {a: 2}])
   .min("a")
   .ifPresent(function (min) {
      console.log(min);    // => {a: 1}
   });
max()

Returns an Optional wrapping the maximum element of the stream (according the natural order) or Optional.empty() if the stream is empty.

Stream([4, 1, 3, 2])
   .max()
   .ifPresent(function (min) {
      console.log(min);  // => 1
   });
max(comparator)

Returns an Optional wrapping the maximum element of the stream (according the given comparator function) or Optional.empty() if the stream is empty.

Stream([{a: 3}, {a: 1}, {a: 2}])
   .max(function (obj1, obj2) {
      if (obj1.a === obj2.a) return 0;
      return obj1.a > obj2.a ? 1 : -1;
   })
   .ifPresent(function (min) {
      console.log(min);    // => {a: 3}
   });
max(path)

Returns an Optional wrapping the maximum element of the stream (according the given string path) or Optional.empty() if the stream is empty.

Stream([{a: 3}, {a: 1}, {a: 2}])
   .min("a")
   .ifPresent(function (min) {
      console.log(min);    // => {a: 3}
   });
sum()

Returns the sum of all elements in this stream.

Stream([1, 2, 3, 4])
   .sum();     // => 10
sum(path)

Returns the sum of all elements for the given path in this stream.

Stream([{a: 1}, {a: 2}, {a: 3}])
   .sum("a");     // => 6
average()

Returns an Optional wrapping the arithmetic mean of all elements of this stream or Optional.empty() if the stream is empty.

Alias: avg

Stream([1, 2, 3, 4])
   .average()
   .ifPresent(function (avg) {
      console.log(avg);    // => 2.5
   });
average(path)

Returns an Optional wrapping the arithmetic mean of all elements of this stream or Optional.empty() if the stream is empty, by resolving the given path for each element.

Alias: avg

Stream([{a: 1}, {a: 2}, {a: 3}, {a: 4}])
   .average("a")
   .ifPresent(function (avg) {
      console.log(avg);    // => 2.5
   });
count()

Returns the number of elements of the stream.

Alias: size

Stream([1, 2, 3, 4])
   .count();     // => 4
allMatch(predicate)

Returns whether all elements of the stream match the given predicate function.

Stream([1, 2, 3, 4])
   .allMatch(function (num) {
      return num > 1;   // => false
   });
allMatch(sample)

Returns whether all elements of the stream match the given sample object.

Stream([{a: 1}, {a: 2}])
   .allMatch({a: 1});   // => false
allMatch(regexp)

Returns whether all elements of the stream match the given regexp pattern.

Stream(["a1", "a2", "b3"])
   .allMatch(/a.*/);    // => false
anyMatch(predicate)

Returns whether any element of the stream matches the given predicate function.

Stream([1, 2, 3, 4])
   .anyMatch(function (num) {
      return num > 1;   // => true
   });
anyMatch(sample)

Returns whether any element of the stream match the given sample object.

Stream([{a: 1}, {a: 2}])
   .anyMatch({a: 1});   // => true
anyMatch(regexp)

Returns whether any element of the stream matches the given regexp pattern.

Stream(["a1", "a2", "b3"])
   .anyMatch(/a.*/);    // => true
noneMatch(predicate)

Returns whether no element of the stream matches the given predicate function.

Stream([1, 2, 3, 4])
   .noneMatch(function (num) {
      return num > 1;   // => false
   });
allMatch(sample)

Returns whether no element of the stream match the given sample object.

Stream([{a: 1}, {a: 2}])
   .noneMatch({a: 1});   // => false
noneMatch(regexp)

Returns whether no element of the stream matches the given regexp pattern.

Stream(["a1", "a2", "a3"])
   .noneMatch(/b.*/);    // => true
reduce(identity, accumulator)

Performs a reduction operation using the provided identity object as initial value and the accumulator function and returns the reduced value.

Stream([1, 2, 3, 4])
   .reduce(1000, function (result, num) {
      return result + num;    // => 1010
   });
reduce(accumulator)

Performs a reduction operation using the first element of the stream as as initial value and the accumulator function and returns the reduced value wrapped as Optional.

Stream([1, 2, 3, 4])
   .reduce(function (result, num) {
      return result + num;
   })
   .ifPresent(function (result) {
      console.log(result);    // => 10
   });
collect(collector)

Performs a generalized reduction operation denoted by the given collector and returns the reduced value. A collector consists of a supplier function, an accumulator function and an optional finisher function.

Stream([1, 2, 3, 4])
   .collect({
      supplier: function () {
         return "Data: ";
      },
      accumulator: function (val, num) {
         return val + num + " ";
      },
      finisher: function (val) {
         return val + "!";
      }
   });
// => Data: 1 2 3 4 !
groupingBy(keyMapper)

Groups all elements of the stream by applying the given keyMapper function and returns an object map, assigning an array value for each key.

Alias: groupBy

Stream([{a: "foo", b: 1}, {a: "bar", b: 2}, {a: "bar", b: 3}])
  .groupBy(function (obj) {
      return obj.a;
  });

// => { foo: [{a: "foo", b: 1}], bar: [{a: "bar", b: 2}, {a: "bar", b: 3}] }
groupingBy(path)

Groups all elements of the stream by resolving the given string path for each element of the stream and returns an object map, assigning an array value for each key.

Stream([{a: "foo", b: 1}, {a: "bar", b: 2}, {a: "bar", b: 3}])
  .groupBy('a');

// => { foo: [{a: "foo", b: 1}], bar: [{a: "bar", b: 2}, {a: "bar", b: 3}] }

Alias: groupBy

toMap(keyMapper, mergeFunction)

Groups all elements of the stream by applying the given keyMapper function and returns an object map, assigning a single value for each key. Multiple values for the same key will be merged using the given merge function. The second argument mergeFunction is not required. If no merge function is present and multiple values are found for the same key, an error is thrown.

Alias: indexBy

Stream([{a: "foo", b: 1}, {a: "bar", b: 2}])
  .toMap(function (obj) {
      return obj.a;
  });

// => { foo: {a: "foo", b: 1}, bar: {a: "bar", b: 2} }
toMap(path, mergeFunction)

Groups all elements of the stream by resolving the given path for each element of the stream and returns an object map, assigning a single value for each key. Multiple values for the same key will be merged using the given merge function. The second argument mergeFunction is not required. If no merge function is present and multiple values are found for the same key, an error is thrown.

Alias: indexBy

Stream([{a: "foo", b: 1}, {a: "bar", b: 2}])
  .toMap("a");

// => { foo: {a: "foo", b: 1}, bar: {a: "bar", b: 2} }
partitioningBy(predicate)

Groups all elements of the stream by the results of applying the given predicate to each element of the stream, returning an object with two keys true and false.

Alias: partitionBy

Stream([1, 2, 3, 4])
   .partitionBy(function (num) {
      return num % 2 === 0;
   });

// => {"true": [2, 4], "false": [1, 3]}
partitioningBy(sample)

Groups all elements of the stream by the results of matching the given sample object to each element of the stream, returning an object with two keys true and false.

Alias: partitionBy

var data = [
   {a: 1, b: 1},
   {a: 2, b: 1},
   {a: 3, b: 5}
];
Stream(data)
   .partitionBy({b: 1});
// => {"true": [{a: 1, b: 1}, {a: 2, b: 1}], "false": [{a: 3, b: 5}]}
partitioningBy(regexp)

Groups all elements of the stream by the results of testing the given regexp pattern to each element of the stream, returning an object with two keys true and false.

Alias: partitionBy

Stream(["a1", "a2", "b1"])
   .partitionBy(/a.*/);

// => {"true": ["a1", "a2"], "false": ["b1"]}
partitioningBy(size)

Groups all elements of the stream by chunks of the given size, returning an array of arrays.

Alias: partitionBy

Stream([1, 2, 3, 4])
   .partitionBy(2);

// => [[1, 2,], [3, 4]]
joining()

Joins all elements of the stream into a string.

Alias: join

Stream([1, 2, 3, 4])
   .joining();    // => "1234"
joining(delimiter)

Joins all elements of the stream into a string, using the given delimiter.

Alias: join

Stream([1, 2, 3, 4])
   .joining("-");    // => "1-2-3-4"
joining(options)

Joins all elements of the stream into a string, using the following non-required options: options.delimiter, options.prefix, options.suffix.

Alias: join

Stream([1, 2, 3, 4])
   .joining({
      prefix: "PREFIX_",
      suffix: "_SUFFIX",
      delimiter: ","
   });
// => "PREFIX_1,2,3,4_SUFFIX"
iterator()

Returns a StreamIterator to traverse upon the elements described by this stream. StreamIterator defines a single method next, returning an object holding the next value and a boolean flag done indicating if the iterator has been finished.

var iterator = stream.iterator(), result;
while (true) {
   result = iterator.next();
   console.log(result.value);
   if (result.done) {
      break;
   }
}

Stream.Optional

Wraps a single value which may be null or undefined.

Constructors

Optional.of(value)

Creates a new optional wrapper for the given non-null and non-undefined value.

Stream.Optional.of(1);
Optional.ofNullable(value)

Creates a new optional wrapper for the given value. The value may be null or undefined.

Stream.Optional.ofNullable(null);
Optional.empty()

Creates a new empty optional wrapper (same as Optional.ofNullable(null)).

Stream.Optional.empty();

Intermediate Operations

filter(predicate)

If a value is present and value matches the given predicate function, returns this optional, otherwise return an empty optional.

var optional = Stream.Optional.of({a: 23})
   .filter(function (obj) {
      return obj.a % 2 === 0;
   });
optional.isPresent();   // => false
map(mappingFn)

If a value is present apply the given mapping function and wrap the resulting value with an optional.

var optional = Stream.Optional.of({a: 23})
   .map(function (obj) {
      return obj.a;
   });
optional.get();   // => 23
flatMap(mappingFn)

If a value is present apply the given optional-returning mapping function, and return the optional result or empty.

var optional = Stream.Optional.of({a: 23})
   .flatMap(function (obj) {
      return Stream.Optional.of(obj.a);
   });
optional.get();   // => 23

Terminal Operations

isPresent()

Check if a value is present.

var optional = Stream.Optional.of(23);
optional.isPresent();   // => true
get()

Returns the value if a value is present, otherwise throws an error.

var optional = Stream.Optional.of(23);
optional.get();   // => 23
ifPresent(consumer)

Invoke the given consumer function for the specified value if present.

Stream.Optional.of(23)
   .ifPresent(function (num) {
      console.log(num);     // => 23
   });
orElse(other)

Returns the value if present, otherwise return the given other value.

var optional = Stream.Optional.empty();
optional.orElse(3);   // => 3
orElseGet(supplier)

Returns the value if present, otherwise return the result of invoking the supplier function.

var optional = Stream.Optional.empty();
optional.orElseGet(function () {
   return 1337;
});
orElseThrow(error)

Returns the value if present, otherwise throw the given error.

var optional = Stream.Optional.empty();
optional.orElseThrow("something went wrong");