Suppose I have two streams of A's and B's.
Stream 1: A1, A2, A3, A4, A5, A6
Stream B: B1, B2, B3, B4
Timeline: B1 > A1 > A2 > B2 > A3 > A4 > B3 > A5
Desired output: [B1 & A1], [A2 & B2], [B2 & A3], [A4 & B3], [B3 & A5], [A6 & B4]
This mirrors a real life process where B's are double sided and stacked together. For example,
---- B1 ----]
|-1
---- A1 ----]
---- A2 ----]
]-2
---- B2 ----|]
]|-3
---- A3 ---- ]
---- A4 ----]
|-4
---- B3 ----]]
|-5
---- A5 ---- ]
---- A6 ----]
|-6
---- B4 ----]
Grouping 1 is characteristic of the initial pair. Grouping 6 is characteristic of the terminal pairing. Groups 2/3 and 4/5 can be repeated.
I'm looking for ideas on how to achieve this with RxJava2.
Thanks, Dan.
EDIT
I made a test case:
import java.util.Objects;
import org.junit.Test;
import io.reactivex.Flowable;
import io.reactivex.functions.BiFunction;
import io.reactivex.processors.FlowableProcessor;
import io.reactivex.processors.ReplayProcessor;
public class BuildControllerTest {
#Test
public void testProductBlanketSequencing() throws Exception {
final String a1="A1", a2="A2", a3="A3", a4="A4", a5="A5", a6="A6";
final String b1="B1", b2="B2", b3="B3", b4="B4";
class AB {
String a;
String b;
public AB(String a, String b) {
this.a = a;
this.b = b;
}
#Override public boolean equals(Object obj) {
if(getClass() != obj.getClass()) return false;
if( ! Objects.equals(a, ((AB)obj).a)) return false;
if( ! Objects.equals(b, ((AB)obj).b)) return false;
return true;
}
}
final FlowableProcessor<String> a = ReplayProcessor.create();
final FlowableProcessor<String> b = ReplayProcessor.create();
b.onNext(b1);
a.onNext(a1);
a.onNext(a2);
b.onNext(b2);
a.onNext(a3);
a.onNext(a4);
b.onNext(b3);
a.onNext(a5);
a.onNext(a6);
b.onNext(b4);
a.onComplete(); b.onComplete();
Flowable.zip(a, b, new BiFunction<String, String, AB>() {
#Override public AB apply(String t1, String t2) throws Exception {
return new AB(t1, t2);
}
})
.test()
.assertResult(
new AB(a1, b1),
new AB(a2, b2),
new AB(a3, b2),
new AB(a4, b3),
new AB(a5, b3),
new AB(a6, b4));
}
}
From your example it is not clear what condition drives pairing.
When we use zip the pair is formed as soon as both sources has next item:
(A1, B1), (A2, B2), (A3, B3), (A4, B4)
If we use combineLatest the pair is formed when there is next item in any source:
(A1, B1), (A2, B1), (A2, B2), (A3, B2), (A4, B2), (A4, B3), (A5, B3), (A6, B3), (A6, B4)
For me it looks like you want to combine sources but take not every pair. You can remove extra pairs depending on business rules.
The extra rule can be time, then take look at sampling.
Flowable.combineLatest(a, b, (t1, t2) -> new AB(t1, t2))
.throttleLast(1, TimeUnit.MICROSECONDS)
it will take latest result from combined pairs every time interval
(A1, B1), (A4, B3), (A5, B3), (A6, B3), (A6, B4)
If extra rules is to have unique values from source A and posible repeats from B then you can use distinct
Flowable.combineLatest(a, b, (t1, t2) -> new AB(t1, t2))
.distinct(ab -> ab.a)
(A1, B1), (A2, B1), (A3, B2), (A4, B2), (A5, B3), (A6, B3)
Related
Consider the following future sequence:
val s1 = Future.sequence((1 to 5).map(someFuncReturningFuture))
And in the end I want to return a Future[MyCaseClass] where MyCaseClass is a case class that takes 5 parameters, which I want to be the 5 Ints inside s1. I thought s1.map((a1, a2, a3, a4, a5) => MyCaseClass(a1, a2, a3, a4, a5)) should work. How to achieve what I'm trying to do?
Solution I found was to do:
for (Vector(a1,a2,a3,a4,a5) <- Future.sequence((1 to 5).map(someFuncReturningFuture)))
yield MyCaseClass(a1,a2,a3,a4,a5)
I have a List "a, b, c,d", and this is the expected result
a
ab
abc
abcd
b
bc
bcd
c
cd
d
I tried bruteforce, but I think, there might be any other efficient solution, provided I am having very long list.
Here's a one-liner.
"abcd".tails.flatMap(_.inits.toSeq.init.reverse).mkString(",")
//res0: String = a,ab,abc,abcd,b,bc,bcd,c,cd,d
The mkString() is added just so we can see the result. Otherwise the result is an Iterator[String], which is a pretty memory efficient collection type.
The reverse is only there so that it comes out in the order you specified. If the order of the results is unimportant then that can be removed.
The toSeq.init is there to remove empty elements left behind by the inits call. If those can be dealt with elsewhere then this can also be removed.
This may not be the best solution but one way of doing this is by using sliding function as follow,
val lst = List('a', 'b', 'c', 'd')
val groupedElements = (1 to lst.size).flatMap(x =>
lst.sliding(x, 1))
groupedElements.foreach(x => println(x.mkString("")))
//output
/* a
b
c
d
ab
bc
cd
abc
bcd
abcd
*/
It may not be the best solution, but I think is a good one, and it's tailrec
First this function to get the possible sublists of a List
def getSubList[A](lista: Seq[A]): Seq[Seq[A]] = {
for {
i <- 1 to lista.length
} yield lista.take(i)
}
And then this one to perform the recursion calling the first function and obtain all the sublists possible:
def getSubListRecursive[A](lista: Seq[A]): Seq[Seq[A]] = {
#tailrec
def go(acc: Seq[Seq[A]], rest: Seq[A]): Seq[Seq[A]] = {
rest match {
case Nil => acc
case l => go(acc = acc ++ getSubList(l), rest= l.tail)
}
}
go(Nil, lista)
}
The ouput:
getSubListRecursive(l)
res4: Seq[Seq[String]] = List(List(a), List(a, b), List(a, b, c), List(a, b, c, d), List(b), List(b, c), List(b, c, d), List(c), List(c, d), List(d))
In following code, why does method A1 of traits C and D are called even though class B has defined A1?
scala> trait A {
| def A1 = A2
| def A2 //A2 is abstract and need implementation
| }
defined trait A
scala> class B extends A {
| def A2 = println("B") //implemented A2 in B
| }
defined class B
scala> val b1 = new B
b1: B = B#163e8949
this works fine.
scala> b1.A1
B
scala> b1.A2
B
now I mix traits. C still has A2 abstract
scala> trait C extends A {
| abstract override def A1 = {super.A1; C}
| def C = println("C")
| }
defined trait C
D still has A2 abstract strong text
scala> trait D extends A {
| abstract override def A1 = {super.A1; D}
| def D = println("D")
| }
defined trait D
Why C and D are printed when I cam calling A1 of B. Shouldn't B's A1 only call B's A2 which is implemented to print B?
scala> val b2 = new B with C with D
b2: B with C with D = $anon$1#701d2b59
scala> b2.A1
B
C
D
scala> b2.A2
B
scala> val b3 = new B with D with C
b3: B with D with C = $anon$1#270f28cf
scala> b3.A1
B
D
C
I suppose what I am using is called stackable trait pattern. Basically, if I use new B with C with D and call a method, compiler will see if the method is defined in D, then C and then B (order is right to left). When the compiler sees an implementation it uses it. The use of super makes the compiler call the next implementation available in the class or trait on left (again right to left order)
Removed super calls.
scala> trait D extends A {
| abstract override def A1 = { D}
| def D = println("D")
| }
defined trait D
scala> trait C extends A {
| abstract override def A1 = { C }
| def C = println("C")
| }
defined trait C
scala> val b3 = new B with D with C
b3: B with D with C = $anon$1#7417ef4f
As super is removed, when compiler sees call to A1, it checks C first (rightmost, order is right to left) and it finds A1 and uses that implementation. As A1 no longer uses super, A1 in D or B are not called.
scala> b3.A1
C
If D is rightmost then only D's A1 is called
scala> val b3 = new B with C with D
b3: B with C with D = $anon$1#fd4459b
scala> b3.A1
D
Added super after print. This changes order or printing.
scala> trait C extends A {
| abstract override def A1 = { C ; super.A1}
| def C = println("C")
| }
defined trait C
scala> val b3 = new B with C with D
b3: B with C with D = $anon$1#4489f60f
scala> b3.A1
D
As super is called, after calling A1 from D, compiler looks for A1 of super class (next one on left) and keeps going towards left.
scala> val b3 = new B with D with C
b3: B with D with C = $anon$1#7b3c0ecb
scala> b3.A1
C
D
scala>
Why C and D are printed when I cam calling A1 of B. Shouldn't B's A1 only call B's A2 which is implemented to print B?
No. super.A1 refers to A#A1, which calls A2 of the object it's called on: not B#A2, not A#A2, but this.A2.
Also, C and D aren't related to B in any way, so they couldn't be calling B#A1 in any case: did you mean class C/D extends B?
As far as I understand, for expressions are translated into Scala expressions which are build upon:
map
flatMap
filterWith
foreach
High order lists methods.
A common example is the one where:
for(b1 <= books; b2 <- books if b1 != b2;
a1 <- b1.authors; a2 <- b2.authors if a1 == a2) yield a1;
Results in:
books flatMap (b1 =>
books withFilter( b2 => b1 != b2) flatMap( b2 =>
b1.authors flatMap ( a1 =>
b2.authors withFilter ( a2 => a2 == a1 ) map ( a2 => a1 )
)
)
)
Where:
books is a list of class Book objects (List[Book])
Book has a public attribute authors of type List[String]
My question is about this line:
b2.authors withFilter ( a2 => a2 == a1 ) map ( a2 => a1 )
Since the condition is a2 == a1 that line is equivalent to:
b2.authors withFilter ( a2 => a2 == a1 ) map ( x => x )
Why the generated code isn't just?
b2.authors filter ( a2 => a2 == a1 )
Can it be explained by the fact that the example is the reproduction of code automatically generated by Scala's compiler?
Is filter out of the for "building bricks"?
The translation of for/yield syntax into method calls is very simple and mechanical, almost at the level of string manipulation. withFilter is necessary in some places for its laziness, therefore it's used everywhere for simplicity. I don't understand the phrasing of your final question, but for/yield expressions are AIUI never translated into calls to filter except in a deprecated way for objects that don't yet have a withFilter method.
Any hints how to draw branching schema in spirit of attached image is welcomed.
Note that I would like to do it in graphviz for fast editing and future changes.
I made an attempt to imitate the famous git branching strategy from http://nvie.com/posts/a-successful-git-branching-model/ using GraphViz.
This is the original picture:
And this is the result:
The code:
strict digraph g{
rankdir="TB";
nodesep=0.5;
ranksep=0.25;
splines=line;
forcelabels=false;
// general
node [style=filled, color="black",
fontcolor="black", font="Consolas", fontsize="8pt" ];
edge [arrowhead=vee, color="black", penwidth=2];
// branch names
node [fixedsize=false, penwidth=0, fillcolor=none, shape=none, width=0, height=0, margin="0.05"];
subgraph {
rank=sink;
me [label="master", group="master"];
}
subgraph {
rank=sink;
de [label="develop", group="develop"];
}
// tags
node [shape=cds, fixedsize=false, fillcolor="#C6C6C6", penwidth=1, margin="0.11,0.055"]
t1 [label="0.1"]
t2 [label="0.2"]
t3 [label="1.0"]
// graph
node [width=0.2, height=0.2, fixedsize=true, label="", margin="0.11,0.055", shape=circle, penwidth=2, fillcolor="#FF0000"]
// branches
node [group="master", fillcolor="#27E4F9"];
m1;
m2;
m3;
m4;
subgraph {
rank=source;
ms [label="", width=0, height=0, penwidth=0];
}
m1 -> m2 -> m3 -> m4;
ms -> m1 [color="#b0b0b0", style=dashed, arrowhead=none ];
m4 -> me [color="#b0b0b0", style=dashed, arrowhead=none ];
node [group="hotfixes", fillcolor="#FD5965"];
h1;
node [group="release", fillcolor="#52C322"];
r1;
r2;
r3;
r4;
r5;
r1 -> r2 -> r3 -> r4;
node [group="develop", fillcolor="#FFE333"];
d1;
d2;
d3;
d4;
d5;
d6;
d7;
d8;
d9;
d10;
d1 -> d2 -> d3 -> d4 -> d5 -> d6 -> d7 -> d8 -> d9 -> d10;
d10 -> de [color="#b0b0b0", style=dashed, arrowhead=none ];
node [group="feature 1", fillcolor="#FB3DB5"];
fa1;
fa2;
fa3;
fa4;
fa5;
fa6;
subgraph fas1 {
fa1 -> fa2 -> fa3;
}
subgraph fas2 {
fa4 -> fa5 -> fa6;
}
node [group="feature 2", fillcolor="#FB3DB5"];
fb1;
fb2;
fb3;
fb4;
subgraph{ rank=same; fa6; fb4; } // hack
subgraph{ rank=same; fa1; fb1; } // hack
fb1 -> fb2 -> fb3 -> fb4;
// nodes
m1 -> d1;
m1 -> h1;
h1 -> m2;
h1 -> d5;
d3 -> fa1;
fa3 -> d6;
d6 -> r1;
r2 -> d7;
r4 -> d8;
r4 -> m3;
d9 -> r5;
r5 -> m4;
r5 -> d10;
d7 -> fa4;
fa6 -> d9;
d3 -> fb1;
fb4 -> d9;
// tags connections
edge [color="#b0b0b0", style=dotted, len=0.3, arrowhead=none, penwidth=1];
subgraph {
rank="same";
m1 -> t1;
}
subgraph {
rank="same";
m2 -> t2 ;
}
subgraph {
rank="same";
m3 -> t3;
}
}
Hope this helps someone.
This particular diagram was made with inkscape, therefore it will be difficult to match it with graphviz's output.
Here's how you may match some of it with graphviz:
Use a different group attribute for each branch in order to get straight lines for each branch (here's another example of using group, and one using weight)
Define the branches in the right order to have them appear from top to bottom
Use shape, style, width and height have some nodes stand out, and hide others
Use some \n newline cheating to have labels on top of the nodes (you may also try labelloc="t", or using xlabel instead of label)
digraph g{
rankdir="LR";
pad=0.5;
nodesep=0.6;
ranksep=0.5;
forcelabels=true;
node [width=0.12, height=0.12, fixedsize=true,
shape=circle, style=filled, color="#909090",
fontcolor="deepskyblue", font="Arial bold", fontsize="14pt" ];
edge [arrowhead=none, color="#909090", penwidth=3];
node [group="release3"];
s3 [label="release 3\n\n", width=0.03, height=0.03, shape=box];
r30 [label=" R3.0\n\n\n"];
e3 [label="", width=0.03, height=0.03, shape=box];
e3f [label="", width=0.03, height=0.03, shape=circle, color="#b0b0b0"];
s3 -> r30 -> e3;
e3 -> e3f [color="#b0b0b0", style=dashed];
node [group="release2"];
s2 [label="release 2\n\n", width=0.03, height=0.03, shape=box];
b2 [label="", width=0.03, height=0.03, shape=box];
r20 [label=" R2.0\n\n\n"];
e2 [label="", width=0.03, height=0.03, shape=box];
e2f [label="", width=0.03, height=0.03, shape=circle, color="#b0b0b0"];
s2 -> b2 -> r20 -> e2;
e2 -> e2f [color="#b0b0b0", style=dashed];
node [group="release1"];
s1 [label="release 1\n\n", width=0.03, height=0.03, shape=box];
ttest [label=" test\n\n\n"];
b1 [label="", width=0.03, height=0.03, shape=box];
r10 [label=" R1.0\n\n\n"];
r11 [label=" R1.1\n\n\n"];
e1 [label="", width=0.03, height=0.03, shape=box];
e1f [label="", width=0.03, height=0.03, shape=circle, color="#b0b0b0"];
s1 -> ttest -> b1 -> r10 -> r11 -> e1;
e1 -> e1f [color="#b0b0b0", style=dashed];
b1 -> s2;
b2 -> s3;
}