Java collection pack() method - java

What would be a good way to implement the pack() operation of a collection using Java stream? Here is what I'd like to accomplish:
List<String> items = Arrays.asList("A", "A", "B", "B", "A", "C", "C", "A", "A", "A");
List<List<String>> packs = items.stream().pack();
// packs: [[A,A],[B,B],[A],[C,C],[A,A,A]]
Alternatively, the pack operation could return a list of tuples in the form of (index, element, count):
[(0, A, 2), (2, B, 2), (4, A, 1), (5, C, 2), (7, A, 3)]
Currently I implemented this using a mutable accumulator in the following fashion:
Packer<String> packer = new Packer<>();
items.stream().forEach(packer);
List<Triple<Integer, T, Integer>> packs = packer.get();
public class Packer<T> implements Consumer<T>, Supplier<List<Triple<Integer, T, Integer>>>
{
private List<Triple<Integer, T, AtomicInteger>> result = new ArrayList<>();
private Optional<Triple<Integer, T, AtomicInteger>> currentElement = Optional.empty();
private int count = 0;
#Override
public void accept(T t)
{
if (currentElement.isPresent() && currentElement.get().getMiddle().equals(t))
{
currentElement.get().getRight().incrementAndGet();
}
else
{
currentElement = Optional.of(Triple.of(count, t, new AtomicInteger(1)));
result.add(currentElement.get());
}
count++;
}
#Override
public List<Triple<Integer, T, Integer>> get()
{
return result.stream().map(x -> Triple.of(x.getLeft(), x.getMiddle(), x.getRight().get())).collect(Collectors.toList());
}
}

You can collapse adjacent elements with StreamEx:
List<List<String>> packs = StreamEx.of(items)
.collapse(Object::equals, Collectors.toList())
.collect(Collectors.toList());
Output:
[[A, A], [B, B], [A], [C, C], [A, A, A]]
JDoodle Demo
I'm not sure if StreamEx supports collapsing with indexes.

Try this.
List<String> items = Arrays.asList("A", "A", "B", "B", "A", "C", "C", "A", "A", "A");
List<List<String>> result = items.stream()
.collect(() -> new LinkedList<List<String>>(),
(list, e) -> {
if (list.isEmpty() || !list.getLast().contains(e))
list.add(new LinkedList<>());
list.getLast().add(e);
},
(a, b) -> {});
System.out.println(result);
result
[[A, A], [B, B], [A], [C, C], [A, A, A]]
This can not be processed with parallel stream.

As #shmosel mentioned in his answer, it can done by StreamEx
List<List<String>> packs = StreamEx.of(items).collapse(Object::equals, Collectors.toList()).toList();
System.out.println(packs);
// [[A, A], [B, B], [A], [C, C], [A, A, A]]
MutableInt idx = MutableInt.of(0);
List<Triple<Integer, String, Integer>> packs2 = StreamEx.of(items).collapse(Object::equals, Collectors.toList())
.map(l -> Triple.of(idx.getAndAdd(l.size()), l.get(0), l.size())).toList();
System.out.println(packs2);
// [[0, A, 2], [2, B, 2], [4, A, 1], [5, C, 2], [7, A, 3]]
[Update]: Just found out, actually there are even simpler APIs available in StreamEx for this OP:
packs = StreamEx.of(items).groupRuns(Object::equals).toList();
System.out.println(packs);
packs2 = StreamEx.of(items).runLengths().mapKeyValue((k, v) -> Triple.of(idx.getAndAdd(v.intValue()), k, v.intValue())).toList();
System.out.println(packs2);
Amazing!

