2017-11-14 1 views
0

Ich habe zwei Sets - Land und Staat. Ich möchte alle möglichen Permutationen von beiden erstellen.String-Permutationen aus mehreren Set-Werten generieren (Java 8 Streams)

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

Der Status oder das Land oder beide Permutationen können null sein. Ich möchte immer noch, dass mein Code funktioniert.

Anforderungen

  1. Wenn staatliche Permutation null ist, sollte die endgültige Ausgabe [Houston USA, Houston Vereinigte Staaten von Amerika]

  2. werden, wenn Land Permutationen null ist, sollte die endgültige Ausgabe sein [Houston TX Houston Texas,]

  3. Wenn beide null sind, dann keine Ausgabe

ich meinen Code unten

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

Dies erfüllt 3. Wenn entweder Permutation ist null geändert, 1 & 2 nicht erfüllt sind. Ich bekomme keine Aliase als Antwort. Wie ändere ich meinen Code?

+0

ich das letzte Codebeispiel versucht habe, und ich emtpy Ergebnisse, wenn ein oder beiden Sätze null (oder leer), dh es nur erfüllt 3. Dies ist kein surpise, wie Sie entweder Karte ** keine ** Länder in dem Zustand Streams, oder einige Länderströme, um Streams zu leeren, oder ** nichts **, um Streams zu leeren. Um dies zu lösen, könnten Sie ein Null-Objekt (nicht null selbst, aber etwas wie eine leere Zeichenfolge) als Platzhalter für "kein Land" oder "kein Zustand" einführen. Oder überprüfen Sie einfach vor dem Stream und verwenden Sie einfach die Eingabesätze als Ergebnis ("if (countries == null) permutations = states;"). –

+0

danke @MalteHartwig, habe meinen Beitrag aktualisiert. Könnten Sie bitte eine Antwort mit Code schreiben –

+0

Um zu vereinfachen habe ich 2 Sätze verwendet. Mein tatsächlicher Code erfordert das Permutieren durch 4 Sätze. if-else würde zu unordentlich werden –

Antwort

2

Der folgende Code erstellt alle Kombinationen aus einer beliebigen Anzahl von Eingabesätzen, null/leer Sätze ignoriert:

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

Ausgang:

[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] 

Jetzt können Sie Ihre genannten Helfer-Methode verwenden, die erstellen Permutationen jeder Kombination. This question zeigt, wie alle Permutationen einer Liste generiert werden.

+0

viel Aufwand hier ... +1 – Eugene

1

Ihre Frage neu zu formulieren, soweit ich verstand, Sie mehrere Sammlungen von haben, lassen Sie sie sie Etiketten nennen, und die Permutation aller nicht null Sammlungen erstellen, einen leeren Strom erzeugt wird, wenn alle null sind.

Dies kann mit einer Straight-Forward-Logik durchgeführt werden, um alle Sammlungen streamen, herauszufiltern, die null Elemente, Karte, sie zu Stream s und reduzieren sie zu einem einzigen Strom einer streamA.stream().flatMap(… -> streamB.map(combiner)) Logik, mit der Ausnahme, dass nicht verwendet Strömen kann mehr sein als einmal. Um dies zu lösen, können wir es implementieren, indem wir die gleiche Logik auf Lieferanten von Streams anwenden. Ein weiteres Detail ist, dass .map(combiner)a -> streamB.flatMap(b -> Stream.of(combine a and b, combine b and a)) in Ihrem Fall sein sollte.

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

mit Testfällen zu demonstrieren:

// 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: 

Wenn Sie die Permutationen als Listen anstatt Strings erhalten mögen, erstellen die diese Hilfsmethode

static <T> List<T> merge(List<T> a, List<T> b) { 
    return Stream.concat(a.stream(), b.stream()).collect(Collectors.toList()); 
} 

und ändern Strombetrieb zu

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 

Geist, die mehr als die zwei Sammlungen für die Unterstützung, nur Stream.of(stateLabels, countryLabels) ändern müssen, die anderen Sammlungen einfügen.

+0

Danke @Holger so viel dafür. Wie müsste die Reduce-Funktion neu geschrieben werden, um mehr als 2 Streams zu integrieren? –

+0

Sie müssen nichts neu schreiben. Ersetzen Sie einfach 'Stream.of (stateLabels, countryLabels)' 'mit Stream.of (collection1, collection2, collection3, Collection4)', etc – Holger

+0

Ein mögliches Problem, je nachdem, was @PranavKapoor will: Wenn mehr als zwei Listen verwenden, der Zusammenführung Funktion wird nicht alle Permutationen erzeugen: Eingabe '[[a], [b], [c]]' ergibt '[a, b, c], [c, a, b], [b, a, c] , [c, b, a] ', vermischt aber nicht die Elemente der dritten (und vierten ...) Liste in der Mitte der vorherigen Kombinationen. Man könnte damit umgehen, indem man nur mit 'Stream.of (merge (x, y)) 'reduziert und dann die Permutationen erhält. [Diese Fragen] (https://stackoverflow.com/questions/10305153/generating-all-possible-permutations-of-a-list-recursively) zeigt, wie das gemacht werden kann. –