使用 groovy 以纯函数方式解决排名问题

Solving a ranking problem in a purely functional way using groovy

我解决了一个水果按票数排序的问题。不幸的是,我想在不改变 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 变量并改变其状态的情况下解决此问题。请注意,此解决方案有效,但我后来了解到我不应该这样做。 我希望能够用正确的排名来填充排名。 inject 函数进行了累加,但我不知道如何以某种方式将它结合起来,以设置在 inject 中累加的值的排名。

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

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"

任何人都可以然后我会很感激你的解决方案,或者只是假设我的尝试是解决这个问题的最现实的方法。

你可以试试这个:

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()
})

但这两种解决方案都不是真正有效的。真正的功能代码将所有值视为不可变的。请参阅我的其他答案(即将发布)以获得真正的功能解决方案。

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

  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"

最后的结果是这样的 分类的水果是 [[name:lemons, votes:20, ranking:1], [name:guava, votes:20, ranking:1], [name:菠萝,votes:50,ranking:3],[name:杏,votes:66,ranking:4],[ name:plum、votes:66、ranking:4]等

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

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]