Seems like you could create a custom collector for that:
static class PackCollector<T> implements Collector<T, List<List<T>>, List<List<T>>> {
#Override
public Supplier<List<List<T>>> supplier() {
return () -> {
List<List<T>> list = new ArrayList<>();
list.add(new ArrayList<>());
return list;
};
}
#Override
public BiConsumer<List<List<T>>, T> accumulator() {
return (list, s) -> {
int size = list.size();
List<T> inner = list.get(size - 1);
int innerSize = inner.size();
if (innerSize > 0) {
T last = inner.get(inner.size() - 1);
if (s.equals(last)) {
inner.add(s);
} else {
List<T> newList = new ArrayList<>();
newList.add(s);
list.add(newList);
}
} else {
inner.add(s);
}
};
}
#Override
public BinaryOperator<List<List<T>>> combiner() {
return (left, right) -> {
List<T> lastLeft = left.get(left.size() - 1);
List<T> firstRight = right.get(0);
T leftElem = lastLeft.get(lastLeft.size() - 1);
T rightElem = firstRight.get(firstRight.size() - 1);
if (leftElem.equals(rightElem)) {
lastLeft.addAll(right.remove(0));
}
left.addAll(right);
return left;
};
}
#Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Characteristics.IDENTITY_FINISH);
}
#Override
public Function<List<List<T>>, List<List<T>>> finisher() {
return Function.identity();
}
}
And then use it:
List<List<String>> result = items.stream().parallel().collect(new PackCollector<>());
System.out.println(result);

Related

Java 8 - Merge All Subsets Containing Common Elements

