从groovy中的列表创建地图的快捷方式?


106

我想要一些排序方法:

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] }
}

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


2
阿米尔的评论现在是正确的答案:stackoverflow.com/a/4484958/27561
罗伯特·菲舍尔

Answers:


119

最近,我遇到了需要完全做到这一点的需求:将列表转换为地图。这个问题是在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]}
}

+运营商还可以用来代替<<


28

签出“注入”。真正的函数式编程语言将其称为“折叠”。

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

而且,在使用它时,您可能想将方法定义为Categories,而不是直接在metaClass上定义。这样,您可以为所有集合定义一次:

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

我猜它在Groovy中被命名为inject,因为它可能是受Smalltalk中inject:into:启发的。清单总和| list:= OrderedCollection新添加:1; 加:2; 加:3; 你自己 sum:= list注入:0到:[:a:b | a + b]。抄本cr; 显示:总和。“印刷6”
OlliP

13

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


似乎没有,这是2011年1月8日以来的情况。这个问题是在2008年提出的。但是无论如何,groupBy现在确实是可行的方式。
mvmn 2014年

如您在groupBy文档中所见,它基本上将元素分为几组,其中每个组都包含与某个键匹配的元素。因此,其返回类型为。OP Map<K, List<V>>似乎正在寻找具有返回类型的方法Map<K, V>,因此groupBy在这种情况下不起作用。
Krzysiek Przygudzki,

6

如果您需要的是简单的键/值对,则该方法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]]


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的任何子类都具有此方法...

在这里我用它来反转地图中的键/值

[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

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.metaClass.enableGlobally()

在该方法调用之后,您可以将方法添加到接口..在这种情况下,这意味着我的collectMap方法将在如下范围内工作:

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

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


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

这基本上与我的问题相同...我只是用map [it.k] = it.v而不是.putAt我正在寻找一个班轮。
丹布
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.