Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to group a list of list using groovy

Tags:

groovy

I have a list of list that I want to group by marketName and commodityName, by listing price: e.g. ["Terra", "Wheat", 1000.0] - "Terra" is Market name, "Wheat" is Commodity Name, 1000.0 is price

def marketCommodityGroup = [
                              ["Merkato", "Wheat", 1000.0],  
                              ["Shola", "Wheat", 1875.0],  
                              ["Merkato", "Barley", 5000.0],  
                              ["Merkato", "Wheat", 1000.0],  
                              ["Merkato", "Wheat", 1500.0] 
                           ] 

I would like the output to be:

[
   ["Merkato": ["Wheat" : [1000.0, 1000.0, 1500.0]]],
   ["Merkato": ["Barley": [5000.0]]],
   ["Shola": ["Wheat": [1875.0]]]
]
like image 510
soso Avatar asked Dec 04 '22 09:12

soso


2 Answers

Alright, here's one way of doing it.

def mapped = marketCommodityGroup.groupBy {
  [(it[0]) : it[1]]
}.collect { k,v ->
  def grouping = k.find { true }
  def prices = v.inject([]) { acc,val -> acc + val[2] }
  [ (grouping.key) , [ (grouping.value) : prices ] ]
}.sort { left, right ->
  right[0] <=> left[0]
}.collect {
  [(it[0]) : it[1] ]
}
  • first groupBy does exactly what you said, it groups by the market name and commodity name
  • collect creates the wanted structure excluding the final k:v association:
    • grouping is the only entry in the key map split so that it can be reordered to desired form
    • prices is done with the very handy inject which is Groovy's equivalent for the fold left operation in functional languages
  • sort is for flipping the order as you specified - had to quess what the actual logic is so you may want to replace it
  • last collect does the final map assignment to get the exact wanted form

Yes, it's a bit dense and magical but you can always move the closures to defs with proper, descriptive names.

like image 66
Esko Avatar answered Dec 15 '22 01:12

Esko


[Edit: now returns list of maps, per original question]

Somewhat influenced by Vamsi Krishna's answer, but with chained withDefault :

def marketCommodityGroup = [
                              ["Terra", "Wheat", 1000.0],  
                              ["Shola", "Wheat", 1875.0],  
                              ["Terra", "Barley", 5000.0],  
                              ["Terra", "Wheat", 1000.0],  
                              ["Terra", "Wheat", 1500.0] 
                           ]

def marketCommodityMap = [:].withDefault{ [:].withDefault{ [:].withDefault {[]} } }

// map looks like
// ["Terra-Wheat": ["Terra": ["Wheat": [1000.0 ...], "Barley": [5000.0] ]]]
// but we will discard the outer compound key                            
marketCommodityGroup.each { market, commodity, price ->    
    marketCommodityMap["${market}-${commodity}"][market][commodity] << price
}

def listOfMaps = marketCommodityMap.values()
println listOfMaps
like image 24
Michael Easter Avatar answered Dec 15 '22 00:12

Michael Easter