Groovy:将字符串转换为Multimap

时间:2015-10-26 11:10:58

标签: collections groovy metaclass multimap

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

def recalculate
  from, to = params[:from], params[:to]
  if from.empty? || to.empty?
    redirect_to tarriff_path(@tarriff), alert: "Can't be empty"
  elsif ???
    redirect_to tarriff_path(@tarriff), alert: "Should be date"
  else
    ...
    redirect_to tarriff_path(@tarriff), notice: "Was recalculated"
  end
end

我的最终目标是将此String拆分为Multimap,如下所示:

text = "foo/bar;baz/qux"

我还为LinkedHashMap的metaClass添加了Multimap-support:

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

String需要以分号分割,然后再以forforlash分割。目前我在嵌套的for循环中填充我的Multimap,但显然有一种Groovier方法。因此,我想知道我的选择是什么?

我正在思考以下几点:

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

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

我接受它,我不会认为它非常聪明但我觉得它更容易阅读:

def myMap = [:]

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

如果您使用的是Groovy 2.4.0或更高版本,则可以使用已添加到withIndex()的{​​{1}}方法:

java.lang.Iterable