2017-05-05 2 views
3

해결할 수없는 두 가지 문제가 있습니다. 첫 번째는 사용자가 전달할 수있는 1-n 중첩 된 그룹이있을 수있는 곳에서 동적 중첩 그룹화를 수행하는 방법이 필요하다는 것입니다.Java 8 중첩 그룹화

두 번째 문제는 키가있는 위치가 평탄해야한다는 것입니다. concat보다는 중첩 된.

내 예를 들어, 입력은 다음과 같습니다 : 키가 맵 내에 중첩 된지도보다는 CONCAT 있습니다

{ 
    "10.0.1.1user": [ 
    { 
     "uid": "tiger", 
     "ip": "10.0.1.1", 
     "group": "user" 
    }, 
    { 
     "uid": "woods", 
     "ip": "10.0.1.1", 
     "group": "user" 
    } 
    ], 
    "10.0.1.0admin": [ 
    "uid": "root", 
    "ip": "10.0.1.0", 
    "group": "admin" 
    ] 
} 

주의 사항 :

List<Map<String, String>> fakeData = new LinkedList<>(); 
    Map<String, String> data1 = new HashMap<>(); 
    data1.put("ip","10.0.1.0"); 
    data1.put("uid","root"); 
    data1.put("group","admin"); 
    fakeData.add(data1); 

    Map<String, String> data2 = new HashMap<>(); 
    data2.put("ip","10.0.1.1"); 
    data2.put("uid","tiger"); 
    data2.put("group","user"); 
    fakeData.add(data2); 

    Map<String, String> data3 = new HashMap<>(); 
    data3.put("ip","10.0.1.1"); 
    data3.put("uid","woods"); 
    data3.put("group","user"); 
    fakeData.add(data3); 

최종 결과는지도 키의 CONCAT 있습니다.

나는 그것이 운이없이 동적 일 수 어디에 groupingby을 만들려고 해요 : 약

public Map<String, List<Map<String, String>>> doGrouping(List<String> columns, 
                List<Map<String, String>> data); 
+1

구현하려는 메소드의 입/출력을 완전히 이해하고 있는지 잘 모르겠습니다. 출력 유형에 "n"중첩 그룹이 포함되어 있지는 않습니다. 예제 데이터로'doGrouping'을 어떻게 호출 할 것인지, 그리고 예상되는 결과는 무엇인지 보여줄 수 있습니까? – 4castle

+0

doGrouping의'columns' 인자에'N' 엘리먼트가 포함되어 있다면'N' 엘리먼트의 각각에 의해 중첩 된 그룹을 만들어야한다는 말입니까?그리고 각 그룹의 키가 중첩되지 않고 연결되도록하고 싶습니까? –

+2

출력 또는 인터페이스에서 n 레벨 중첩을 제안하는 항목이 표시되지 않습니다. 최상위 수준의 여러 키로 그룹화 된 것처럼 보입니다. – shmosel

답변

5

은 다음을 시도해보십시오

public Map<String, List<Map<String, String>>> doGrouping(
     List<String> columns, 
     List<Map<String, String>> data) { 

    return data.stream() 
     .collect(Collectors.groupingBy(
      elem -> columns.stream() 
       .map(elem::get) 
       .collect(Collectors.joining()))); 
} 

첫째,지도의 목록입니다 data을 스트리밍. 즉시 스트림의 각 요소에 대해 계산 된 키를 사용하여 Collectors.groupingBy을 사용하여 목록의지도로 스트림을 수집했습니다.

키 계산은 까다로운 부분이었습니다. 이를 위해 columns의 목록을 스트리밍하고 각 열을 스트림의 해당 요소 값으로 변환했습니다. 이 방법은 Stream.map 메서드를 사용하여 elem::map을 매핑 함수로 전달했습니다. 마지막으로 Collectors.joining을 사용하여이 내부 스트림을 단일 문자열로 수집했습니다.이 스트림은 스트림의 각 요소를 효율적인 방식으로 최종 문자열로 연결합니다.

편집 : 위의 코드는 columns의 모든 요소가 data의지도 요소 키로 존재하면 잘 작동합니다.

return data.stream() 
    .collect(Collectors.groupingBy(
     elem -> columns.stream() 
      .map(elem::get) 
      .filter(Objects::nonNull) 
      .collect(Collectors.joining()))); 

이 버전은 몇 가지지도 요소가 columns 목록에 지정된 키를 포함하지 않는 경우 발생할 수있는 스트림에서 null 요소를 필터링 : 더 안전한 사용을 다음을합니다.

+2

'c -> elem.getOrDefault (c, "")'를 사용할 수도 있습니다. – shmosel

+0

@shmosel 그러나 올바른지, 빈 문자열을 터미널 작업에 전파합니다 (올바른 용어입니까?). 이 경우에는 빈 문자열을 조인하면 마지막으로 조인 된 문자열에 아무런 영향을 미치지 않으므로 작업 할 수 있습니다. 그러나 스트림에서 요소를 수집 중에 필터링하는 것이 가장 좋지 않다고 생각했습니다. –

+1

완벽! 페데리코 정말 고마워. 그것은 트릭을했다. – Xuan

2

확실하지 : 이것은 구현하기 위해 노력하고있어 인터페이스가

fakeData.stream() 
       .collect(groupingBy(map -> map.get("ip"), 
         groupingBy(map -> map.get("uuid"), 
           ... nested "n" times))); 

스트림을 사용하지만 일반 자바 방식을 선호하는 경우 훨씬 쉽습니다. 여기에 문제를 올바르게 이해하려면 구축 할 방법이 있습니다. 좀 더 빨리 수정해야 할 수도 있습니다.

public Map<String, List<Map<String, String>>> doGrouping(List<String> columns, List<Map<String, String>> data) { 
    Map<String, List<Map<String, String>>> output = new HashMap<>(); 
    for (Map<String, String> map : data) { 
     String key = ""; 
     for(String column : columns) key += "".equals(key) ? (map.get(column)) : (":" + map.get(column)); 
     output.computeIfAbsent(key, k -> Arrays.asList(map)); 
    } 
    return output; 
} 

테스트 :

doGrouping(Arrays.asList("ip", "group"), fakeData) 
>> {10.0.1.1:user=[{uid=tiger, ip=10.0.1.1, group=user}, {uid=woods, ip=10.0.1.1, group=user}], 10.0.1.0:admin=[{uid=root, ip=10.0.1.0, group=admin}]} 

doGrouping(Arrays.asList("group"), fakeData) 
>> {admin=[{uid=root, ip=10.0.1.0, group=admin}], user=[{uid=tiger, ip=10.0.1.1, group=user}, {uid=woods, ip=10.0.1.1, group=user}]} 
+2

Upvoted, 정답입니다. 그냥 제안 ... 당신의'if/else' 블록을'output.computeIfAbsent (key, k -> 새로운 ArrayList <>()) .add (map)'로 바꿀 수 있습니다. –

+0

그건 좋은 제안 인 것 같습니다. 지금 당장 통근하고, 잠시 후에 다시 시도해보고 –