Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding RxJS's Flatmap, FlatmapLatest in C# terms

I'm predominantly C# developer expanding my horizon in JavaScript and recently stumbled upon a library called RxJS.

I would like to understand how Map, Flatmap, FlatmapLatest relate and are there any equivalents in C#.Net ?

like image 607
Navap Avatar asked Oct 20 '15 21:10

Navap


1 Answers

RxJS is part of the Reactive Extensions family which is implemented in various languages, including C# (naturally, as Rx is now a Microsoft project).

So, yes, there are equivalences in C#... :-)

The concepts of map, flatMap and flatMapLatest are not obvious. I am myself a beginner at RxJS, so I hope I get it right...

map takes the items of the observable and map them (transform them) into something else. Eg. might be an arithmetic operation on numbers, transforming a primitive into an object or removing a key from the object, etc.

flatMap has several variants, but basically it takes a function returning an observable from each item of the source observable. This makes a stream of streams (where stream = observable = sequence of items), so flatMap flatten this into a single stream / observable where the all items are in sequence.
Mmm, confusing explanation, I fear... Let's do Ascii marbles to explain.

--A------------------- // First stream
--a1----a2----a3------ // flatMap's function result
-----B---------------- // Second stream
-----b1----b2----b3--- // flatMap's function result
--a1-b1-a2-b2-a3-b3--- // flatMap

flatMapLatest is a flatMap where only the items of the current observable are emitted. If a new observable comes, the values of the previous one are ignored.

--A------------------- // First stream
--a1----a2----a3------ // flatMapLatest's function result
-----B---------------- // Second stream
-----b1----b2----b3--- // flatMapLatest's function result
--a1-b1----b2----b3--- // flatMapLatest

[EDIT] I made some code to better understand the concepts... Showing flatMapLatest wasn't obvious... I saw it used on Ajax requests: if a new one is emitted, no need to take in account the previous one(s).

Demo: a click on any button shows the raw event.

  • Click on the first button to see an enriched event (with map).
  • Click on the second button to trigger a sequence of 5 events at 1 s interval (flatMap). If you click again before the sequence ends, you see interleaved results from the running observables.
  • The third button acts similarly, but using flatMapLatest, a new click drops the results of the previous sequence.

// Generic code to display results

var results = document.getElementById('results');
function showHTML(html)
{
  results.insertAdjacentHTML('beforeend', html);
}
function show(text, obj)
{
  showHTML("<p>" + text + (obj !== undefined ? ': ' + JSON.stringify(obj) : '') + "<p>");
}
function showObject(obj)
{
  show("<p>" + JSON.stringify(obj) + "<p>");
}

// The real code

var button1 = document.querySelector('#button1');
var button2 = document.querySelector('#button2');
var button3 = document.querySelector('#button3');
var buttonClickStream1 = Rx.Observable.fromEvent(button1, 'click');
var buttonClickStream2 = Rx.Observable.fromEvent(button2, 'click');
var buttonClickStream3 = Rx.Observable.fromEvent(button3, 'click');

// Raw
Rx.Observable.merge(buttonClickStream1, buttonClickStream2, buttonClickStream3)
  .subscribe(
    function(v) { show("Value", v); },
    function(e) { show("Error", e); },
    function() { show("Done"); }
  );

// Map
buttonClickStream1
  .map(function (e, i) 
  { 
    e.id = i; // Add id
    e.t = new Date(); // Add timestamp
    return e; 
  })
  .subscribe(function(v) { show("Button 1", v) }); // Simplify: no errors, no completion

// FlatMap
buttonClickStream2
  // Returns several values
  .flatMap(function (e, i) 
  { 
    return Rx.Observable
      .interval(1000).take(5)
      .flatMap(function (x, j) { return Rx.Observable.of(i + ' ' + j) });
  })
  .subscribe(function(v) { show("Button 2", v) });

// FlatMapLatest
buttonClickStream3
  // Returns several values but keep only the last one
  .flatMapLatest(function (e, i) 
  { 
    return Rx.Observable
      .interval(1000).take(5)
      .flatMap(function (x, j) { return Rx.Observable.of(i + ' ' + j) });
  })
  .subscribe(function(v) { show("Button 3", v) });
<button type="button" id="button1">Test map</button>
<button type="button" id="button2">Test flatMap</button>
<button type="button" id="button3">Test flatMapLatest</button>
<div id="results"></div>

<script src="http://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.lite.js"></script>
like image 156
PhiLho Avatar answered Nov 06 '22 14:11

PhiLho