【问题标题】:Groovy: Transforming a String into a MultimapGroovy:将字符串转换为 Multimap
【发布时间】:2016-01-25 11:11:27
【问题描述】:

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

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

【问题讨论】:

    标签: collections groovy metaclass multimap


    【解决方案1】:

    您可以在返回的 Map 上使用 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']]
    

    【讨论】:

    • 简直完美。它完全符合我的需要。太感谢了! :)
    【解决方案2】:

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

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

    如果您使用的是 Groovy 2.4.0 或更高版本,则可以使用已添加到 java.lang.IterablewithIndex() 方法:

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

    【讨论】:

    • 那不会提供所需的输出吧?
    • 我的意思是text.split(';').withIndex() 不起作用(因为split 返回一个数组,而不是一个列表)。无需在您的变量定义中分散final。您的第一个解决方案将为您提供[level1:['foo', 'bar'], level2:['baz', 'qux']] 的输出映射,而不是问题中要求的[level1 : ["foo", "baz"], level2 : ["bar", "qux"]]....所以在回答您的问题时,此答案中的所有代码都有问题
    • ['foo', 'bar'] != ['foo', 'baz']
    • @tim_yates 我误读了您的规格,我认为; 是拆分级别。 barbaz 的亲密关系误导了我的眼睛。
    • 哈哈,不是我的问题,我同意一些更好的占位符会让这个问题更容易回答;-)
    猜你喜欢
    • 1970-01-01
    • 2018-11-03
    • 2010-12-15
    • 2014-03-10
    • 2013-02-14
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多