使用groovy以纯功能方式解决排名问题

时间:2019-01-31 22:32:37

标签: groovy functional-programming ranking

我解决了一个根据票数对水果进行排名的问题。不幸的是,我想以一种纯粹的功能性方式解决该问题,而无需更改rankPosition变量。这是我的解决方案:

  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"

我如何解决此问题而不必在闭包外部声明rankingPosition变量并更改其状态。请注意,此解决方案有效,但是自那以后,我了解到我不应该这样做。 我希望能够用正确的排名来填充排名。注入函数会进行累加,但是我不知道如何以某种方式将其组合起来,从而也可以使用注入中累积的值来设置排名。

我只是被困住了,只是似乎无法对此做出推理。我在下面尝试使用注入的尝试根本没有用。也许没有办法以纯粹的功能方式做到这一点,更好的是我的尝试。

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"

那么我每个人都可以感谢您的解决方案,或者只是假设我的尝试是解决这一问题的最现实的方法。

3 个答案:

答案 0 :(得分:0)

您可以尝试以下方法:

def results = groupedByVotes.sort()
    .inject(new Tuple(1, []), { acc, entry ->
        entry.value.each { it.ranking = acc[0] }
        return new Tuple(acc[0] + entry.value.size(), acc[1] << entry.value)
    })

finalResults = results[1]

在折叠的每个步骤(由.inject(...)完成)中,您都有一个Tuple,其中包含下一排和到目前为止已计算的部分列表。最后一步是从Tuple中提取结果列表。但这会将地图转换为列表。

此解决方案甚至更简单,因为如果就地修改了旧集合,则不必创建新集合,并且可以保留地图:

def finalResults = groupedByVotes.sort()
finalResults.inject(1, { acc, entry ->
    entry.value.each { it.ranking = acc }
    return acc + entry.value.size()
})

但是这两种解决方案都没有真正起作用。实际的功能代码将所有值视为不变。请参阅我的其他答案(即将推出)以获取真正的功能解决方案。

答案 1 :(得分:0)

我可能没有很好地解释我要达到的目标。在计算排名之后,我希望为列表中的各个元素插入值。这是我想出的:

  def sortedFruits= fruits.sort{f1, f2 -> f1.votes <=> f2.votes}

  (0..sortedFruits.size()-1)
   .each{ i ->
    if(i==0){
    sortedFruits.get(i).ranking=1
    }else if(sortedFruits.get(i-1).votes==sortedFruits.get(i).votes){
      sortedFruits.get(i).ranking=i
   }else{
    sortedFruits.get(i).ranking=i+1
  }
   if(i<sortedFruits.size()){
     def f= sortedFruits.get(i)
      println "${f}"
  }

}

 println "Sorted Fruits are $sortedFruits"

最终结果看起来像 排序的水果是[[名称:柠檬,投票:20,排名:1],[名称:番石榴,投票:20,排名:1],     [名称:菠萝,投票:50,排名:3],[名称:杏,投票:66,排名:4],[     名称:李子,投票数:66,排名:4]等。

答案 2 :(得分:0)

这是一个纯功能解决方案。它将原始地图的地图保持不变,并生成一个新的地图:

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]