Best way to code this, string to map conversion in Groovy
I have a string like
def data = "session=234567893egshdjchasd&userId=12345673456&timeOut=1800000"
I want to convert it to a map
["session", 234567893egshdjchasd]
["userId", 12345673456]
["timeout", 1800000]
This 开发者_如何学Gois the current way I am doing it,
def map = [:]
data.splitEachLine("&"){
it.each{ x ->
def object = x.split("=")
map.put(object[0], object[1])
}
}
It works, but is there a more efficient way?
I don't know think this is would run any faster, but it does suggest itself in terms of syntactic parsimony:
def data = 'session=234567893egshdjchasd&userId=12345673456&timeOut=1800000'
def result = data.split('&').inject([:]) { map, token ->
//Split at "=" and return map with trimmed values
token.split('=').with {
map[it[0].trim()] = it[1].trim()
}
map
}
Personally, I like Don's answer for readability and maintainability, but depending on context, this may be appropriate.
Edit: This is actually a reformatted one-liner.
I don't know if this is more efficient, but to my eyes, it's a bit simpler (YMMV)
def data = "session=234567893egshdjchasd&userId=12345673456&timeOut=1800000"
def map = [:]
data.split("&").each {param ->
def nameAndValue = param.split("=")
map[nameAndValue[0]] = nameAndValue[1]
}
If you're looking for efficient, regular expressions are where it's at:
def data = "session=234567893egshdjchasd&userId=12345673456&timeOut=1800000"
def map = [:]
data.findAll(/([^&=]+)=([^&]+)/) { full, name, value -> map[name] = value }
println map
prints:
[session:234567893egshdjchasd, userId:12345673456, timeOut:1800000]
If you're not familiar with regular expressions, it might look a little foreign, but it's really not that complicate. It just has two (groups), the first group is any character but a "&" or a "=". The second group is any character besides a "=". The capture groups are on either side of a "=".
After some searching, "collectEntries()" is the magic thing to use, it creates a map element. Work just like "collect()" that creates a list. So given
def params = "a1=b1&a2=b2&a3&a4=&a5=x=y"
the one-liner is
map = params.tokenize("&").collectEntries{
it.split("=",2).with{
[ (it[0]): (it.size()<2) ? null : it[1] ?: null ]
}
}
which creates
map = [a1:b1, a2:b2, a3:null, a4:null, a5:x=y]
Depending how you want to handle the cases "a3" and "a4=" you can also use a slightly shorter version
...
[ (it[0]): (it.size()<2) ? null : it[1] ]
...
and then you get this:
map = [a1:b1, a2:b2, a3:null, a4:, a5:x=y]
If you are in a grails controller, then this is nice and simple:
GrailsParameterMap map = new GrailsParameterMap(request)
http://grails.org/doc/latest/api/org/codehaus/groovy/grails/web/servlet/mvc/GrailsParameterMap.html
If you use Grails, the best way I find is WebUtils the function fromQueryString.
https://grails.github.io/grails-doc/2.0.x/api/org/codehaus/groovy/grails/web/util/WebUtils.html
Here's my effort, which initializes and fills the map in one go, and avoids the inject method which I personally find hard to follow:-
def map = data.split('&').collectEntries {
def kvp = it.split('=').collect { string ->
string = string.trim()
return string
}
[(kvp[0]): kvp.size() > 1 ? kvp[1] ?: '' : '']
// the null check is almost certainly overkill so you could use :-
// [(kvp[0]): kvp.size() > 1 ? kvp[1] : '']
// this just checks that a value was found and inserts an empty string instead of null
}
I wouldn't suggest using split at all.
Split creates a new string, whereas when creating a collection of environment variables, you would probably want a list of maps.
Tokenize both on the initial break (&) and on the nested break (=). While most interpreters will still work, some may run the split literally, and you'll end up with a list of strings, rather than a list of maps.
def data= "test1=val1&test2=val2"
def map = [:]
map = data.tokenize("&").collectEntries {
it.tokenize("=").with {
[(it[0]):it[1]]
}
}
精彩评论