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)
}
}
Related
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);
This question already has answers here:
Get a list of combinations of lists' elements
(4 answers)
Closed 8 years ago.
Cross Join concept is the same as database cross join. I have multiple lists lets say 3 to start with and I have to join the lists as below:
List<E> l1: {a, b},
List<E> l2: {c, d},
List<E> l3: {e, f},
Cross join should produce:
List<E> l4 = {a, c, f},
List<E> l5 = {a, c, e},
List<E> l6 = {a, d, f},
List<E> l7 = {a, d, e},
List<E> l8 = {b, c, f},
List<E> l9 = {b, c, e},
List<E> l10 = {b, d, f},
List<E> l11= {b, d, e};
3 Lists with 3 elements each will give 27 new lists. And number of elements in each list is same, always.
Now one possible solution is to iterate with three for loops and then add to 3 new lists.
Is there any other possible way to achieve this so that complexity is lower?
Thanks
This code will work for any List<List<T>> parameter.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class X {
public static void main(String[] args) {
List<List<String>> param = new ArrayList<List<String>>() {{
add(Arrays.asList("A", "B"));
add(Arrays.asList("C", "D"));
add(Arrays.asList("E", "F"));
}};
for (List<String> l : getCross(param)) {
System.out.println(l);
}
}
public static <T> List<List<T>> getCross(List<List<T>> values) {
List<List<T>> accumulator = new ArrayList<List<T>>();
if (values.size() != 0) {
List<T> comb = new ArrayList<T>();
comb.addAll(Collections.<T>nCopies(values.size(), null));
getCross(accumulator, 0, comb, values);
}
return accumulator;
}
private static <T> void getCross(List<List<T>> accumulator, int idx, List<T> combination, List<List<T>> param) {
if (idx == combination.size()) {
accumulator.add(new ArrayList<T>(combination));
} else {
for(T t : param.get(idx)) {
combination.set(idx, t);
getCross(accumulator, idx + 1, combination, param);
}
}
}
}
Below code will work for any kind of metric.
static void joinList()
{
List l11 = new ArrayList();
List l22 = new ArrayList();
List l33 = new ArrayList();
l11.add("a");
l11.add("b");
l22.add("c");
l22.add("d");
l33.add("e");
l33.add("f");
List<List> crosslist = new ArrayList<List>();
for(int i =0;i<l11.size();i++)
{
for(int j=0 ; j<l22.size();j++)
{
for(int k =0; k<l33.size();k++)
{
List list = new ArrayList();
list.add(l11.get(i));
list.add(l22.get(j));
list.add(l33.get(k));
crosslist.add(list);
}
}
}
}
try my solution,
you can use here as much as you need lists.
public class Main{
private void run() {
List<String> l11 = new ArrayList<String>(){{add("a");add("b");}};
List<String> l22 = new ArrayList<String>(){{add("c");add("d");}};
List<String> l33 = new ArrayList<String>(){{add("e");add("f");}};
crossJoin(l11, l22, l33);
}
public void crossJoin(List<String> ... lists) {
for (List<String> list : lists) {
mixList(list, lists);
}
}
private void mixList(List<String> list, List<String>[] lists) {
for (List<String> listSrt : lists) {
if (listSrt == list) continue;
for (String baseString : list) {
System.out.print(baseString + " ");
for (String outerStr : listSrt ) {
System.out.print(outerStr + " ");
}
System.out.println(" ");
}
}
}
public static void main(String[] args) {
Main main = new Main();
main.run();
}
}
I am trying to make a url from a different combinations of string separated by comma so that I can use those url to execute them and get the data back.
I have simplified something like this, I have a HashSet that will contain all my strings, not A,B,C in real. I just modified it here to make it simple.
Set<String> data = new HashSet<String>();
h.add("A");
h.add("B");
h.add("C");
for (int i = 1; i < 1000; i++) {
String pattern = generateString(data);
String url = "http://localhost:8080/service/USERID=101556000/"+pattern;
System.out.println(url);
}
/**
* Below is the method to generate Strings.
/
private String generateString(HashSet<String> data) {
//not sure what logic I am supposed to use here?
}
So the output should be something like this-
http://localhost:8080/service/USERID=101556000/A
http://localhost:8080/service/USERID=101556000/B
http://localhost:8080/service/USERID=101556000/C
http://localhost:8080/service/USERID=101556000/A,B,C
http://localhost:8080/service/USERID=101556000/B,C
http://localhost:8080/service/USERID=101556000/C,A
--
And other combinations
The above output can be in any random order as well. But it should be all the possible combinations. And if all the possible combinations are finished then start again.
Any suggestion how can I achieve the above problem definition?
What you're asking is not trivial.
Let's look at 2 strings, A and B.
Here are all of the permutations.
A
B
AB
BA
Ok, let's look at 3 strings, A, B, and C.
Here are all the permutations.
A
B
C
AB
AC
BA
BC
CA
CB
ABC
ACB
BAC
BCA
CAB
CBA
Do you see a pattern yet?
First, you have to find all of the single string permutations. Then the two string permutations. Then the three string permutations. And so on, up to the number of strings.
Then, within a set of permutations (like the two string set), you have to find all of the possible permutations.
You can do this with java loops. You can also use recursion.
Given what is k-arrangement (http://en.wikibooks.org/wiki/Probability/Combinatorics), you are looking for the k-arrangement where k varies from 1 to D, where D is the size of the data collections.
This means to compute - my first post I can't post image so look at equation located at :
In order to do it, you can make k varies, and the for each k may n varies (i.e. deal only with a sub array or data to enumerate the k-permutations). These k-permutations can be found by walking the array to the right and to the left using recursion.
Here is a quick bootstrap that proves to enumerate whart is required :
public class EnumUrl {
private Set<String> enumeration = null;
private List<String> data = null;
private final String baseUrl = "http://localhost:8080/service/USERID=101556000/";
public EnumUrl(List<String> d) {
data = d;
enumeration = new HashSet<String>(); // choose HashSet : handle duplicates in the enumeration process
}
public Set<String> getEnumeration() {
return enumeration;
}
public static void main(String[] args) {
List<String> data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
EnumUrl enumerator = new EnumUrl(data);
for (int k = 0; k < data.size(); k++) {
// start from any letter in the set
for (int i = 0; i < data.size(); i++) {
// enumerate possible url combining what's on the right of indice i
enumerator.enumeratePossibleUrlsToRight(data.get(i), i);
// enumerate possible url combining what's on the left of indice i
enumerator.enumeratePossibleUrlsToLeft(data.get(i), i);
}
// make a circular permutation of -1 before the new iteration over the newly combined data
enumerator.circularPermutationOfData();
}
// display to syso
displayUrlEnumeration(enumerator);
}
private void circularPermutationOfData() {
String datum = data.get(0);
for (int i = 1; i < data.size(); i++) {
data.set(i - 1, data.get(i));
}
data.set(data.size() - 1, datum);
}
private static void displayUrlEnumeration(EnumUrl enumerator) {
for (String url : enumerator.getEnumeration()) {
System.out.println(url);
}
}
private void enumeratePossibleUrlsToRight(String prefix, int startAt) {
enumeration.add(baseUrl + prefix);
if (startAt < data.size() - 1) {
startAt++;
for (int i = startAt; i < data.size(); i++) {
int x = i;
enumeratePossibleUrlsToRight(prefix + "," + data.get(x), x);
}
}
}
private void enumeratePossibleUrlsToLeft(String prefix, int startAt) {
enumeration.add(baseUrl + prefix);
if (startAt > 0) {
startAt--;
for (int i = startAt; i >= 0; i--) {
int x = i;
enumeratePossibleUrlsToLeft(prefix + "," + data.get(x), x);
}
}
}
}
The program outputs for {A,B,C} :
http://localhost:8080/service/USERID=101556000/B,C
http://localhost:8080/service/USERID=101556000/B,A,C
http://localhost:8080/service/USERID=101556000/B,C,A
http://localhost:8080/service/USERID=101556000/B,A
http://localhost:8080/service/USERID=101556000/C
http://localhost:8080/service/USERID=101556000/B
http://localhost:8080/service/USERID=101556000/C,B,A
http://localhost:8080/service/USERID=101556000/A,C,B
http://localhost:8080/service/USERID=101556000/A,C
http://localhost:8080/service/USERID=101556000/A,B
http://localhost:8080/service/USERID=101556000/A,B,C
http://localhost:8080/service/USERID=101556000/A
http://localhost:8080/service/USERID=101556000/C,B
http://localhost:8080/service/USERID=101556000/C,A
http://localhost:8080/service/USERID=101556000/C,A,B
And for {A,B,C,D} :
http://localhost:8080/service/USERID=101556000/B,A,D,C
http://localhost:8080/service/USERID=101556000/C,D
http://localhost:8080/service/USERID=101556000/A,D,C,B
http://localhost:8080/service/USERID=101556000/A,C,D
http://localhost:8080/service/USERID=101556000/D
http://localhost:8080/service/USERID=101556000/C
http://localhost:8080/service/USERID=101556000/A,C,B
http://localhost:8080/service/USERID=101556000/B
http://localhost:8080/service/USERID=101556000/A,B,C,D
http://localhost:8080/service/USERID=101556000/A,B,C
http://localhost:8080/service/USERID=101556000/D,C,B,A
http://localhost:8080/service/USERID=101556000/C,B,A,D
http://localhost:8080/service/USERID=101556000/A,B,D
http://localhost:8080/service/USERID=101556000/D,B
http://localhost:8080/service/USERID=101556000/D,C
http://localhost:8080/service/USERID=101556000/A
http://localhost:8080/service/USERID=101556000/D,C,A
http://localhost:8080/service/USERID=101556000/D,C,B
http://localhost:8080/service/USERID=101556000/C,D,A
http://localhost:8080/service/USERID=101556000/C,D,B
http://localhost:8080/service/USERID=101556000/D,A
http://localhost:8080/service/USERID=101556000/A,D,C
http://localhost:8080/service/USERID=101556000/A,D,B
http://localhost:8080/service/USERID=101556000/C,B,D
http://localhost:8080/service/USERID=101556000/B,A,D
http://localhost:8080/service/USERID=101556000/B,C
http://localhost:8080/service/USERID=101556000/B,A,C
http://localhost:8080/service/USERID=101556000/B,C,A
http://localhost:8080/service/USERID=101556000/B,A
http://localhost:8080/service/USERID=101556000/B,C,D
http://localhost:8080/service/USERID=101556000/C,B,A
http://localhost:8080/service/USERID=101556000/A,D
http://localhost:8080/service/USERID=101556000/D,A,B
http://localhost:8080/service/USERID=101556000/A,C
http://localhost:8080/service/USERID=101556000/D,A,C
http://localhost:8080/service/USERID=101556000/B,C,D,A
http://localhost:8080/service/USERID=101556000/A,B
http://localhost:8080/service/USERID=101556000/B,D
http://localhost:8080/service/USERID=101556000/C,D,A,B
http://localhost:8080/service/USERID=101556000/D,A,B,C
http://localhost:8080/service/USERID=101556000/D,B,A
http://localhost:8080/service/USERID=101556000/D,B,C
http://localhost:8080/service/USERID=101556000/B,D,A
http://localhost:8080/service/USERID=101556000/C,B
http://localhost:8080/service/USERID=101556000/C,A,D
http://localhost:8080/service/USERID=101556000/C,A
http://localhost:8080/service/USERID=101556000/B,D,C
http://localhost:8080/service/USERID=101556000/C,A,B
Which is not the exhaustive enumeration. Basically we should have:
(my first post I can't post image to look at equation located in my reply, I don't have the reputation to post 2 links ... #omg)
That makes 64 combinaisons, distributed as follows:
4 combinaisons of 1 element (k=1)
12 combinaisons of 12 element (k=2)
24 combinaisons of 24 element (k=3)
24 combinaisons of 24 element (k=4)
You can see that my program is OK for k=1, k=2, and k=3. But there are not 24 combinaisons for k=4. In order to complete the program, you will need to iterate also on other type of shuffling the data than circular permutation. Actually when k=4, circular permutation does not generate for instance ADBC as input data (hence DBCA cannot be generated by my implementation for instance). In this case, you will want to enumerate all possible data input array with n elements in all possible order. This is a special case of k-permutation, where k=n, and therefore leads to finding the n! permutation. We can achieve this by calling the EnumUrl method for each of the n! possible permutation.
For this, you should update EnumUrl enumerator = new EnumUrl(data); accordingly, but I guess I am letting some work for you to make :-)
HTH
A short version working for arbitrary set size, with generics, using guava, and the method for permutation given here.
Basically the idea is the following :
Generate the powerset, discard empty set
For each set of the powerset, generate all permutations
public class QuickEnumeration {
Set<T> objects;
public QuickEnumeration(Set<T> objects) {
this.objects = objects;
}
public List<List<T>> generateEnumeration() {
List<List<T>> result = new ArrayList<List<T>>();
// Compute the powerset
Set<Set<T>> powerset = Sets.powerSet(objects);
for (Set<T> set : powerset) {
// Discard empty set
if (set.size() > 0) {
// Arraylist faster for swapping
ArrayList<T> start = new ArrayList<T>(set);
permute(start, 0, result);
}
}
return result;
}
private void permute(ArrayList<T> arr, int k, List<List<T>> result) {
for (int i = k; i < arr.size(); i++) {
java.util.Collections.swap(arr, i, k);
permute(arr, k + 1, result);
java.util.Collections.swap(arr, k, i);
}
if (k == arr.size() - 1) {
result.add((List<T>) arr.clone());
}
}
public static void main(String[] args) {
Set<String> testSet = new HashSet<>();
testSet.add("A");
testSet.add("B");
testSet.add("C");
QuickEnumeration<String> enumerate = new QuickEnumeration<>(testSet);
System.out.println(enumerate.generateEnumeration());
}
}
Testing with "A","B","C" gives :
[[A], [B], [A, B], [B, A], [C], [A, C], [C, A], [B, C], [C, B], [A, B, C], [A, C, B], [B, A, C], [B, C, A], [C, B, A], [C, A, B]]
I am not entirely sure what you really want, so I ended up writing this piece of code for you. Hope it gets you started!
public static void doThis() {
String url1="http://www.example.com";
String string1="A";
String url2="http://www.foo.com";
String string2="B";
String url3="http://www.bar.com";
String string3="C";
Map<String, String> abbrMap = new HashMap<String, String>();
abbrMap.put(string1, url1);
abbrMap.put(string2, url2);
abbrMap.put(string3, url3);
String string = string1+string2+string3;
for(Map.Entry<String, String> m : abbrMap.entrySet()) {
arrange(string, m.getValue());
}
}
private static void arrange(String str, String url) {
if (str.length()==0) return;
StringBuffer sbuf = new StringBuffer();
for (int j=0; j<str.length(); j++) {
for(int i=j; i<str.length(); i++) {
char c = str.charAt(i);
sbuf.append(c);
System.out.println(url+"/"+sbuf.toString());
sbuf.append(",");
}
sbuf.setLength(0);
}
}
Output:
http://www.example.com/A
http://www.example.com/A,B
http://www.example.com/A,B,C
http://www.example.com/B
http://www.example.com/B,C
http://www.example.com/C
http://www.foo.com/A
http://www.foo.com/A,B
http://www.foo.com/A,B,C
http://www.foo.com/B
http://www.foo.com/B,C
http://www.foo.com/C
http://www.bar.com/A
http://www.bar.com/A,B
http://www.bar.com/A,B,C
http://www.bar.com/B
http://www.bar.com/B,C
http://www.bar.com/C
I try to sort only a selected part of an array.
I have try this code:
public static void shortArray(String[][] array){
Arrays.sort(array, new Comparator<String[]>() {
#Override
public int compare(final String[] entry1, final String[] entry2) {
final float time1 = Float.parseFloat(entry1[1]);
final float time2 = Float.parseFloat(entry2[1]);
if (time2 > time1) {
return 1;
} else if (time2 == time1) {
return 0;
} else {
return -1;
}
}
});
}
but it sorts the entire array.
Is there any way to sort starting from a specific index?
Yes, by creating a list-view of the array (through Arrays.asList), and by using list.subList and Collections.sort to sort a part of it.
String[][] arr = {{"5.0","a"},{"3.0","b"},{"2.0","c"},{"4.0","d"},{"1.0","e"}};
// Sort elements in range 2 ... arr.length
List<String[]> sublist = Arrays.asList(arr).subList(2, arr.length);
Collections.sort(sublist, new Comparator<String[]>() {
#Override
public int compare(String[] a1, String[] a2) {
return Float.valueOf(a1[0]).compareTo(Float.valueOf(a2[0]));
}
});
// Prints [[5.0, a], [3.0, b], [1.0, e], [2.0, c], [4.0, d]]
// ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// not affected sorted by first index
System.out.println(Arrays.deepToString(arr));
Take the sublist from the main list, starting from particular index(that you need). Then use
Collections.sort(sublist)
Hope that helps.
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