在 groovy 中从 List 创建 Map 的快捷方式?

我想听听你们怎么说:

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

given the way the GDK stuff is, I'd expect to be able to do something like:

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

but I haven't seen anything in the docs... am I missing something? or am I just way too lazy?

76540 次浏览

I can't find anything built in... but using the ExpandoMetaClass I can do this:

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

这将 CollectMap 方法添加到所有的 ArrayList 中... ... 我不知道为什么将它添加到 List 或 Collection 不起作用。.我想这是另一个问题... 但现在我可以这样做..。

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

从名单到计算地图与一个闭包... 正是我所寻找的。

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

List.metaClass.enableGlobally()

在方法调用之后,可以向接口添加方法。.在这种情况下,这意味着我的 CollectMap 方法可以在这样的范围内工作:

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

得到地图: [0:0,1:2,2:4]

此外,如果你使用谷歌收藏(http://code.google.com/p/google-collections/) ,你可以这样做:

  map = Maps.uniqueIndex(list, Functions.identity());

好吧... 我已经玩了一点点,我认为这是一个非常酷的方法..。

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]

现在我只需要把它放到一个类中,这个类在我的应用程序启动时被调用,这个方法在我的整个代码中都是可用的。

EDIT:

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

Object[].metaClass.collectMap = collectMap

What about something like this?

// 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']

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

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

同时,您可能希望将方法定义为类别,而不是在 metClass 上定义。这样,您可以为所有集合定义一次:

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

Example usage:

use(PropertyMapCategory) {
println columns.mapProperty('name', 'val')
}

当问到这个问题时,GroupBy方法是否不可用?

我最近碰到了这样的需要: 将一个列表转换成一个地图。这个问题是在 Groovy 版本1.7.9发布之前发布的,所以方法 collectEntries还不存在。它的工作原理与 collectMap方法 有人提议完全一样:

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

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

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

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

如果您需要的是一个简单的键-值对,那么方法 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]]