Starting with a set of sets "groups":
Set<Set<String>> groups = new HashSet<>();
I want to create a new list of sets by merging all subsets with common elements:
i.e. Starting with the sets below:
A = {a, b, c}
B = {c, d, e, f}
C = {f, g, h, i, j}
D = {k, l, m}
E = {m, n, o}
F = {p, q, r}
The final result would be:
Set 1 = {a, b, c, d, e, f, g, h, i, j}
Set 2 = {k, l, m, n, o}
Set 3 = {p, q, r}
Any advice on how to accomplish this would be appreciated.
EDIT: In case of uneven sets it would perform the same. So if it were a method, it pseudo would look like this:
public void doStuff(){
Set<Set<String>> groups = {{a,b,c}, {c,d,e,f}, {m, n, o}}
Set<Set<String>> newGroups = mergeSubsets(groups);
System.out.println(newGroups);
}
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups){
//some operations
}
Console out:
New Groups: {{a,b,c,d,e,f}, {m, n, o}}
You can just implement the algorithm as you describe it in your problem statement -- find intersecting sets and merge them until there is nothing to merge. Standard library has a method Collections.disjoint that helps by determining if two collections have any elements in common:
// this implementation sacrifices efficiency for clarity
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups) {
Set<Set<String>> result = new HashSet<>();
for (Set<String> set : groups) {
// try to find a set in result that intersects this set
// if one is found, merge the two. otherwise, add this set to result
result.stream()
.filter(x -> !Collections.disjoint(x, set))
.findAny()
.ifPresentOrElse( // this method was added in java 9
x -> x.addAll(set),
() -> result.add(new HashSet<>(set))
);
}
// if nothing got merged we are done; otherwise, recurse and try again
return result.size() == groups.size() ? result : mergeSubsets(result);
}
Here is the imperative way based on #NiksVij solution. Obviously the solution of #NiksVij is not correct and this answer aims to fix this and extend a bit more:
public class MergeSet {
public static void main(String... args) {
List<Set<String>> list = new ArrayList<>();
String[] A = {"a", "c", "e", "g"};
String[] B = {"b", "d", "f", "h"};
String[] C = {"c", "e", "f"};
String[] D = {"b"};
list.add(new HashSet<>(Arrays.asList(A)));
list.add(new HashSet<>(Arrays.asList(C)));
list.add(new HashSet<>(Arrays.asList(B)));
list.add(new HashSet<>(Arrays.asList(D)));
List<Set<String>> newGroups = merge(list);
System.out.println(newGroups);
}
#SuppressWarnings("empty-statement")
private static <T> List<Set<T>> merge(List<Set<T>> list) {
if (list == null || list.isEmpty()) {
return list;
}
List<Set<T>> merged = new ArrayList<>();
do {
merged.add(list.get(0));
list.remove(0);
while (mergeStep(merged.get(merged.size() - 1), list));
} while (!list.isEmpty());
return merged;
}
private static <T> boolean mergeStep(Set<T> setToCheck, List<Set<T>> remainingList) {
boolean atLeastOnceMerged = false;
Iterator<Set<T>> iterator = remainingList.iterator();
while (iterator.hasNext()) {
Set<T> elements = iterator.next();
boolean doMerge = !Collections.disjoint(elements, setToCheck);
if (doMerge) {
atLeastOnceMerged |= doMerge;
setToCheck.addAll(elements);
iterator.remove();
}
}
return atLeastOnceMerged;
}
import java.util.*;
public class MergeSet {
public static void main(String... args) {
List<Set<String>> groups = new ArrayList<>();
String[] A = {"a", "b", "c"};
String[] B = {"c", "d", "e", "f"};
String[] C = {"f", "g", "h", "i", "j"};
String[] D = {"k", "l", "m"};
String[] E = {"m", "n", "o"};
String[] F = {"p", "q", "r"};
groups.add(new HashSet<>(Arrays.asList(A)));
groups.add(new HashSet<>(Arrays.asList(B)));
groups.add(new HashSet<>(Arrays.asList(C)));
groups.add(new HashSet<>(Arrays.asList(D)));
groups.add(new HashSet<>(Arrays.asList(E)));
groups.add(new HashSet<>(Arrays.asList(F)));
Set<Set<String>> newGroups = mergeSubsets(groups);
System.out.println(newGroups);
}
private static Set<Set<String>> mergeSubsets(List<Set<String>> groups) {
List<Set<String>> newGroups = new ArrayList<>();
Set<String> init = groups.get(0);
groups.remove(0);
newGroups.add(init);
while (!groups.isEmpty()) {
removeMergedElementFromGroupAndUpdateNewGroup(newGroups.get(newGroups.size() - 1), groups);
if(!groups.isEmpty()) {
init = groups.get(0);
groups.remove(0);
newGroups.add(init);
}
}
return new HashSet<>(newGroups);
}
private static void removeMergedElementFromGroupAndUpdateNewGroup(Set<String> master2, List<Set<String>> masterList) {
Iterator<Set<String>> iterator = masterList.iterator();
while (iterator.hasNext()) {
Set<String> strings = iterator.next();
boolean merge = strings.stream().anyMatch(string -> master2.contains(string));
if (merge) {
master2.addAll(strings);
iterator.remove();
}
}
}
}
Hope this helps instead of Set<Set<String>> groups I have used List<Set<String>> groups for the ease of using lists if you have a constraint of using Set only , you can generate List from Set(say yourSet) by passing it into the constructor of Lists implementation , for eg.
groups = new ArrayList<>(yourSet);

Loop Nesting List RxJava2

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;
});
}

Reducing Cognitive Complexity of Six-Way Cartesian Product

