0

First of all, I'm fairly new to streams, so I'm still getting to grips with some common patterns.

In many libraries we can split a stream into a stream of streams using .groupBy(keySelectorFn). For example, this stream is split into streams based on the value of 'a' in each object (pseudo-code, not based on any particular library):

var groups = Stream.of(
    { a: 1, b: 0 },
    { a: 1, b: 1 },
    { a: 2, b: 2 },
    { a: 1, b: 3 }
)
.groupBy(get('a'));

Say I want to process groups differently based on the value of 'a' of that group:

groups.map(function(group) {
    if (..?) {
        // Run the group through some process
    }

    return group;
});

I can't see how to get the value of 'a' without consuming the first element of each group (and if the first element of a group is consumed the group is no longer intact).

This seems to me a fairly common thing that I want to do with streams. Am I taking the wrong approach?

--- EDIT ---

Here's a more specific example of a problem that I'm stuck on:

var groups = Stream.of(
    { a: 1, b: 0 },
    { a: 1, b: 1 },
    { a: 2, b: 0 },
    { a: 2, b: 1 },
    { a: 2, b: 2 },
    { a: 1, b: 2 }
)
.groupBy(get('a'));

How to select the first 1 object where a === 1, and the first 2 objects where a === 2, and pass any other objects straight through? This seems logical to me:

groups.chain(function(group) {
    return group.key === 1 ?
        group.take(1) :
    group.key === 2 ?
        group.take(2) :
        group ;
});

But group.key does not exist (and even if it did it would seem a bit... smelly).

stephband
  • 2,594
  • 2
  • 23
  • 33

1 Answers1

0

groupBy will give you a stream of streams(each value in the stream being a stream itself). using fold, you can process each group (which is a stream), into a single value (using conditionals). flatMap puts all the results into a single stream. Here is a simple example that processes groups of objects. It groups the objects according to property "a", does a arithmetic operation based on the value into a single object containing type and val properties. These final objects are flattened into a single stream:

var groupStream = Bacon.fromArray([
     { a: 1, b: 0 },
    { a: 1, b: 1 },
    { a: 2, b: 2 },
    { a: 1, b: 3 }
]);

// -----[stream of objects where a=1]-------[stream of objects where a=2]---->
var groups = groupStream.groupBy(function(k){ return  k.a; })

// v is a stream of a=1 objects or a=2 objects
groups.flatMap(function(v) {

//fold(reduce) over the values in v (stream)
  return v.fold({type:'', val: 0},function(acc,i) {
    if(i.a == 1) {
      return {type: 'one', val: acc.val + i.b };
    }

    if(i.a == 2) {
      return {type: 'two', val: acc.val + i.b };
    }
  })
}).onValue(function(v) {
  console.log(v);
}); 

Here is the jsbin: http://jsbin.com/haliqerita/edit?js,console

Hope that helps.

Bless Yahu
  • 1,331
  • 1
  • 12
  • 25
  • Right, ok. I can see that using .fold() (or .reduce()) is perhaps the way forward. But you have conditional checks for property 'a' inside .fold()... seeing as you have already grouped groupStream by the value of 'a' it seems a bit wasteful to then have to check each object in those groups for the value of 'a'. One of the things people trumpet about streams is their efficiency. Granted, one little check is not a huge concern, but I'm surprised by this sort of thing. – stephband Sep 21 '16 at 08:22
  • Now imagine that I want to take the first 1 object where a === 1, but the first 2 objects where a === 2. .fold() does not really help here, unless I put some incrementable counter var inside .flatMap – actually, I'd need a counter var for each value of 'a' (and I may not know how many values of 'a' there are). Ideally I want to do if (v.key === 1) { return v.take(1); } if (v.key === 2) { return v.take(2); } ... but there is no such thing as v.key ... – stephband Sep 21 '16 at 08:33
  • I added an example to the question to illustrate that problem. – stephband Sep 21 '16 at 08:46