0

Based on question Create chain in lodash with custom functions I would like to chain my own custom function with the following requirements:

  1. Keep fluent
  2. Return a Seq
  3. Keep lazy behavior

For example consider the sequence res resulting from the following query:

const src = [0, 1, 1, 6, 6, 6, 1, 1, 12, 12, 6, 6]
const res = _(src)
    .filter(nr =>  nr % 2 == 0)
    .map(nr => 'nr:' + nr)
console.log(res.value()) // => ['nr:0','nr:6','nr:6','nr:6','nr:12','nr:12','nr:6','nr:6' ]

Now, I would like to interleave between filter() and map(), a new collapse() operation which merges series of adjacent elements.

I am implementing collapse() like this:

function collapse(src) {
    const res = []
    let prev = undefined
    for(let curr of src) {
        if(prev != curr) {
            prev = curr
            res.push(curr)
        }
    }
    return _(res)
}

But this solution does not fulfill requirements: 1. Keep fluent, and 3. Keep lazy behavior. To use this collapse()we have to write:

collapse(
    _(src)
    .filter(nr =>  nr % 2 == 0)
)
.map(nr => 'nr: ' + nr) // => [ 'nr: 0', 'nr: 6', 'nr: 12', 'nr: 6' ]

Instead, I would like to write: _(src).filter(nr => nr % 2 == 0).collapse().map(nr => 'nr: ' + nr)

Even worst, the collapse is being performed eagerly.

From this answer I see that we can extend the lodash prototype with the mixin utility function. Yet, am not figuring out how can I access the elements of the previous operation (e.g. filter()) for being parsed in collapse() function?

Miguel Gamboa
  • 8,855
  • 7
  • 47
  • 94
  • A function defined using a mixin should work in a chain. In your example just return res rather than _(res). – Gruff Bunny Nov 24 '17 at 17:33
  • @GruffBunny and where do I get the `src`? – Miguel Gamboa Nov 24 '17 at 17:34
  • Chaining will pass the result of the previous operation to the current operation. – Gruff Bunny Nov 24 '17 at 17:42
  • @GruffBunny I am going to try that. Can you tell me where in lodash documentation is describing that behavior? – Miguel Gamboa Nov 24 '17 at 17:51
  • Can't find anything in the docs. What chaining does is pass the result of the previous operation as the first parameter. In your case as you only have one parameter the chain would look something like `.filter(...).collapse().map(...)` i.e. no parameter passed to collapse explicitly. – Gruff Bunny Nov 24 '17 at 18:04
  • Have added an answer as it's a bit more clear than code in a comment. – Gruff Bunny Nov 24 '17 at 18:08

2 Answers2

1

A mixin will work in a chain and the first parameter passed to the function will be the result of the previous operation:

_.mixin( { collapse: function(list){
    const res = []
    let prev = undefined
    for(let curr of list) {
        if(prev != curr) {
            prev = curr
            res.push(curr)
        }
    }
    return res;
}});

const res = _(src)
    .filter(nr =>  nr % 2 == 0)
    .collapse()
    .map(nr => 'nr:' + nr)
    .value();
Gruff Bunny
  • 27,738
  • 10
  • 72
  • 59
0
const collapse = start => el => el !== start ? ( start = el, true ) : false;

So you can do:

_(src).filter( collapse() )
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151