Groovy:将字符串转换为多图

Groovy: Transforming a String into a Multimap

所以我有一个看起来有点像这样的字符串:

text = "foo/bar;baz/qux"

我的最终目标是将这个字符串拆分成一个 Multimap,如下所示:

["level1" : ["foo", "baz"], "level2" : ["bar", "qux"]]

我还为 LinkedHashMap 的元类添加了 Multimap 支持:

LinkedHashMap.metaClass.multiPut << { key, value ->
    delegate[key] = delegate[key] ?: []; delegate[key] += value
}

字符串需要在分号处拆分,然后在正斜杠处再次拆分。目前我正在嵌套的 for 循环中填充我的 Multimap,但显然有一种 Groovier 方法可以做到这一点。因此我想知道我的选择是什么?

我的想法大致如下:

def final myMap = text.split(';')
        .collectEntries { it.split('/')
        .eachWithIndex { entry, index -> ["level${index + 1}" : entry] }}

您可以在返回的地图上使用 withDefault 来摆脱三元:

def text = "foo/bar;baz/qux;foo/bar/woo"

def result = text.split(';')*.split('/').inject([:].withDefault {[]}) { map, value ->
    value.eachWithIndex { element, idx ->
        map["level${idx+1}"] << element
    }
    map
}

assert result == [level1:['foo', 'baz', 'foo'], level2:['bar', 'qux', 'bar'], level3:['woo']]

如果您不想在结果中出现重复,那么您可以在 withDefault 中使用一个 Set(然后再转换回一个 List):

def text = "foo/bar;baz/qux;foo/bar/woo"

def result = text.split(';')*.split('/').inject([:].withDefault {[] as Set}) { map, value ->
    value.eachWithIndex { element, idx ->
        map["level${idx+1}"] << element
    }
    map
}.collectEntries { key, value -> [key, value as List] }

assert result == [level1:['foo', 'baz'], level2:['bar', 'qux'], level3:['woo']]

我的看法,我不认为它很聪明,但我发现它更容易阅读:

def myMap = [:]

text.split(';').eachWithIndex{ entry, index ->
  myMap << ["level${index + 1}": entry.split('/')]
}

如果您使用的是 Groovy 2.4.0 或更高版本,您可以使用已添加到 java.lang.Iterable:

withIndex() 方法
def myMap = text.split(';').withIndex().collect{ entry, index ->
  ["level${index + 1}": entry.split('/')]
}