I Have a List of Object A, and in every A I have a list of Object B.
here is what I want to do:
while looping over List<A> using RxJava I want to be able to loop the inner list and do some filtering on the List<B> and at the end, I would like to add the filtered List<B> to the original parent object of List<A>.
is this operation possible without breaking the chain?
something like this example :
class A {
int mAId;
List<B> mBList;
}
class B{
int mId;
}
public void loop(List<A> list){
Observable.fromIterable(list)
.flatMapIterable(objs-> objs.mBList)
.filter("filtering each b")
.toList()
.map("add back the list of b to the right a")
.subscribe()
}
My opinion:
public void loop(List<A> list){
List<FromObject> innerList;
list.forEach(e->{
innerList=e.getInnerList();
innerList.forEach(in->{
<apply your filter here>
<for example>
if(in.getNumber()>0)
innerList.remove(in);
}
}
}
Wish it would help
Yes, you can achieve it by nesting the reactive streams:
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(0, 1, 2, 3, 4, 5),
Arrays.asList(6, 7, 8, 9, 10, 11),
Arrays.asList(12, 13, 14, 15, 16, 17),
Arrays.asList(18, 19, 20, 21, 22, 23)
);
Observable.fromIterable(listOfLists)
.flatMap((innerList) -> {
return Observable.fromIterable(innerList)
.filter((value) -> value % 2 == 0)
.toList()
.toObservable();
})
.toList()
.subscribe(listOfListsFiltered -> {
for (List<Integer> innerList : listOfListsFiltered) {
for (Integer value : innerList) {
System.out.print(value + ", ");
}
System.out.println();
}
});
}
I finally found the answer to this problem:
class A(val filterable: Boolean = random.nextBoolean())
class B(val name: String = random.nextInt().toString(), val listOfA: List<A> = listOf(A(), A(), A(), A(), A(), A()))
#Test
fun rxTest() {
Observable.fromIterable(listOf(B(), B(), B(), B(), B(), B(), B()))
.compose {
it.publish<B> { publish ->
val filteredList = publish.flatMapIterable { it.listOfA }
.filter {
it.filterable
}
.toList()
.toObservable()
Observable.zip<B, List<A>, B>(publish, filteredList, BiFunction<B, List<A>, B> { b, listOfA ->
B(b.name, listOfA)
})
}
}
}
for future readers or inspiration of a better answer.
There is Another way of doing this
public static class A {
int id;
List<B> mBList;
}
public static class B {
int id;
}
public void test() {
Observable.just(Arrays.asList(new A(), new A(), new A()))
.flatMapIterable(a -> a)
.map(a -> {
a.mBList = Observable.fromIterable(a.mBList)
.distinct()
.toList()
.blockingGet();
return a;
});
}
Related
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
What do I need to do for the output to be the below?
// one
// two
// three
// four
// five
// six
I looked into concat but as the javadoc explains, it just appends one after the other, it does not interleave / intersperse.
Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);
Creates a lazily concatenated stream whose elements are all the
elements of the first stream followed by all the elements of the
second stream.
Wrongly gives
// one
// three
// five
// two
// four
// six
Could do it if I collected them and iterated, but was hoping for something more Java8-y, Streamy :-)
Note
I don't want to zip the streams
“zip” operation will take an element from each collection and combine them.
the result of a zip operation would be something like this: (unwanted)
// onetwo
// threefour
// fivesix
I’d use something like this:
public static <T> Stream<T> interleave(Stream<? extends T> a, Stream<? extends T> b) {
Spliterator<? extends T> spA = a.spliterator(), spB = b.spliterator();
long s = spA.estimateSize() + spB.estimateSize();
if(s < 0) s = Long.MAX_VALUE;
int ch = spA.characteristics() & spB.characteristics()
& (Spliterator.NONNULL|Spliterator.SIZED);
ch |= Spliterator.ORDERED;
return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
Spliterator<? extends T> sp1 = spA, sp2 = spB;
#Override
public boolean tryAdvance(Consumer<? super T> action) {
Spliterator<? extends T> sp = sp1;
if(sp.tryAdvance(action)) {
sp1 = sp2;
sp2 = sp;
return true;
}
return sp2.tryAdvance(action);
}
}, false);
}
It retains the characteristics of the input streams as far as possible, which allows certain optimizations (e.g. for count()and toArray()). Further, it adds the ORDERED even when the input streams might be unordered, to reflect the interleaving.
When one stream has more elements than the other, the remaining elements will appear at the end.
A much dumber solution than Holger did, but may be it would fit your requirements:
private static <T> Stream<T> interleave(Stream<T> left, Stream<T> right) {
Spliterator<T> splLeft = left.spliterator();
Spliterator<T> splRight = right.spliterator();
T[] single = (T[]) new Object[1];
Stream.Builder<T> builder = Stream.builder();
while (splRight.tryAdvance(x -> single[0] = x) && splLeft.tryAdvance(builder)) {
builder.add(single[0]);
}
return builder.build();
}
As you can see from the question comments, I gave this a go using zip:
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
Stream<String> out = interleave(a, b);
public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
}
/**
* https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
**/
private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
final Iterator<A> iteratorA = streamA.iterator();
final Iterator<B> iteratorB = streamB.iterator();
final Iterator<C> iteratorC = new Iterator<C>() {
#Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext();
}
#Override
public C next() {
return zipper.apply(iteratorA.next(), iteratorB.next());
}
};
final boolean parallel = streamA.isParallel() || streamB.isParallel();
return iteratorToFiniteStream(iteratorC, parallel);
}
private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
This may not be a good answer because
(1) it collects to map, which you don't want to do I guess and
(2) it is not completely stateless as it uses AtomicIntegers.
Still adding it because
(1) it is readable and
(2) community can get an idea from this and try to improve it.
Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");
AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);
Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
.flatMap(m -> m.entrySet().stream())
.sorted(Comparator.comparing(Map.Entry::getKey))
.forEach(e -> System.out.println(e.getValue())); // or collect
Output
one
two
three
four
five
six
#Holger's edit
Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getValue())); // or collect
without any external lib (using jdk11)
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class MergeUtil {
private static <T> Stream<T> zipped(List<T> lista, List<T> listb) {
int maxSize = Math.max(lista.size(), listb.size());
final var listStream = IntStream
.range(0, maxSize)
.mapToObj(i -> {
List<T> result = new ArrayList<>(2);
if (i < lista.size()) result.add(lista.get(i));
if (i < listb.size()) result.add(listb.get(i));
return result;
});
return listStream.flatMap(List::stream);
}
public static void main(String[] args) {
var l1 = List.of(1, 2, 3);
var l2 = List.of(4, 5, 6, 7, 8, 9);
final var zip = zipped(l1, l2);
System.out.println(zip.collect(Collectors.toList()));
}
}
listStream is a Stream<List<A>> that flatted in return.
The result is:
[1, 4, 2, 5, 3, 6, 7, 8, 9]
One solution with Iterator
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();
final Iterator<String> iter = new Iterator<String>() {
private final AtomicInteger idx = new AtomicInteger();
#Override
public boolean hasNext() {
return iterA.hasNext() || iterB.hasNext();
}
#Override
public String next() {
return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
}
};
// Create target Stream with StreamEx from: https://github.com/amaembo/streamex
StreamEx.of(iter).forEach(System.out::println);
// Or Streams from Google Guava
Streams.stream(iter).forEach(System.out::println);
Or simply by the solution in abacus-common provided by me:
AtomicInteger idx = new AtomicInteger();
StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println());
Using Guava's Streams.zip and Stream.flatMap:
Stream<String> interleaved = Streams
.zip(a, b, (x, y) -> Stream.of(x, y))
.flatMap(Function.identity());
interleaved.forEach(System.out::println);
Prints:
one
two
three
four
five
six
I want to count the number of identical objects one after another with the Java 8 stream. How do I do that? If I have a list of
3, 3, 5, 5, 5, 6, 3, 3
I want the result to be
[3-2] [5-3] [6-1] [3-2]
My naive try without Java 8 streams:
private static List<ValueCount> counteSameValueInRow(List<Integer> Values) {
List<ValueCount> result = new ArrayList<ValueCount>();
ValueCount valueCount = null;
for (int value: Values) {
if (valueCount == null) {
valueCount = new ValueCount(value);
} else if (valueCount.value == value){
valueCount.numberof++;
} else {
result.add(valueCount);
valueCount = new ValueCount(value);
}
}
result.add(valueCount);
return result;
}
What you are doing is comparable to a collect on a stream. You are taking each number and summarize it into a list of "groups". Collectors.groupingBy() comes to mind, but that would group the numbers accross the whole list, i.e. just count the occurrences of each number. Using the Stream.collect(Supplier, BiConsumer, BiConsumer) method to implement a custom collect, you can do something like this:
List<Integer> values = Arrays.asList(3, 3, 5, 5, 5, 6, 3, 3);
values.stream().collect(LinkedList<List<Integer>>::new, (list, value) -> {
if (list.isEmpty() || !list.getLast().get(0).equals(value))
{
list.add(new ArrayList<>());
}
list.getLast().add(value);
}, (list1, list2) -> {
if (list1.getLast().get(0).equals(list2.getFirst().get(0)))
{
list1.getLast().addAll(list2.getFirst());
list2.removeFirst();
}
list1.addAll(list2);
}).forEach(group -> System.out.println("[" + group.get(0) + "-" + group.size() + "]"));
Note that I used ArrayLists to collect duplicates. You can use your ValueCount class for this purpose which might make it more readable.
The stream in this example does not improve your code in terms of readability, but enables the use of parallel processing. Look at the third parameter of the collect method. That one combines two intermediate results in case the stream was processed in parallel.
To try it in parallel, replace stream() with parallelStream() and put a sysout in the third parameter's lambda to see when two intermediate results are merged. Note that parallel processing will only benefit you if your list is very large.
I have made a refactoring to get better readability after Malte Hartwig's proposal. Here is the result
public static void main(String[] args) {
List<Integer> values = Arrays.asList(3, 3, 5, 5, 5, 6, 3, 3);
BiPredicate<Integer, Integer> predicate = (value1, value2) -> value1.equals(value2);
Supplier<BiConsumer<LinkedList<ValueCount>, Integer>> accumulator =
() -> (list, value) -> {
if (list.isEmpty() || !predicate.test(list.getLast().getFirstValue(), value)) {
list.add(new ValueCount());
}
list.getLast().add(value);
};
Supplier<BiConsumer<LinkedList<ValueCount>, LinkedList<ValueCount>>> combiner = () -> (list1, list2) -> {
if (list1.getLast().getFirstValue().equals(list2.getFirst().getFirstValue())) {
list1.getLast().addAll(list2.getFirst());
list2.removeFirst();
}
list1.addAll(list2);
};
values.stream().collect(LinkedList::new, accumulator.get(), combiner.get())
.forEach(group -> System.out.println(group));
}
private static class ValueCount {
private List<Integer> lista = new ArrayList<>();
public String toString() { return "[" + lista.get(0) + "-" + lista.size() + "]";}
public void add(Integer value) { lista.add(value);}
public Integer getFirstValue() { return lista.get(0);}
public void addAll(ValueCount first) { lista.addAll(first.getAll());}
private Collection<? extends Integer> getAll() { return lista;}
}
I have a stream of Foo objects.
class Foo {
private int variableCount;
public Foo(int vars) {
this.variableCount = vars;
}
public Integer getVariableCount() {
return variableCount;
}
}
I want a list of Foo's that all have the lowest variableCount.
For example
new Foo(3), new Foo(3), new Foo(2), new Foo(1), new Foo(1)
I only want the stream to return the last 2 Foos, since they have the lowest value.
I've tried doing a collect with grouping by
.collect(Collectors.groupingBy((Foo foo) -> {
return foo.getVariableCount();
})
And that returns a Map<Integer, List<Foo>> and I'm not sure how to transform that into what I want.
Thanks in advance
You can use a sorted map for grouping and then just get the first entry.
Something along the lines:
Collectors.groupingBy(
Foo::getVariableCount,
TreeMap::new,
Collectors.toList())
.firstEntry()
.getValue()
Here is a solution that:
Only streams the list once.
Doesn't build a map or other structure that contains all of the input items (unless the variable counts are all the same), only keeping those that are currently the minimum.
Is O(n) time, O(n) space. It's entirely possible that all Foos have the same variable count, in which case this solution would store all items like other solutions. But in practice, with different, varied values and higher cardinality, the number of items in the list is likely to be much lower.
Edited
I've improved my solution according to the suggestions in the comments.
I implemented an accumulator object, which supplies functions to the Collector for this.
/**
* Accumulator object to hold the current min
* and the list of Foos that are the min.
*/
class Accumulator {
Integer min;
List<Foo> foos;
Accumulator() {
min = Integer.MAX_VALUE;
foos = new ArrayList<>();
}
void accumulate(Foo f) {
if (f.getVariableCount() != null) {
if (f.getVariableCount() < min) {
min = f.getVariableCount();
foos.clear();
foos.add(f);
} else if (f.getVariableCount() == min) {
foos.add(f);
}
}
}
Accumulator combine(Accumulator other) {
if (min < other.min) {
return this;
}
else if (min > other.min) {
return other;
}
else {
foos.addAll(other.foos);
return this;
}
}
List<Foo> getFoos() { return foos; }
}
Then all we have to do is collect, referencing the accumulator's methods for its functions.
List<Foo> mins = foos.stream().collect(Collector.of(
Accumulator::new,
Accumulator::accumulate,
Accumulator::combine,
Accumulator::getFoos
)
);
Testing with
List<Foo> foos = Arrays.asList(new Foo(3), new Foo(3), new Foo(2), new Foo(1), new Foo(1), new Foo(4));
The output is (with a suitable toString defined on Foo):
[Foo{1}, Foo{1}]
IF you are OK streaming (iterating) twice:
private static List<Foo> mins(List<Foo> foos) {
return foos.stream()
.map(Foo::getVariableCount)
.min(Comparator.naturalOrder())
.map(x -> foos.stream()
.filter(y -> y.getVariableCount() == x)
.collect(Collectors.toList()))
.orElse(Collections.emptyList());
}
To avoid creating the entire map and also avoiding streaming twice, I copied a custom collector from here https://stackoverflow.com/a/30497254/1264846 and modified it to work with min instead of max. I didn't even know custom collectors were possible so I thank #lexicore for pointing me in that direction.
This is the resulting function minAll
public static <T, A, D> Collector<T, ?, D> minAll(Comparator<? super T> comparator,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier();
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
BinaryOperator<A> downstreamCombiner = downstream.combiner();
class Container {
A acc;
T obj;
boolean hasAny;
Container(A acc) {
this.acc = acc;
}
}
Supplier<Container> supplier = () -> new Container(downstreamSupplier.get());
BiConsumer<Container, T> accumulator = (acc, t) -> {
if(!acc.hasAny) {
downstreamAccumulator.accept(acc.acc, t);
acc.obj = t;
acc.hasAny = true;
} else {
int cmp = comparator.compare(t, acc.obj);
if (cmp < 0) {
acc.acc = downstreamSupplier.get();
acc.obj = t;
}
if (cmp <= 0)
downstreamAccumulator.accept(acc.acc, t);
}
};
BinaryOperator<Container> combiner = (acc1, acc2) -> {
if (!acc2.hasAny) {
return acc1;
}
if (!acc1.hasAny) {
return acc2;
}
int cmp = comparator.compare(acc1.obj, acc2.obj);
if (cmp < 0) {
return acc1;
}
if (cmp > 0) {
return acc2;
}
acc1.acc = downstreamCombiner.apply(acc1.acc, acc2.acc);
return acc1;
};
Function<Container, D> finisher = acc -> downstream.finisher().apply(acc.acc);
return Collector.of(supplier, accumulator, combiner, finisher);
}
You could use collect wisely on the sorted list and in accumulator add the logic to add only either first element to empty list or add any other Foo having variable count same as of the first element of the list.
A complete working example below:-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
class Foo {
private int variableCount;
public Foo(int vars) {
this.variableCount = vars;
}
public Integer getVariableCount() {
return variableCount;
}
public static void main(String[] args) {
List<Foo> list = Arrays.asList(
new Foo(2),
new Foo(2),
new Foo(3),
new Foo(3),
new Foo(1),
new Foo(1)
);
System.out.println(list.stream()
.sorted(Comparator.comparing(Foo::getVariableCount))
.collect(() -> new ArrayList<Foo>(),
(ArrayList<Foo> arrayList, Foo e) -> {
if (arrayList.isEmpty()
|| arrayList.get(0).getVariableCount() == e.getVariableCount()) {
arrayList.add(e);
}
},
(ArrayList<Foo> foos, ArrayList<Foo> foo) -> foos.addAll(foo)
)
);
}
#Override
public String toString() {
return "Foo{" +
"variableCount=" + variableCount +
'}';
}
}
Also, you could first find the minimum variableCount in one stream and use that inside filter of another stream.
list.sort(Comparator.comparing(Foo::getVariableCount));
int min = list.get(0).getVariableCount();
list.stream().filter(foo -> foo.getVariableCount() == min)
.collect(Collectors.toList());
I think in any case either sorting is required or a way to find the minimum number which later can be used inside the predicate. Even if you are using the map to group the values.
Cheers!
Here is alternative with one stream and custom reducer. The idea is to first sort and then collect only elements with first min value:
List<Foo> newlist = list.stream()
.sorted( Comparator.comparing(Foo::getVariableCount) )
.reduce( new ArrayList<>(),
(l, f) -> {
if ( l.isEmpty() || l.get(0).getVariableCount() == f.getVariableCount() ) l.add(f);
return l;
},
(l1, l2) -> {
l1.addAll(l2);
return l1;
}
);
Or using collect is even more compact:
List<Foo> newlist = list.stream()
.sorted( Comparator.comparing(Foo::getVariableCount) )
.collect( ArrayList::new,
(l, f) -> if ( l.isEmpty() || l.get(0).getVariableCount() == f.getVariableCount() ) l.add(f),
List::addAll
);
To avoid creating the map you could use two streams :
the first finds the minimum value.
the second filters elements with this value.
It could give :
List<Foo> foos = ...;
int min = foos.stream()
.mapToInt(Foo::getVariableCount)
.min()
.orElseThrow(RuntimeException::new); // technical error
List<Foo> minFoos = foos.stream()
.filter(f -> f.getVariableCount() == min)
.collect(Collectors.toList());
I want to be able to collect a list of elements of fixed size of 50 elements. Here is how I am currently doing it. I would like to use lambdas if possible.
List<Contact> contactList=getContacts();
Iterator<Contact> it=contactList.iterator();
List<Contact> batch=new ArrayList<>();
while(it.hasNext()) {
if(batch.size()<50) {
batch.add(it.next())
} else {
processBatch(batch);
}
//When iterator has less than 50 elements
if (!it.hasNext() && batch.size()<50) {
processBatch(batch);
}
}
You can do it in that way :
Iterable<String> iterable = () -> it;
contactList.addAll(StreamSupport.stream(iterable.spliterator(), false)
.limit(50)
.collect(Collectors.toList()));
Approach-1
public static void main(String[] args) {
List<Integer> list = IntStream.range(0, 280).boxed().collect(toList());
AtomicInteger count = new AtomicInteger();
StreamSupport.stream(list.spliterator(), false)
.collect(groupingBy(e -> count.getAndIncrement() / 50,
collectingAndThen(toList(), l -> {
processBatch(l);
return null;
})));
}
public static <T extends Object> void processBatch(List<T> list) {
System.out.println(list.size());
}
I have taken AtomicInteger to act as mutable counter object. If you are using apache commons lang API then replace AtomicInteger with MutableInt object.
Approach-2
If you can directly use list object rather than using iterator, then we can code like below. Here external counter object not require.
IntStream.range(0, list.size()).mapToObj(i -> new Object[] { i, list.get(i) }).collect(groupingBy(
arr -> (int) arr[0] / 50, Collectors.mapping(arr -> arr[1], collectingAndThen(toList(), l -> {
processBatch(l);
return null;
}))));
I have the following structure:
class A {
List<B> bs;
}
class B {
List<C> cs;
}
class C {
List something.
}
I have List of A class and I have to get summ of all elements inside something list. I tried to do the following:
totalCount = as
.stream()
.map(a -> a.getBs()
.stream()
.mapToInt(b -> b.getSomething().size())
.sum());
But that doesn't compile. Where is my mistake?
Compile error is:
Error:(61, 21) java: incompatible types: no instance(s) of type variable(s) R exist so that java.util.stream.Stream<R> conforms to java.lang.Integer
There are many ways to obtain this result, one possible way is to just flat everything and count the results:
A a = ..;
a.bs.stream()
.flatMap(aa -> aa.cs.stream())
.flatMap(bb -> bb.something.stream())
.count();
Let this be a lesson why nested streams are a terrible idea.
You have a mapping function:
Function<A, Integer> func = a -> a.getBs().stream()
.mapToInt(b -> b.getSomething().size())
.sum();
Put this in your initial stream and you get:
totalCount = as
.stream()
.map(func);
Unless totalCount is a Stream<Integer>, then this won't compile.
This is not as short at the other ones, but at least its has test code to verify it works.
public class NewMain {
public static void main(String[] args) {
List<A> as = getAlist();
int totalCount = as
.stream()
.map(a -> a.getBs())
.collect(Collectors.summingInt(bs -> bs.stream()
.map(b -> b.cs)
.collect(Collectors.summingInt(cs -> cs.stream()
.map(c -> c.something)
.collect(Collectors.summingInt(s -> s.size()))))));
System.out.println(totalCount);
}
private static List<A> getAlist() {
List<A> all = new ArrayList<>();
for (int k = 0; k < 10; k++) {
A a = new A();
for (int j = 0; j < 10; j++) {
B b = new B();
for (int i = 0; i < 10; i++) {
C c = new C();
c.something = Arrays.asList(1, 2, 3, 4);
b.cs.add(c);
}
a.bs.add(b);
}
all.add(a);
}
return all;
}
static class A {
List<B> bs = new ArrayList<>();
private List<B> getBs() {
return bs;
}
}
static class B {
List<C> cs = new ArrayList<>();
}
static class C {
List something;
List getSomething() {
return something;
}
}
}