java - collection - groovy map collect
acceso directo para crear un mapa de una lista en groovy? (8)
Me gustaría algún tipo de mano para esto:
Map rowToMap(row) {
def rowMap = [:];
row.columns.each{ rowMap[it.name] = it.val }
return rowMap;
}
Dada la forma en que son las cosas de GDK, espero poder hacer algo como:
Map rowToMap(row) {
row.columns.collectMap{ [it.name,it.val] }
}
pero no he visto nada en los documentos ... ¿me estoy perdiendo algo? o soy demasiado perezoso?
¿Qué tal algo así?
// 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'']
Además, si usa colecciones de google ( http://code.google.com/p/google-collections/ ), puede hacer algo como esto:
map = Maps.uniqueIndex(list, Functions.identity());
Echa un vistazo a "inyectar". La programación funcional real lo llama "fold".
columns.inject([:]) { memo, entry ->
memo[entry.name] = entry.val
return memo
}
Y, mientras lo hace, probablemente desee definir métodos como Categorías en lugar de hacerlo a la derecha en metaClass. De esta forma, puedes definirlo una vez para todas las colecciones:
class PropertyMapCategory {
static Map mapProperty(Collection c, String keyParam, String valParam) {
return c.inject([:]) { memo, entry ->
memo[entry[keyParam]] = entry[valParam]
return memo
}
}
}
Ejemplo de uso:
use(PropertyMapCategory) {
println columns.mapProperty(''name'', ''val'')
}
No puedo encontrar nada integrado ... pero usando ExpandoMetaClass puedo hacer esto:
ArrayList.metaClass.collectMap = {Closure callback->
def map = [:]
delegate.each {
def r = callback.call(it)
map[r[0]] = r[1]
}
return map
}
esto agrega el método collectMap a ArrayLists ... No estoy seguro de por qué agregarlo a List o Collection no funcionó ... Supongo que es por otra cuestión ... pero ahora puedo hacer esto ...
assert ["foo":"oof", "42":"24", "bar":"rab"] ==
["foo", "42", "bar"].collectMap { return [it, it.reverse()] }
de la lista al mapa calculado con un cierre ... exactamente lo que estaba buscando.
Editar: la razón por la que no pude agregar el método a las interfaces Lista y Colección fue porque no hice esto:
List.metaClass.enableGlobally()
después de esa llamada al método, puede agregar métodos a las interfaces ... lo que en este caso significa que mi método collectMap funcionará en rangos como este:
(0..2).collectMap{[it, it*2]}
que produce el mapa: [0: 0, 1: 2, 2: 4]
Recientemente me di cuenta de la necesidad de hacer exactamente eso: convertir una lista en un mapa. Esta pregunta se publicó antes de que Groovy versión 1.7.9 apareciera, por lo que el método collectEntries
aún no existía. Funciona exactamente como el método collectMap
que se propuso :
Map rowToMap(row) {
row.columns.collectEntries{[it.name, it.val]}
}
Si por alguna razón estás atrapado en una versión anterior de Groovy, el método de inject
también se puede usar (como se propone here ). Esta es una versión ligeramente modificada que toma solo una expresión dentro del cierre (¡solo para ahorrar caracteres!):
Map rowToMap(row) {
row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}
El operador +
también se puede usar en lugar del <<
.
Si lo que necesita es un par clave-valor simple, entonces el método collectEntries
debería ser suficiente. Por ejemplo
def names = [''Foo'', ''Bar'']
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar]
Pero si quieres una estructura similar a Multimap, en la que hay varios valores por clave, entonces querrás usar el método groupBy
def names = [''Foo'', ''Bar'', ''Fooey'']
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]
vale ... He jugado con esto un poco más y creo que este es un método bastante genial ...
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
ahora cualquier subclase de Map o Collection tiene este método ...
aquí lo uso para invertir la clave / valor en un mapa
[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3]
y aquí lo uso para crear un mapa de una lista
[1,2].collectMap{[it,it]} == [1:1, 2:2]
ahora solo coloco esto en una clase que se llama cuando mi aplicación se inicia y este método está disponible en todo mi código.
EDITAR:
para agregar el método a todas las matrices ...
Object[].metaClass.collectMap = collectMap