可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'd like some sorthand for this:
Map rowToMap(row) {
def rowMap = [:];
row.columns.each{ rowMap[it.name] = it.val }
return rowMap;
}
given the way the GDK stuff is, I'd expect to be able to do something like:
Map rowToMap(row) {
row.columns.collectMap{ [it.name,it.val] }
}
but I haven't seen anything in the docs... am I missing something? or am I just way too lazy?
回答1:
I've recently came across the need to do exactly that: converting a list into a map. This question was posted before Groovy version 1.7.9 came out, so the method collectEntries
didn't exist yet. It works exactly as the collectMap
method that was proposed:
Map rowToMap(row) {
row.columns.collectEntries{[it.name, it.val]}
}
If for some reason you are stuck with an older Groovy version, the inject
method can also be used (as proposed here). This is a slightly modified version that takes only one expression inside the closure (just for the sake of character saving!):
Map rowToMap(row) {
row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}
The +
operator can also be used instead of the <<
.
回答2:
Check out "inject". Real functional programming wonks call it "fold".
columns.inject([:]) { memo, entry ->
memo[entry.name] = entry.val
return memo
}
And, while you're at it, you probably want to define methods as Categories instead of right on the metaClass. That way, you can define it once for all Collections:
class PropertyMapCategory {
static Map mapProperty(Collection c, String keyParam, String valParam) {
return c.inject([:]) { memo, entry ->
memo[entry[keyParam]] = entry[valParam]
return memo
}
}
}
Example usage:
use(PropertyMapCategory) {
println columns.mapProperty('name', 'val')
}
回答3:
Was the groupBy method not available when this question was asked?
回答4:
Also, if you're use google collections (http://code.google.com/p/google-collections/), you can do something like this:
map = Maps.uniqueIndex(list, Functions.identity());
回答5:
ok... I've played with this a little more and I think this is a pretty cool method...
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
now any subclass of Map or Collection have this method...
here I use it to reverse the key/value in a Map
[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3]
and here I use it to create a map from a list
[1,2].collectMap{[it,it]} == [1:1, 2:2]
now I just pop this into a class that gets called as my app is starting and this method is available throughout my code.
EDIT:
to add the method to all arrays...
Object[].metaClass.collectMap = collectMap
回答6:
If what you need is a simple key-value pair, then the method collectEntries
should suffice. For example
def names = ['Foo', 'Bar']
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar]
But if you want a structure similar to a Multimap, in which there are multiple values per key, then you'd want to use the groupBy
method
def names = ['Foo', 'Bar', 'Fooey']
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]
回答7:
I can't find anything built in... but using the ExpandoMetaClass I can do this:
ArrayList.metaClass.collectMap = {Closure callback->
def map = [:]
delegate.each {
def r = callback.call(it)
map[r[0]] = r[1]
}
return map
}
this adds the collectMap method to all ArrayLists... I'm not sure why adding it to List or Collection didn't work.. I guess that's for another question... but now I can do this...
assert ["foo":"oof", "42":"24", "bar":"rab"] ==
["foo", "42", "bar"].collectMap { return [it, it.reverse()] }
from List to calculated Map with one closure... exactly what I was looking for.
Edit: the reason I couldn't add the method to the interfaces List and Collection was because I did not do this:
List.metaClass.enableGlobally()
after that method call, you can add methods to interfaces.. which in this case means my collectMap method will work on ranges like this:
(0..2).collectMap{[it, it*2]}
which yields the map: [0:0, 1:2, 2:4]
回答8:
What about something like this?
// 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']