Search code examples
javascriptfrpbacon.js

Refactor common combinators out of bacon.js streams


Let's say I have two bacon.js streams:

const stream1 = $('#input1').asEventStream('input')
    .map(e => e.target.value)
    .filter(inp => inp.length > 3)
    .log();
const stream2 = $('#input2').asEventStream('input')
    .map(e => e.target.value)
    .filter(inp => inp.length > 3)
    .log();

I would like to extract the common step of filtering of by the input length into a variable and apply it to both of the streams afterwards, fantasy code:

const my_filter = Bacon.map(e => e.target.value).filter(inp => inp.length > 3)
const stream1 = $('#input1').asEventStream('input')
    .apply(my_filter)
    .log();
const stream2 = $('#input2').asEventStream('input')
    .apply(my_filter)
    .log();

Is there an idiomatic way to do that?

EDIT1: To clarify, my_filter is just an example. I would like to be able to refactor an arbitrary chain of combinators and apply them to multiple streams.

EDIT2: As Bless Yahu noticed, I need an additional map to get the value out of events. This demonstrates my problem even better.


Solution

  • To me this looks like a basic software refactoring problem.

    Generally, if you want to apply an arbitrary transformation to a stream, you'll probably want to write a function that takes a stream as an argument and returns the modified stream as a result.

    In your particular case I'd remove duplication by extracting steps into functions.

    const getValue = input => input.target.value
    const my_filter = stream => stream.filter(text => text.length > 3)
    const filteredInputValue = ($input) => {
      my_filter($input.asEventStream('input').map(getValue))
    const stream1 = filteredInputValue($('#input1')).log()
    const stream2 = filteredInputValue($('#input2')).log()
    

    In this example, you can easily modify my_filter to apply any other transformations as well, like throttling and text trimming.