Reputation: 1281
I have two Sets - country and state. I want to create all possible permutations from both.
import java.util.*;
import java.util.stream.Collectors;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
Set<String> countryPermutations = new HashSet<>(
Arrays.asList("United States of america", "USA"));
Set<String> statePermutations = new HashSet<>(
Arrays.asList("Texas", "TX"));
Set<String> stateCountryPermutations = countryPermutations.stream()
.flatMap(country -> statePermutations.stream()
.flatMap(state -> Stream.of(state + country, country + state)))
.collect(Collectors.toSet());
Set<String> finalAliases = Optional.ofNullable(stateCountryPermutations)
.map(Collection::stream).orElse(Stream.empty())
.map(sc -> "houston " + sc)
.collect(Collectors.toSet());
System.out.println(stateCountryPermutationAliases);
}
}
The state or country or both permutations can be null. I still want my code to function.
Requirements
If state permutation is null, final output should be [Houston USA, Houston United States of America]
If country permutations is null, final output should be [Houston TX, Houston Texas]
If both are null, then no output
I'm changed my code to below
Set<String> stateCountryPermutations =
Optional.ofNullable(countryPermutations)
.map(Collection::stream)
.orElse(Stream.empty())
.flatMap(country -> Optional.ofNullable(statePermutations)
.map(Collection::stream)
.orElse(Stream.empty())
.flatMap(state -> Stream.of(state + country, country + state)))
.collect(Collectors.toSet());
This satisfies 3. When either permutation is null, 1 & 2 are not satisfied. I get no aliases as response. How do I modify my code?
Upvotes: 0
Views: 1813
Reputation:
Cartesian product of multiple non-empty lists using the map and reduce approach.
public static void main(String[] args) {
// a list of lists
List<List<String>> list = Arrays.asList(
Arrays.asList("houston"),
Arrays.asList("United States of america", "USA"),
Arrays.asList("Texas", "TX"),
null, Collections.emptyList());
// cartesian product
List<List<String>> cp = cartesianProduct(list);
// output
cp.forEach(System.out::println);
}
/**
* @param list the input list of lists
* @param <E> type of the element of the list
* @return cartesian product of multiple non-empty lists
*/
public static <E> List<List<E>> cartesianProduct(List<List<E>> list) {
// check if not null
if (list == null) return Collections.emptyList();
return list.stream()
// non-null and non-empty lists
.filter(lst -> lst != null && lst.size() > 0)
// represent each element of a list as a singleton list
.map(lst -> lst.stream().map(Arrays::asList)
// Stream<List<List<E>>>
.collect(Collectors.toList()))
// summation of pairs of list into a single list
.reduce((list1, list2) -> list1.stream()
// combinations of inner lists
.flatMap(inner1 -> list2.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());
}
Output:
[houston, United States of america, Texas]
[houston, United States of america, TX]
[houston, USA, Texas]
[houston, USA, TX]
See also: Generating all possible permutations of a list recursively
Upvotes: 0
Reputation: 4555
The following code creates all combinations from any number of input sets, ignoring null/empty sets:
Stream<Collection<String>> inputs = Stream.of(
Arrays.asList("United States of america", "USA"),
Arrays.asList("Texas", "TX"),
Arrays.asList("Hello", "World"),
null,
new ArrayList<>());
Stream<Collection<List<String>>> listified = inputs
.filter(Objects::nonNull)
.filter(input -> !input.isEmpty())
.map(l -> l.stream()
.map(o -> new ArrayList<>(Arrays.asList(o)))
.collect(Collectors.toList()));
Collection<List<String>> combinations = listified
.reduce((input1, input2) -> {
Collection<List<String>> merged = new ArrayList<>();
input1.forEach(permutation1 -> input2.forEach(permutation2 -> {
List<String> combination = new ArrayList<>();
combination.addAll(permutation1);
combination.addAll(permutation2);
merged.add(combination);
}));
return merged;
}).orElse(new HashSet<>());
combinations.forEach(System.out::println);
Output:
[United States of america, Texas, Hello]
[United States of america, Texas, World]
[United States of america, TX, Hello]
[United States of america, TX, World]
[USA, Texas, Hello]
[USA, Texas, World]
[USA, TX, Hello]
[USA, TX, World]
Now you can use your mentioned helper method to create the permutations of each combination. This question shows how to generate all permutations of a list.
Upvotes: 4
Reputation: 3357
If you want to create a data like this:
[text1=text2, text1=text3, text2=text3]
Here how you can do it:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class MainTest {
public static void main(String[] args) {
processData();
}
public static void processData() {
List<String> datalist = new ArrayList<>();
datalist.add("text1");
datalist.add("text2");
datalist.add("text3");
List<String> tempList = new ArrayList<>(datalist);
List<String> result = datalist.stream()
.flatMap(str1 -> getList(tempList, str1).stream().map(str2 -> str1 + "=" +str2))
.collect(Collectors.toList());
System.out.println(result);
}
private static List<String> getList(List<String> list, String obj){
list.remove(obj);
return list;
}
}
Upvotes: 0
Reputation: 298283
To rephrase your question, as far as I understood, you have several collections of, let’s call them labels, and create the permutation of all non-null
collections, producing an empty stream, if all are null
.
This can be done with a straight-forward logic, stream over all collections, filter out the null
elements, map them to Stream
s and reduce them to a single stream using a streamA.stream().flatMap(… -> streamB.map(combiner))
logic, except that streams cannot be used more than once. To solve this, we can implement it by applying the same logic to suppliers of streams. Another detail is that .map(combiner)
should be a -> streamB.flatMap(b -> Stream.of(combine a and b, combine b and a))
in your case.
Stream.of(stateLabels, countryLabels) // stream over all collections
.filter(Objects::nonNull) // ignore all null elements
.<Supplier<Stream<String>>>map(c -> c::stream) // map to a supplier of stream
.reduce((s1,s2) -> // combine them using flatMap and creating a×b and b×a
() -> s1.get().flatMap(x -> s2.get().flatMap(y -> Stream.of(x+" "+y, y+" "+x))))
.orElse(Stream::empty) // use supplier of empty stream when all null
.get() // get the resulting stream
.map("houston "::concat) // combine all elements with "houston "
.forEach(System.out::println);
To demonstrate with test cases:
// testcases
List<Collection<String>> countryLabelTestCases = Arrays.asList(
Arrays.asList("United States of america", "USA"),
null
);
List<Collection<String>> stateLabelTestCases = Arrays.asList(
Arrays.asList("Texas", "TX"),
null
);
for(Collection<String> countryLabels: countryLabelTestCases) {
for(Collection<String> stateLabels: stateLabelTestCases) {
// begin test case
System.out.println(" *** "+(
countryLabels==null? stateLabels==null? "both null": "countryLabels null":
stateLabels==null? "stateLabels null": "neither null"
)+":"
);
// actual operation:
Stream.of(stateLabels, countryLabels)
.filter(Objects::nonNull)
.<Supplier<Stream<String>>>map(c -> c::stream)
.reduce((s1,s2) -> () -> s1.get().flatMap(x ->
s2.get().flatMap(y -> Stream.of(x+" "+y, y+" "+x))))
.orElse(Stream::empty)
.get()
.map("houston "::concat)
.forEach(System.out::println);
// end of operation
System.out.println();
}
}
*** neither null:
houston Texas United States of america
houston United States of america Texas
houston Texas USA
houston USA Texas
houston TX United States of america
houston United States of america TX
houston TX USA
houston USA TX
*** stateLabels null:
houston United States of america
houston USA
*** countryLabels null:
houston Texas
houston TX
*** both null:
If you want to get the permutations as lists rather than strings, create this helper method
static <T> List<T> merge(List<T> a, List<T> b) {
return Stream.concat(a.stream(), b.stream()).collect(Collectors.toList());
}
and change the stream operation to
Stream.of(stateLabels, countryLabels)
.filter(Objects::nonNull)
.<Supplier<Stream<List<String>>>>map(c ->
() -> c.stream().map(Collections::singletonList))
.reduce((s1,s2) -> () -> s1.get().flatMap(x ->
s2.get().flatMap(y -> Stream.of(merge(x,y), merge(y,x)))))
.orElse(Stream::empty)
.get()
.map(list -> merge(Collections.singletonList("houston"), list))
// proceed processing the List<String>s
Mind that for supporting more than the two collections, you only have to change Stream.of(stateLabels, countryLabels)
, inserting the other collections.
Upvotes: 1