artwork: https://code-art.pictures/
Imagine a library:
- like in photo frameprovides many useful functions for iterative operations
- Reads familiar, like a standard array iterative method
- No attempt is made to replace JavaScript built-in functions, such as
Array
,Set
,or iteration protocol - Like Java Streams, including Elective Clearly distinguish between “worthless” and “
undefined
as a value” - Compact size of bundle
I couldn’t find a library that could satisfy all these needs, so I created my small library – smooth smooth.
Talk is cheap. show code
The 3 most common words in the text
const words = ['Lorem', 'ipsum', /* ... */]
stream(words)
.groupBy(word => word.toLowerCase())
.map(([word, list]) => [word, list.length])
.sortBy(([, length]) => -length)
.take(3)
.toArray()
// => ['ut', 3], ['in', 3], ['dolor', 2]
Relatively prime integers
// Endless stream of 2..999 integers
const randomInts = continually(() =>
2 + Math.floor(Math.random() * 998)
)
randomInts
.zip(randomInts)
.filter(([a, b]) => gcd(a, b) === 1)
.distinctBy(pair => stream(pair).sortBy(i => i).join())
.take(10)
// => [804, 835], [589, 642], [96, 145], ...
- Streams can be unlimited as shown, but you can limit them to the first
n
Items usedtake(n)
method - Streams can be reused multiple times, even in parallel. This is because streams are stateless and only store references to inputs. State is only established when the stream produces an iterator.
Generate a deck of cards
const deck = streamOf('♠', '♥', '♣', '♦')
.flatMap(suit =>
streamOf<string | number>(
'A',
...range(2, 11),
'J',
'Q',
'K'
).map(rank => `${rank}${suit}`)
)
// => 'A♠', '2♠', '3♠', ...
And play Texas holdem poker!
const playersNum = 2
const [flop, turn, river, ...hands] = deck
.takeRandom(3 + 1 + 1 + playersNum * 2)
.zipWithIndex()
.splitWhen((_, [, j]) =>
j === 3 // flop
|| j === 4 // turn
|| j >= 5 // river
&& j % 2 === 1 // ...players' hands
)
.map(chunk =>
// Unzip index
chunk.map(([card]) => card)
)
// flop = ['3♦', '9♣', 'J♦']
// turn = ['4♣']
// river = ['7♦']
// hands = ['J♠', '4♥'], ['10♠', '8♥']
The first player flopped a pair of jacks, while the second player got a straight on the river. Who will win?
this must be cheap
All of the above can be achieved using only native data structures. However, code written using Fluent Streams reads better. While making code more readable is a perfectly valid goal, the cost of achieving it should be low in terms of cognitive load, package size, and performance.
Fluent Streams does just that! Here’s why:
- No learning curve:The API feels familiar, similar to standard array methods. Adding is easy, removing is easy.
- No need to reinvent: This library does not create new data structures or protocols – it builds on the already powerful capabilities of JavaScript.
- Bundling has minimal impact: just Reduced to 8.5 kBit is very light. If your project already includes React and its accompanying libraries (which weigh hundreds of thousands of bytes), this addition will be almost unnoticeable.
- Lazy processing: This library delays processing of items, which can reduce memory usage and improve the efficiency of long pipelines by avoiding unnecessary copies of intermediate data.
Things to note
Warehouse has been shipped untranslated to ES5. This decision was driven by a desire to keep the package size small, achieved by leveraging ES6+ features that enable iteration with very concise code – most notably dynamo. However, only Widely supported Language features are exploited.
If you are still compiling to ES5, you can use the library by compiling it yourself and adding a polyfill. Note, however, that this increases the size of the bundle and is not recommended. On the contrary, this could be a great opportunity relive Configure your build and take advantage of modern JavaScript capabilities.
Happy coding!