116

我想要一些排序方法:

Map rowToMap(row) {
    def rowMap = [:];
    row.columns.each{ rowMap[it.name] = it.val }
    return rowMap;
}

鉴于 GDK 的方式,我希望能够执行以下操作:

Map rowToMap(row) {
    row.columns.collectMap{ [it.name,it.val] }
}

但我没有在文档中看到任何内容......我错过了什么吗?还是我太懒了?

4

8 回答 8

133

我最近遇到了这样做的需要:将列表转换为地图。这个问题是在Groovy 1.7.9 版本出来之前发的,所以collectEntries还没有这个方法。它的工作原理与提出collectMap的方法完全相同:

Map rowToMap(row) {
    row.columns.collectEntries{[it.name, it.val]}
}

如果由于某种原因您卡在较旧的 Groovy 版本中,inject也可以使用该方法(如此处建议)。这是一个稍作修改的版本,它只在闭包内使用一个表达式(只是为了节省字符!):

Map rowToMap(row) {
    row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}

+也可以使用运算符代替<<.

于 2011-04-13T06:47:13.700 回答
30

查看“注入”。真正的函数式编程专家称之为“折叠”。

columns.inject([:]) { memo, entry ->
    memo[entry.name] = entry.val
    return memo
}

而且,当您使用它时,您可能希望将方法定义为类别,而不是直接在元类上。这样,您可以为所有集合定义一次:

class PropertyMapCategory {
    static Map mapProperty(Collection c, String keyParam, String valParam) {
        return c.inject([:]) { memo, entry ->
            memo[entry[keyParam]] = entry[valParam]
            return memo
        }
    }
}

示例用法:

use(PropertyMapCategory) {
    println columns.mapProperty('name', 'val')
}
于 2008-10-13T18:48:46.723 回答
13

问这个问题时 groupBy方法不可用吗?

于 2010-12-19T20:34:54.860 回答
8

如果您需要的是一个简单的键值对,那么该方法collectEntries就足够了。例如

def names = ['Foo', 'Bar']
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar]

但是如果你想要一个类似于 Multimap 的结构,其中每个键有多个值,那么你会想要使用该groupBy方法

def names = ['Foo', 'Bar', 'Fooey']
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]
于 2017-03-09T08:06:40.983 回答
6

此外,如果您使用 google 集合 ( http://code.google.com/p/google-collections/ ),您可以执行以下操作:

  map = Maps.uniqueIndex(list, Functions.identity());
于 2008-08-20T22:32:57.030 回答
5

好的...我已经玩过这个了,我认为这是一个很酷的方法...

def collectMap = {Closure callback->
    def map = [:]
    delegate.each {
        def r = callback.call(it)
        map[r[0]] = r[1]
    }
    return map
}
ExpandoMetaClass.enableGlobally()
Collection.metaClass.collectMap = collectMap
Map.metaClass.collectMap = collectMap

现在 Map 或 Collection 的任何子类都有这个方法......

在这里,我用它来反转 Map 中的键/值

[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3]

在这里我用它从列表中创建地图

[1,2].collectMap{[it,it]} == [1:1, 2:2]

现在我只是将它弹出到一个在我的应用程序启动时被调用的类中,并且该方法在我的代码中可用。

编辑:

将该方法添加到所有数组...

Object[].metaClass.collectMap = collectMap
于 2008-08-20T23:28:29.767 回答
1

我找不到任何内置的东西......但使用 ExpandoMetaClass 我可以做到这一点:

ArrayList.metaClass.collectMap = {Closure callback->
    def map = [:]
    delegate.each {
        def r = callback.call(it)
        map[r[0]] = r[1]
    }
    return map
}

这会将 collectMap 方法添加到所有 ArrayLists...我不确定为什么将它添加到 List 或 Collection 不起作用..我想这是另一个问题...但现在我可以这样做...

assert ["foo":"oof", "42":"24", "bar":"rab"] ==
            ["foo", "42", "bar"].collectMap { return [it, it.reverse()] }

从 List 到带有一个闭包的计算 Map ......正是我想要的。

编辑:我无法将方法添加到接口 List 和 Collection 的原因是因为我没有这样做:

List.metaClass.enableGlobally()

在该方法调用之后,您可以将方法添加到接口。在这种情况下,这意味着我的 collectMap 方法将适用于这样的范围:

(0..2).collectMap{[it, it*2]}

生成地图: [0:0, 1:2, 2:4]

于 2008-08-20T22:25:12.197 回答
0

这样的事情呢?

// setup
class Pair { 
    String k; 
    String v; 
    public Pair(def k, def v) { this.k = k ; this.v = v; }
}
def list = [ new Pair('a', 'b'), new Pair('c', 'd') ]

// the idea
def map = [:]
list.each{ it -> map.putAt(it.k, it.v) }

// verify
println map['c']
于 2008-09-29T17:41:19.937 回答