Groovy: как сгруппировать значения карты по 2 различным критериям? - PullRequest
6 голосов
/ 26 января 2012

Как можно сгруппировать значения карты по 2 различным критериям, чтобы получить выходные данные ниже?

def listOfMaps = [
  [name:'Clark', city:'London', hobby: 'chess'], [name:'Sharma', city:'London', hobby: 'design'],
  [name:'Maradona', city:'LA', hobby: 'poker'], [name:'Zhang', city:'HK', hobby: 'chess'],
  [name:'Ali', city: 'HK', hobby: 'poker'], [name:'Liu', city:'HK', hobby: 'poker'],
  [name:'Doe', city:'LA', hobby: 'design'], [name:'Smith', city:'London', hobby: 'poker'],
  [name:'Johnson', city: 'London', hobby: 'poker'], [name:'Waters', city:'LA', hobby: 'poker'],
  [name:'Hammond', city:'LA', hobby: 'design'], [name:'Rogers', city:'LA', hobby: 'chess'],
]
  1. групповой порядок: хобби, город

    poker
        London
            Smith
            Johnson
        LA
            Maradona
            Waters
        HK
            Ali
            Liu
    design
        London
            Sharma
        LA
            Doe
            Hammond
        HK
    chess
        London
            Clark   
        LA
            Rogers
        HK
            Zhang   
    
  2. групповой порядок: город, хобби

    London
        poker
            Smith
            Johnson
        design
            Sharma
        chess
            Clark
    LA
        poker
            Maradona
            Waters
        design
            Doe
            Hammond
        chess
            Rogers
    
    HK
        poker
            Ali
            Liu
        design
        chess
            Zhang
    

Редактировать:

Мне действительно нужен способ итерации кэффективно проходить по структуре группы и иметь возможность построить результат (группа / подгруппа / имя).

Что-то вроде:

  1. для каждой группы, распечатать / вывести имя группы;
  2. для каждой подгруппы внутри группы, распечатать / вывести имя подгруппы
  3. внутри каждой подгруппы, распечатать имена.

Это даст результат, описанный выше.

Как приятно, я бы хотел отсортировать всю структуру данных (группы и имена).

Ответы [ 3 ]

15 голосов
/ 26 января 2012

Для первого случая:

result = map.groupBy( { it.hobby }, { it.city } )

и для второго:

result = map.groupBy( { it.city }, { it.hobby } )

В итоге вы получите исходные значения на карте, а не просто имя, но вы сможете сделать:

result[ 'poker' ][ 'HK' ].name

чтобы получить результат

["Ali", "Liu"]

Кстати: эта форма groupBy была доступна только начиная с Groovy 1.8.1, поэтому, если вы застряли на более ранней версии, она не будет работать

редактировать 2

Исходя из вашего комментария ниже, вы можете сделать:

result.each { a, b ->
  println "$a"
  b.each { c, d ->
    println "  $c"
    d.each {
      println "    $it.name"
    }
  }
}

Это та же логика, что и в GVdP в его ответе, но я чувствую, что использование groupBy с несколькими параметрами, как здесь, делает ваш код более читабельным и понятным с точки зрения его намерений

2 голосов
/ 26 января 2012

Если вы хотите, чтобы результатом была пара вложенных карт, попробуйте что-то вроде этого:

def byHobbyCity = map.groupBy{it.hobby}.collectEntries{k, v -> [k, v.groupBy{it.city}]}
def byCityHobby = map.groupBy{it.city}.collectEntries{k, v -> [k, v.groupBy{it.hobby}]}

println byHobbyCity['chess']['London']*.name
==> [Clark]

println byCityHobby['London']['chess']*.name
==> [Clark]
1 голос
/ 27 января 2012

Отвечая на основании ваших комментариев, это будет делать так, как вы просите:

def listOfMaps = [
  [name:'Clark', city:'London', hobby: 'chess'], [name:'Sharma', city:'London', hobby: 'design'],
  [name:'Maradona', city:'LA', hobby: 'poker'], [name:'Zhang', city:'HK', hobby: 'chess'],
  [name:'Ali', city: 'HK', hobby: 'poker'], [name:'Liu', city:'HK', hobby: 'poker'],
  [name:'Doe', city:'LA', hobby: 'design'], [name:'Smith', city:'London', hobby: 'poker'],
  [name:'Johnson', city: 'London', hobby: 'poker'], [name:'Waters', city:'LA', hobby: 'poker'],
  [name:'Hammond', city:'LA', hobby: 'design'], [name:'Rogers', city:'LA', hobby: 'chess'],
]

listOfMaps.groupBy {it.hobby}.each { k,v ->
 println k
 v.groupBy {it.city}.each {k1, v1->
  println "  $k1"
  v1.each {
   println "    $it.name"
  }
 }
}

Протестировано с Groovy 1.7.10

...