I have solved a problem that ranks fruits by the number of votes. Unfortunately, I want to solve the problem in a purely functional way without mutating the rankPosition variable. Here is my solution:
def fruits=[
[name:'apple', votes:120 , ranking:null ],
[name:'banana', votes:200, ranking: null],
[name:'apricot', votes:66, ranking:null ],
[name:'pear', votes:84, ranking:null],
[name:'kiwi', votes:77, ranking:null],
[name:'plum', votes:66, ranking:null],
[name:'berry', votes:120, ranking:null],
[name:'pineapple', votes:50, ranking:null],
[name:'grapes', votes:200, ranking:null]
]
def rankPosition= 1
def groupedByVotes = fruits.groupBy {it.votes }
println "Ratings $groupedByVotes"
def finalResults=groupedByVotes.sort().each { votes, items ->
items.each { it.ranking = rankPosition }
rankPosition += items.size()
}
println "Final Results are $finalResults"
How can I solve this problem without having to declare a rankingPosition variable external to the closure and mutating its state. Please notes that this solution works but I have since learned that I shouldn't be doing it this way. I want to be able to fill the rankings with the correct ranking. The inject function does an accumulation but I don't know how to combine it in a way to also set the ranking with the value accumulated in the inject.
I am simply stuck, just don't seem to be able to reason about this one. My attempt below to use inject, simply did not work. Maybe there isn't a way to do this in a purely functional way, better thsn my attempt.
def res= groupedByVotes.collectEntries{votes, list1->
println "list class $list1"
def r= list1.inject(0){acc,l-> acc+l.size()}
list1.each{it.ranking=r}
println "$r"
[(votes): list1]
}
println "$res"
I anyone can then I would appreciate your solution or just assume my attempt is the most realistic way of solving this one.
This is a pure functional solution. It leaves the initial map of maps unchanged and produces a new one:
def results = groupedByVotes.sort().inject(new Tuple(1, [:]), { acc, entry ->
def newRank = acc[0] + entry.value.size()
def newValue = entry.value.collect { [*:it, ranking:acc[0]] }
return new Tuple(newRank, [*:acc[1], (entry.key):newValue] )
})
finalResults = results[1]