2016-08-01 3 views
17

人のリストをグループ化したい。GroupByコレクターを手動でチェーンする

Object groupedData = data.stream().collect(groupingBy(Person::getName, Collectors.groupingBy(Person::getCountry, Collectors.groupingBy(Person::getTown)))); 

しかし、問題は、それが動的ではないこと、である:人は、私は非常にうまく機能静的コードを書いた名前、国、町、郵便番号、などのようないくつかの属性を持ちます。時には名前と町でグループ化したいときもあれば、属性によってグループ化することもあります。これどうやってするの? Java 8以外のソリューションも歓迎します。

+0

「時々、名前と町によって、時には属性によってグループ化したいと思っています。 - ユーザーの入力や何に応じて? – Smutje

+1

も参照してくださいhttp://stackoverflow.com/a/34772639/1587046 –

答えて

11

グループ化する任意の数の属性を持つ関数を作成して、groupingBy - Collectorをループ内に作成し、毎回自分自身の前のバージョンをdownstreamコレクタとして渡すことができます。

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) { 
    Iterator<Function<T, ?>> iter = Arrays.asList(groupers).iterator(); 
    Collector collector = Collectors.groupingBy(iter.next()); 
    while (iter.hasNext()) { 
     collector = Collectors.groupingBy(iter.next(), collector); 
    } 
    return (Map) data.stream().collect(collector); 
} 

あなたは逆の順序で渡す(または関数内にそれらを逆に、例えば、あなたが好む方、Collections.reverseを用いて、またはグアバのLists.reverse)しなければならないので、grouper機能の順序が逆であることに留意されたいです。

Object groupedData = collectMany(data, Person::getTown, Person::getCountry, Person::getName); 

またはこのような、機能の配列を逆にする古い学校forループを使用して、あなたは逆の順序でハタを渡す必要はありません(ただし、IMHO、これは理解しにくい)例:

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) { 
    Collector collector = Collectors.groupingBy(groupers[groupers.length-1]); 
    for (int i = groupers.length - 2; i >= 0; i--) { 
     collector = Collectors.groupingBy(groupers[i], collector); 
    } 
    return (Map) data.stream().collect(collector); 
} 

どちらの方法も元のコードと同じようにMap<?,Map<?,Map<?,T>>>を返します。そのデータで何をしたいのかに応じて、TunakiのようにMap<List<?>,T>を使用することを検討する価値があります。

+0

保存しました。素敵でスムーズに動作します。ありがとう! –

+0

イテレータの定型文をすべて使用するのはなぜですか?あなたは単に配列の上に置くことができます – Clashsoft

+1

@Clashsoft問題は、最初のエントリを別々に扱わなければならないということです。私の最初のバージョンは 'foreach'でしたが、最初のエントリを処理するためには三項が必要でした。 –

8

グループ化する属性で構成されるリストでグループ化できます。

名前と国をグループ化するとします。

Map<List<Object>, List<Person>> groupedData = 
    data.stream().collect(groupingBy(p -> Arrays.asList(p.getName(), p.getCountry()))); 

これは、同じ要素が同じ順序で含まれている場合、2つのリストが等しいとみなされるためです。したがって、結果の地図には、異なる名前/国のペアごとにキーがあり、その特定の名前と国の人のリストが値として表示されます。別の言い方をすると、「名前でグループ化し、国別にグループ化する」というよりも、「名前と国でグループ化する」と効果的に言います。利点は、地図などの地図のマップで終わらないことです。

+0

"利点は、地図の地図のマップで終わらないということです。"ということは、OPが実際に何をしているのではないかと仮定しているからです... –

+2

その要求は、@tobias_kという質問の一部ではありませんでした。キー、値のみを持つマップは、内部マップのレベルが不明なマップを使用する方が簡単です。 – Tunaki

+0

オペレーションズ・コードによれば、OPのコードは現在、「とてもうまくいく」という意味での質問の一部です。とにかく、どのようなアプローチをとるべきかは、どのようにデータを使いたいかにかかっていると思いますし、あなたのアプローチが扱いやすいシナリオもあります。 –

関連する問題