2

How do you do it? RxJs is still a mystery to me.

I was trying stuff like:

filterChanges
   .delay(400)
   .replay()
   .reduce(function(acc,x) { return acc.concat(x) }, [])
   .subscribe(function(changes) {
      console.log(changes);
      ...

Or

 filterChanges.subscribe(function() {
     filterChanges.aggregate(function(changes) {
          ...

I'm really pretty lost here. The behavior I want is: certain user actions result in multiple filter changes. I don't want to process them one at a time, but only when the spurt of changes is finished. But when I process them I want all the filter changes from the beginning of the stream.

Now that I write this, I realize it would be better to just get all the the filters after a spurt of changes, not all the changes themselves, so I just need to catch the end of a spurt. I'd like the answer to both questions because I think it will help me understand RxJs better.

  1. Subscribe to the end of a burst of events.
  2. Subscribe to the end of a burst of events and capture all events from the beginning of the stream.
Sigfried
  • 2,943
  • 3
  • 31
  • 43
  • 1
    Sounds like want a combination of Buffer and Scan. Buffer to group up all the actions that occur within a given time period, and Scan to consume this buffered stream and maintain a List which accumulates all observed values. – Chris May 27 '15 at 06:36
  • 1
    Can you clarify what you mean with "filter changes"? – André Staltz May 27 '15 at 10:14
  • My use case is: Filters take a simple attribute/value form: so a `state-->Texas` filter means exclude all records where the state is Texas. I use the same scheme with date filters because there are a small number of distinct dates in my data. So if the user changes the start or end dates to include, filters for several distinct dates can be set or removed at once. Whenever filters change, I go through all the records setting or unsetting a .hidden flag. But if the user unsets some filters (e.g., expands the date range), I can't unhide records that are still hidden by some other filter. – Sigfried May 27 '15 at 13:06

1 Answers1

4

If I understood correctly, use buffer in combination with debounce. Buffer will give you a list of events happened during the buffer window, and debounce will define when the buffer window should be closed, that's how you can get all events in the spurt. To get all events from the very beginning of the stream, you scan to accumulate all the lists from the buffers.

var hotFilterChanges = filterChanges.share();

hotFilterChanges
  .buffer(hotFilterChanges.debounce(200))
  .scan((acc, curr) => acc.concat(curr))
  .subscribe(x => console.log(x));

We use .share() to make the Observable be "hot". That's necessary so hotFilterChange.buffer( ... ) and hotFilterChanges.debounce(200) refer to the exact same Observable execution.

See the JSBin.

André Staltz
  • 13,304
  • 9
  • 48
  • 58
  • Thanks! I think it must have been the "hot" thing that kept messing me up. I'd refer to my stream twice and get totally different results. Also, I forgot to say in the question that I was using a ReplaySubject. I guess with your answer, that's not necessary. – Sigfried May 27 '15 at 12:50
  • Not sure if this will matter to anyone, but I had to change `.scan((acc, curr) => acc.concat(curr))` to `.scan( [], (acc, curr) => ...)`, i.e., give it a seed value. Without that, the scan accumulator wouldn't run the first time through and I had put some code there that I needed to have run. – Sigfried May 27 '15 at 13:20