3

I have a stream that emits numbers x. What I want is dx (difference in x) so I need a rolling buffer which emits x_n and x_(n-1) so I can map to dx = x_n - x_(n-1). In a marble diagram this would look like ...

SOURCE --A------B------C--------D------F--G-----

RESULT ---------AB-----BC-------CD-----DF-FG----

This would be handy for other operations like rolling averages etc.

I have checked the operator docs but can't seem to find anything similar. sample is sort of close but is time dependent. buffer is also close but it strictly queues values with no overlap between the buffers.

I'm using RxJS

Brendan
  • 18,771
  • 17
  • 83
  • 114

4 Answers4

5

RXJS 5

RxJS has the bufferCount operator which works as follows ...

observable = Rx.Observable.from(["A", "B", "C", "D", "E", "F"])

bufferSize = 2
overlap = 1
observable.bufferCount(bufferSize, overlap)
    .subscribe(vals => console.log(vals));

// Results in,
//
// ["A", "B"]
// ["B", "C"]
// ...

overlap is actually the sample frequency so for example in the above case if overlap = 2 then we would get the normal buffer behaviour.

olsn
  • 16,644
  • 6
  • 59
  • 65
Brendan
  • 18,771
  • 17
  • 83
  • 114
  • 1
    Didn't know about `overlap` – Bradford Jan 06 '17 at 16:59
  • @Bradford neither did I! The documentation makes it very easy to miss and with the myriad of operators it's easy to go off and expect something dedicated for this. – Simon_Weaver Jan 13 '18 at 21:03
  • Note: This requires two items to have 'come in' before anything happens. If you're looking for something similar for a larger number of items (for example a recently viewed item list) this may be what you need https://stackoverflow.com/questions/47911178/rxjs-take-n-last-elements-from-an-observable – Simon_Weaver Jan 13 '18 at 21:40
3

RXJS 4

You maybe don't even need a buffer for this, a simple concatMap might work for you (of course I don't know any details of your stream:

observable = Rx.Observable.from(["A", "B", "C", "D", "E", "F"]);

observable
  .bufferWithCount(2, 1)
  .subscribe(all => {
    console.log(all);
});

See live here

olsn
  • 16,644
  • 6
  • 59
  • 65
  • That's a pretty interesting way of doing it - I did find a way which uses `bufferCount` which seems to be a RxJS only function not in the main reactive documents. I'll accept this but post mine as well. – Brendan Jan 06 '17 at 16:41
  • Since there was no tag for [rxjs5] i assumed you where using version 4, and there is no `bufferCount` in rxjs4 – olsn Jan 06 '17 at 16:56
  • Ah, well spotted! – Brendan Jan 06 '17 at 17:07
  • 1
    Turns out I was wrong, and there is such an operator for rxjs4 - not that you'd need it now, but in case someone else uses RXJS4 – olsn Jan 07 '17 at 17:56
3

Two options that will work regardless of the version:

Better

Pairwise

Rx.Observable.from(["A", "B", "C", "D", "E", "F"])
  .pairwise()
  .subscribe(all => {
    console.log(all);
  });

bufferCount (or bufferWithCount)

This does exist in RxJS 4 as well as RxJS 5

version == 5.* .* ===> bufferCount

version >= 4.1.0 ===> bufferCount

version < 4.1.0 ===> bufferWithCount

Rx.Observable.from(["A", "B", "C", "D", "E", "F"])
  // Buffers of length 2 and skip 1 item (i.e. overlap)
  .bufferCount(2, 1)
  // the last buffer will have only the last item so drop it
  .filter(x => x.length == 2)
  .subscribe(all => {
    console.log(all);
  });

See both here

Community
  • 1
  • 1
paulpdaniels
  • 18,395
  • 2
  • 51
  • 55
1
observable = Rx.Observable.from(["A", "B", "C", "D", "E", "F"]);

we can do

observable
  .scan((x, y) => [_.last(x), y], [])
  .filter((x) => x[0] !== undefined)
  .subscribe(all => {
    console.log(all);
  });

or if a hot observable:

observable.
  zip(observable.skip(1))
  .subscribe(all => {
    console.log(all);
  });

or if a cold observable:

observable
  .publish(source_ => source_.zip(source_.skip(1)))
  .subscribe(all => {
    console.log(all);
  });

or easiest:

observable
  .pairwise()
  .subscribe(all => {
    console.log(all);
  });
Bradford
  • 4,143
  • 2
  • 34
  • 44