I have a piece of code that has Cognitive Complexity of 21
for (String item1 : itemList1){
for (String item2 : itemList2){
for (String item3 : itemList3){
for (String item4 : itemList4){
for (String item5 : itemList5){
for (String item6 : itemList6){
methodToRun(item1, item2, item3, item4, item5, item6);
}
}
}
}
}
}
Our linter specifies a maximum Cognitive Complexity of 15, so I should reduce this by the standards we've been following.
Can anyone suggest an alternative solution to this piece of code? Or is leaving it like this acceptable despite the complexity being too high?
I know this could be a personal opinion, but I'm looking for genuine solutions or answers from people who have had similar situations before.
EDIT : I cannot access a lot of libraries and packages from the dev machine I'm working on. I have access to some (too many to list), so please take note of this before suggesting use of one.
You can go for a recursive solution. It is arguably less readable, but has a much smaller level of nesting, which reduces the complexity measure:
static void recursiveRun(List<List<String>> list, int pos, String[] item) {
if (pos == 6) {
methodToRun(item[0], item[1], item[2], item[3], item[4], item[5]);
} else {
for (String s : list.get(pos)) {
item[pos] = s;
recursiveRun(list, pos+1, item);
}
}
}
The initial call looks like this:
recursiveRun(
Arrays.asList(itemList1, itemList2, itemList3, itemList4, itemList5, itemList6)
, 0
, new String[6]
);
Google Guava solution
Once your data is packed in List<List<String>> then you can use n-ary Cartesian Product preserving the order of elements (lexicographical) implemented in Google Guava.
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
List<List<String>> input = Arrays.asList(
ImmutableList.of("Mary", "Alice"),
ImmutableList.of("Smith", "Darcy", "Brown"),
ImmutableList.of("Ford", "Saab")
);
List<List<String>> result = Lists.cartesianProduct(input); //Cognitive Complexity of 0
for (List<String> shuffle: result) {
System.out.println(String.join(",", shuffle));
}
... produces:
Mary,Smith,Ford
Mary,Smith,Saab
Mary,Darcy,Ford
Mary,Darcy,Saab
Mary,Brown,Ford
Mary,Brown,Saab
Alice,Smith,Ford
Alice,Smith,Saab
Alice,Darcy,Ford
Alice,Darcy,Saab
Alice,Brown,Ford
Alice,Brown,Saab
Pure Java half-solution
Here is a quick solution with hard-coded values for 3 lists which potentially could get generalized without incurring too much of complexity penalty.It basically does some neat (a.k.a hard to follow) index calculations.
String[] list0 = new String[] {"0", "1"};
String[] list1 = new String[] {"4", "5"};
String[] list2 = new String[] {"8", "9"};
int[] indexes = new int[3];
long totalPermutations = list0.length * list1.length * list2.length;
for(int i = 0; i < totalPermutations; i++) {
indexes[0] = i % list0.length;
indexes[1] = (i / list0.length) % list1.length;
indexes[2] = (i / (list0.length * list1.length)) % list2.length;
System.out.println(list0[indexes[0]] + "," + list1[indexes[1]] + "," + list2[indexes[2]]);
}
Metrics discussion
Pure Java solution is a perfect example where for the sake of keeping the metric happy, we had actually increased the complexity and maintainability.
That whole index calculation is tbh quite horrible and took few goes to get right. It will most likely cost a penalty in general solution anyway as iteration will be required. Other solutions I have found on the web (including recursive and functional) are not clearer than the bunch of nested loops.
Invented here Cartesian product routines will IMO be more complex (even if scoring lower complexity) to comprehend.
Software has to build on abstractions, and using open, well designed 3rd party dependency makes the whole issue go away nicely.
Here is an iterator based solution.
class CartesianProductIterator<T> implements Iterator<List<T>>, Iterable<List<T>> {
private List<List<T>> data;
private int size;
private int[] sizes;
private int[] cursors;
private boolean done;
public CartesianProductIterator(List<List<T>> data) {
this.data = data;
this.size = data.size();
this.sizes = new int[this.size];
this.cursors = new int[this.size];
setSizes(data);
}
#Override
public boolean hasNext() {return !done;}
#Override
public List<T> next() {
if (! hasNext()) throw new NoSuchElementException();
ArrayList<T> tuple = new ArrayList<>();
for (int i = 0; i < size; i++) {tuple.add(data.get(i).get(cursors[i]));}
updateCursors();
return tuple;
}
private void updateCursors() {
for (int i = size - 1; i >= 0; i--) {
if (cursors[i] < sizes[i] - 1) {
cursors[i]++;
break;
} else {
cursors[i] = 0;
if (i == 0) done = true;
}
}
}
private void setSizes(List<List<T>> data) {
for (int i = 0; i < size; i++) {sizes[i] = data.get(i).size();}
}
#Override
public void remove() {
throw new UnsupportedOperationException("remove is not supported here");
}
#Override
public Iterator<List<T>> iterator() {return this;}
}
can be used to create cross products on demand
List<List<String>> data = new ArrayList<>();
data.add(Arrays.asList("a", "b", "c"));
data.add(Arrays.asList("1", "2"));
data.add(Arrays.asList("A", "B", "C"));
Iterator<List<String>> dataIterator = new CartesianProductIterator<String>(data);
while (dataIterator.hasNext()) {
System.out.println(dataIterator.next());
}
now with dual Iterable/Iterator interface can alternatively used as
for(List<String> combination: new CartesianProductIterator<>(data)) {
System.out.println(combination);
}
I wanted to follow up on my comment with some workable code. I then realized that the recursive parts are very much like #dasblinkenlight (I assure you that is not intended), so I hesitate posting this (just refer to his). However this is a little more generic. I am upvoting #dasblinkenlight.
public class CartesianProduct {
public static void main(String[] args) {
List<String> l1 = new ArrayList<String>(Arrays.asList("a", "b", "c"));
List<String> l2 = new ArrayList<String>(Arrays.asList("d", "e", "f"));
List<String> l3 = new ArrayList<String>(Arrays.asList("g", "h"));
processCartesianProduct(new MyCartesianProductTask(), l1, l2, l3);
}
private static void processCartesianProduct(CartesianProductTask task, List<String>... lists) {
processCP(task, new String[lists.length], 0, lists);
}
private static void processCP(CartesianProductTask task, String[] element, int pos, List<String>... lists) {
if (pos == lists.length)
task.doTask(element);
else {
for (String s : lists[pos]) {
element[pos] = s;
processCP(task, element, pos+1, lists);
}
}
}
interface CartesianProductTask {
public void doTask(String[] element);
}
static class MyCartesianProductTask implements CartesianProductTask {
#Override
public void doTask(String[] element) {
System.out.println("Performing task on: "+Arrays.asList(element));
// Business logic goes here
}
}
}
Produces:
Performing task on: [a, d, g]
Performing task on: [a, d, h]
Performing task on: [a, e, g]
Performing task on: [a, e, h]
Performing task on: [a, f, g]
Performing task on: [a, f, h]
Performing task on: [b, d, g]
Performing task on: [b, d, h]
Performing task on: [b, e, g]
Performing task on: [b, e, h]
Performing task on: [b, f, g]
Performing task on: [b, f, h]
Performing task on: [c, d, g]
Performing task on: [c, d, h]
Performing task on: [c, e, g]
Performing task on: [c, e, h]
Performing task on: [c, f, g]
Performing task on: [c, f, h]
Here is a solution based on computing an index in a Cartesian Product:
Compute the size of each sub-space by multiplying the size of the next sub-space by the size of the current vector; size of a "point space" is 1.
Iterate over all indexes in the space. Index in space i can be computed by dividing by the size of sub-space and MOD-ing the result with the size of the vector.
Here is an implementation:
static void iterateCartesian(List<List<String>> lists) {
int[] size = new int[lists.size()+1];
size[lists.size()] = 1;
for (int i = lists.size()-1 ; i >= 0 ; i--) {
size[i] = size[i+1]*lists.get(i).size();
}
for (int i = 0 ; i != size[0] ; i++) {
methodToRun(
lists.get(0).get((i/size[1]) % lists.get(0).size())
, lists.get(1).get((i/size[2]) % lists.get(1).size())
, lists.get(2).get((i/size[3]) % lists.get(2).size())
, lists.get(3).get((i/size[4]) % lists.get(3).size())
, lists.get(4).get((i/size[5]) % lists.get(4).size())
, lists.get(5).get((i/size[6]) % lists.get(5).size())
);
}
}
This solution is pretty "flat," but it requires a fair amount of quantitative abilities to understand.
Demo.
I don't advocate this approach, but I think it (a) would suppress the warning and (b) might give you a way to think about how to break out the code to help it make more sense. I don't find this clearer or more readable than the original, but depending on what all the terms mean in your context, it might suggest useful new directions.
void a(list1, list2, list3, list4, list5, list6) {
for (String s1: list1) {
b(s1, list2, list3, list4, list5, list6)
}
}
void b(s1, list2, list3, list4, list5, list6) {
for (String s2: list2) {
c(s1, s2, list3, list4, list5, list6)
}
}
void c(s1, s2, list3, list4, list5, list6) {
for (String s3: list3) {
d(s1, s2, s3, list4, list5, list6)
}
}
void d(s1, s2, s3, list4, list5, list6) {
for (String s4: list4) {
e(s1, s2, s3, s4, list5, list6)
}
}
void e(s1, s2, s3, s4, list5, list6) {
for (String s5: list5) {
f(s1, s2, s3, s4, s5, list6)
}
}
void f(s1, s2, s3, s4, s5, list6) {
for (String s6: list6) {
methodToRun(s1, s2, s3, s4, s5, s6)
}
}

