当groovy clousure的最大值超过一个时,更改映射键

时间:2018-10-14 20:22:39

标签: groovy

我有两张地图:

def map = ['a': 3, 'b': 4, 'c':5]
def map2= ['a': 3, 'b': 4, 'c':4]

我想像这样获取地图的最大值:

def newMap = map.max {it.value}

并且我的输出是正确的('c':5),我的问题是第二张地图,因为有多个最大值。在这种情况下,我想更改密钥,这样我就可以知道存在多个最大值。我希望输出为:

def newMap2 = map2.max {it.value}
assert newMap2 == ['+than1': 4]

在这种特殊情况下,我可以使用groovy函数更改地图的键吗? 我可以在max封闭内实现此目标吗?

如果不超过一个最大值,我不想更改密钥。

2 个答案:

答案 0 :(得分:2)

请记住,map.max(Closure cl)返回Map.Entry<String, Integer>而不是地图。因此,如果您期望使用一个键的地图,则必须从获得的结果中创建一个。

Map.max(Closure cl)搜索最大值并返回。没有任何变体可以让您在两个条目保持最大值的情况下修改函数行为。根据文档:

  

选择地图中具有最大数量的条目   由提供的闭合确定的计算值。   如果多个条目具有最大值,   在具有最大值的条目之间进行任意选择。

在实践中:返回找到的第一个具有最大值的条目。

Groovy提供了另一种收集功能,可以用来实现您期望的功能-Collection.inject(initialValue, closure)功能。这等效于在函数式编程范例中众所周知的流行fold函数。它以某个初始值开始,并迭代一个集合并将一个函数应用于每个元素(此函数返回一个新值,该值替换作为初始值传递的值),以在一次迭代中将元素列表简化为单个元素。 Collection.inject() javadoc给出了一个非常有描述性的示例,将列表中的所有数字相加:

assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val -> acc + val }

现在让我们看一下如何使用此功能来达到预期的结果。考虑以下示例:

def map2= ['a': 3, 'b': 4, 'c':4]

Tuple2<String, Integer> result = map2.inject(new Tuple2<String, Integer>(null, null)) { Tuple2<String, Integer> tuple, entry ->
    entry.value >= tuple.second ?
            new Tuple2<>(entry.value == tuple.second ? '+1than1' : entry.key, entry.value) :
            tuple
}

assert result.first == '+1than1'
assert result.second == 4

这里,我们以new Tuple2<String, Integer>(null, null)作为初始值-Tuple2代表两个值对。在我们的例子中,它代表一个键和最大值。闭包检查当前输入值是否大于或等于我们存储在元组中的值,如果是,则检查值是否与我们已经找到的值相同-如果是,则使用+than1作为键,而不是从地图上获取的键。否则,它仅使用进入键而不进行任何修改。当该值小于当前我们存储在元组中的值时,将返回现有元组。最后,我们得到一个元组,其中tuple.first拥有一个键,而tuple.second拥有一个最大值。

当然,要使inject部分更具可读性,值得提取一个代表您预期结果和行为的数据类。您可以在其中实现类似compareAndReplace的函数,以在发现另一个最大值时定义特定行为。像这样:

import groovy.transform.Immutable

@Immutable
class Result {
    static Result EMPTY = new Result(null, null)

    String key
    Integer value

    Result compareAndReplace(Map.Entry<String, Integer> entry, String key = '+1than1') {
        entry.value >= value ?
                new Result(entry.value == value ? key : entry.key, entry.value) :
                this
    }
}

def map2 = ['a': 3, 'b': 4, 'c': 4]

Result result = map2.inject(Result.EMPTY) { Result result, entry -> result.compareAndReplace(entry) }

assert result.key == '+1than1'
assert result.value == 4

答案 1 :(得分:2)

您可以单线执行此操作,但仍然有点晦涩:

println map2.groupBy{ it.value }.max{ it.key }.value.with{ it.size() > 1 ? ['+than1': it.values()[0]] : it }

我可能至少会提取出最后一个闭包:

def mergedKey = { it.size() > 1 ? ['+than1': it.values()[0]] : it }
def newMap2 = map2.groupBy{ it.value }.max{ it.key }.value.with(mergedKey)
assert newMap2 == ['+than1': 4]