Average difference between elements

I need to transform list (time, channel) which is sorted by time ascending:
[15, A], [16, B], [17, C], [20, A], [22, C], [24, B], [26, C], [27, B], [28, A]
to this one:
[6.5, A], // ((20-15)+(28-20))/2 - average difference between elements (channel A)
[5.5, B], // ((24-16)+(27-24))/2
[4.5, C] // ((22-17)+(26-22))/2
using java streams.
Well, supposing that there is something like ChannelInfo class:
class ChannelInfo {
private final int time;
private final String channel;
// constructor, getters, setters
it could be achieved like this:
List<ChannelInfo> pairs = Arrays.asList(
new ChannelInfo(15, "A"), new ChannelInfo(16, "B"),
new ChannelInfo(17, "C"), new ChannelInfo(20, "A"),
new ChannelInfo(22, "C"), new ChannelInfo(24, "B"),
new ChannelInfo(26, "C"), new ChannelInfo(27, "B"),
new ChannelInfo(28, "A"));
Map<String, Double> map = pairs.stream()
.collect(Collectors.groupingBy(ChannelInfo::getChannel,
Collectors.collectingAndThen(Collectors.toList(),
list -> {
int size = list.size();
return IntStream.range(1, size)
.map(x -> (list.get(size - x).getTime() - list.get(size - x - 1).getTime()))
.average()
.orElse(0d);
})));
System.out.println(map); // {A=6.5, B=5.5, C=4.5}
As I understood from the original question, I can propose one of the ways, using own data class:
public class MyList {
private int first;
private String second;
public MyList(int it, String str) {
this.first = it;
this.second = str;
}
public MyList() {
}
public int getValue() {
return this.first;
}
public MyList getChannel(String str) {
if (str.equals(second)) {
return this;
}
return null;
}
}
And main class for processing:
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
public class MyListProcessing {
private ArrayList<MyList> ml = new ArrayList<MyList>();
private String[] channels = new String[]{"A", "B", "C"};
private double[] channels_avg = new double[channels.length];
private int[] channels_qnt = new int[channels.length];
public MyListProcessing() {
ml.add(new MyList(15, "A"));
ml.add(new MyList(16, "B"));
ml.add(new MyList(17, "C"));
ml.add(new MyList(20, "A"));
ml.add(new MyList(22, "C"));
ml.add(new MyList(24, "B"));
ml.add(new MyList(26, "C"));
ml.add(new MyList(27, "B"));
ml.add(new MyList(28, "A"));
getAverage();
}
private void getAverage() {
for (int i = 0; i < channels.length; i++) {
MyList mmll = new MyList();
double sum = 0.0;
for (int j = 0; j < ml.size(); j++) {
mmll = ml.get(j).getChannel(channels[i]);
if (mmll != null) {
sum += mmll.getValue();
channels_qnt[i] = channels_qnt[i] + 1;
}
channels_avg[i] = sum / channels_qnt[i];
}
}
NumberFormat formatter = new DecimalFormat("#0.00");
for (int i = 0; i < channels_avg.length; i++) {
System.out.println("[" + formatter.format(channels_avg[i]) + ", " + channels[i] + "]");
}
}
public static void main(String[] args) {
new MyListProcessing();
}
}
OUTPUT:
[21.00, A]
[22.33, B]
[21.67, C]

How do I generate a Cartesian product in Java?

I have a number of ArrayList with each ArrayList having objects and each one can have different length. I need to generate permutation like in the below example:
Suppose I have 2 ArrayList:
ArrayList A has object a, object b and object c
ArrayList B has object d, object e
Then the output should be 6 new ArrayList with these combinations:
Combination 1 object a and object d,
Combination 2 object a and object e,
Combination 3 object b and object d,
Combination 4 object b and object e,
Combination 5 object c and object d,
Combination 6 object c and object e,
Can anyone help me?
Guava 19+
Lists.cartesianProduct(List...)
E.g.:
List<Object> list1 = Arrays.asList("a", "b", "c");
List<Object> list2 = Arrays.asList("d", "e");
System.out.println(Lists.cartesianProduct(list1, list2));
Output:
[[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]]
With Java8 streams
List<String> a = Arrays.asList("a", "b", "c");
List<String> b = Arrays.asList("d", "e");
String[][] AB = a.stream()
.flatMap(ai -> b.stream()
.map(bi -> new String[]{ai, bi}))
.toArray(String[][]::new);
System.out.println(Arrays.deepToString(AB));
output
[[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]]
To get as List
List<List<String>> ll = a.stream()
.flatMap(ai -> b.stream()
.map(bi -> new ArrayList<>(Arrays.asList(ai, bi))))
.collect(Collectors.toList());
With an Iterable+Iterator:
import java.util.*;
class CartesianIterator <T> implements Iterator <List <T>> {
private final List <List <T>> lilio;
private int current = 0;
private final long last;
public CartesianIterator (final List <List <T>> llo) {
lilio = llo;
long product = 1L;
for (List <T> lio: lilio)
product *= lio.size ();
last = product;
}
public boolean hasNext () {
return current != last;
}
public List <T> next () {
++current;
return get (current - 1, lilio);
}
public void remove () {
++current;
}
private List<T> get (final int n, final List <List <T>> lili) {
switch (lili.size ())
{
case 0: return new ArrayList <T> (); // no break past return;
default: {
List <T> inner = lili.get (0);
List <T> lo = new ArrayList <T> ();
lo.add (inner.get (n % inner.size ()));
lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
return lo;
}
}
}
}
class CartesianIterable <T> implements Iterable <List <T>> {
private List <List <T>> lilio;
public CartesianIterable (List <List <T>> llo) {
lilio = llo;
}
public Iterator <List <T>> iterator () {
return new CartesianIterator <T> (lilio);
}
}
You can use them in a simplified for-loop:
class CartesianIteratorTest {
public static void main (String[] args) {
List <Character> la = Arrays.asList (new Character [] {'a', 'b', 'c'});
List <Character> lb = Arrays.asList (new Character [] {'d', 'e'});
List <List <Character>> llc = new ArrayList <List <Character>> ();
llc.add (la);
llc.add (lb);
CartesianIterable <Character> ci = new CartesianIterable <Character> (llc);
for (List<Character> lo: ci)
show (lo);
}
public static void show (List <Character> lo) {
System.out.print ("(");
for (Object o: lo)
System.out.print (o);
System.out.println (")");
}
}
Cartesian product of multiple lists using the map and reduce approach
The map method represents each element of the list as a singleton list and specifies the format of the result.
Intermediate output:
[[a], [b], [c]]
[[d], [e]]
[[f]]
The reduce method sums pairs of 2D lists into a single 2D list.
Final output:
[[a, d, f], [a, e, f], [b, d, f], [b, e, f], [c, d, f], [c, e, f]]
Try it online!
public static void main(String[] args) {
List<String> a = Arrays.asList("a", "b", "c");
List<String> b = Arrays.asList("d", "e");
List<String> c = Arrays.asList("f");
List<List<String>> cp = cartesianProduct(Arrays.asList(a, b, c));
// output
System.out.println(cp);
}
public static <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
// check if not null
if (lists == null) return null;
// cartesian product of multiple lists
return lists.stream()
// only those lists that are not null and not empty
.filter(list -> list != null && list.size() > 0)
// represent each list element as a singleton list
.map(list -> list.stream().map(Collections::singletonList)
// Stream<List<List<T>>>
.collect(Collectors.toList()))
// intermediate output
.peek(System.out::println)
// stream of lists into a single list
.reduce((lst1, lst2) -> lst1.stream()
// combinations of inner lists
.flatMap(inner1 -> lst2.stream()
// concatenate into a single list
.map(inner2 -> Stream.of(inner1, inner2)
.flatMap(List::stream)
.collect(Collectors.toList())))
// list of combinations
.collect(Collectors.toList()))
// otherwise an empty list
.orElse(Collections.emptyList());
}
See also: Cartesian product of an arbitrary number of sets
Use Guava... Here is an example of a Cartesian product of a list with itself:
public static void main(String[] args) {
//How to do a cartesian product of a List of items
List<Integer> listToSelfMultiply = Arrays.asList(
new Integer(1), new Integer(2), new Integer(3), new Integer(4));
LinkedList<Integer> linkedListCopy = Lists.newLinkedList(listToSelfMultiply);
for (Integer i : listToSelfMultiply) {
if (linkedListCopy.size() == 1) {
break;
}
linkedListCopy.remove();
System.out.println("" + Arrays.deepToString(
Lists.cartesianProduct(Arrays.asList(i), linkedListCopy).toArray()) + "");
}
}
Use nested for loops that would have a loop for every ArrayList as below. I am assuming I have two ArrayLists - intList and stringList. I can have two nested for loops (one for each list) to generate the permutation.
for (Integer i : intList) {
for (String s : stringList) {
...
}
}
Cartesian product of multiple lists
You can use the reduce method with three parameters:
identity - specify the result stub.
List<List<T>>
accumulator - append elements of lists to the result.
List<List<T>> result, List<T> list
combiner - is used in parallel mode, combines the results.
List<List<T>> result1, List<List<T>> result2
Try it online!
/**
* #param lists the lists for multiplication
* #param <T> the type of list element
* #return the Cartesian product
*/
public static <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
// check if incoming data is not null
if (lists == null) return Collections.emptyList();
return lists.stream()
// non-null and non-empty lists
.filter(list -> list != null && list.size() > 0)
// stream of lists into a single list
.reduce(// identity - specify the result stub
Collections.singletonList(Collections.emptyList()),
// accumulator - append elements of lists to the result
(result, list) -> result.stream()
.flatMap(inner -> list.stream()
.map(el -> {
List<T> nList = new ArrayList<>(inner);
nList.add(el);
return nList;
}))
// list of combinations
.collect(Collectors.toList()),
// combiner - is used in parallel mode, combines the results
(result1, result2) -> {
result1.addAll(result2);
return result1;
});
}
public static void main(String[] args) {
List<String> l1 = Arrays.asList("A", "B");
List<String> l2 = Arrays.asList("C", "D");
List<String> l3 = Arrays.asList("E", "F");
List<List<String>> cp = cartesianProduct(Arrays.asList(l1, l2, l3));
// output
System.out.println(cp);
}
Output:
[[A,C,E],[A,C,F],[A,D,E],[A,D,F],[B,C,E],[B,C,F],[B,D,E],[B,D,F]]
See also: Cartesian product of 3 collections

Categories