atajo para crear un mapa de una lista en groovy?


93

Me gustaría un poco de sorthand para esto:

Map rowToMap(row) { 
    def rowMap = [:]; 
    row.columns.each{ rowMap[it.name] = it.val } 
    return rowMap; 
} 

dada la forma en que el material es GDK, yo esperaría que ser capaz de hacer algo como:

Map rowToMap(row) { 
    row.columns.collectMap{ [it.name,it.val] } 
} 

pero no tengo visto algo en los documentos ... ¿Me estoy perdiendo algo? o soy demasiado perezoso?

+2

comentario de Amir es ahora la respuesta correcta: http://stackoverflow.com/a/4484958/27561 24 may. 152015-05-24 18:01:16

101

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 igual que el método collectMapthat was proposed:

Map rowToMap(row) { 
    row.columns.collectEntries{[it.name, it.val]} 
} 

Si por alguna razón le pegan con una versión más antigua maravillosa, el método inject también se puede utilizar (como se propone here). Esta es una versión ligeramente modificada que tiene sólo una expresión en el interior del cierre (por el simple hecho de ahorro personaje!):

Map rowToMap(row) { 
    row.columns.inject([:]) {map, col -> map << [(col.name): col.val]} 
} 

El operador + también se puede utilizar en lugar de la <<.


1

no puedo encontrar nada construido en ... pero utilizando el 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 se suma el método Recolector a todos ArrayLists ... No estoy seguro de por qué agregarlo a la lista o colección no funcionó .. supongo que es para otra pregunta ... pero ahora puedo hacer esto ...

assert ["foo":"oof", "42":"24", "bar":"rab"] == 
      ["foo", "42", "bar"].collectMap { return [it, it.reverse()] } 

de la Lista de Mapa calculada con un cierre ... exactamente lo que estaba buscando.

edición: la razón por la que no podía añadir el método de la Lista de las interfaces y la colección fue porque yo no hice esto:

List.metaClass.enableGlobally() 

después de esa llamada método, se puede añadir métodos a los que las interfaces .. en este caso significa mi método Recolector trabajará en rangos de la siguiente manera:

(0..2).collectMap{[it, it*2]} 

que produce el mapa: [0: 0, 1: 2, 2: 4]


5

Además, si usted es el uso google collections (http://code.google.com/p/google-collections/), se puede hacer algo como esto:

map = Maps.uniqueIndex(list, Functions.identity()); 

5

bien ... He jugado con esto un poco más y creo que este es un método muy bien ...

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 Mapa o Colección tener este método ...

aquí lo uso para revertir la clave/valor en un mapa

[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3] 

y aquí lo utilizan para crear un mapa de una lista

[1,2].collectMap{[it,it]} == [1:1, 2:2] 

ahora sólo pop esto en una clase que es llamada como mi aplicación está empezando y este método está disponible a través de mi código.

EDIT:

para añadir el método a todas las matrices ...

Object[].metaClass.collectMap = collectMap 

0

¿Qué pasa algo como esto?

// 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'] 
  0

Eso es básicamente lo mismo que en mi pregunta ... Acabo de tener el mapa [it.k] = it.v en lugar de .putAt Estaba buscando por un trazador de líneas. 13 oct. 082008-10-13 01:43:39


28

Echa un vistazo a "inject". La programación funcional real lo llama "fold".

columns.inject([:]) { memo, entry -> 
    memo[entry.name] = entry.val 
    return memo 
} 

Y, mientras estás en ello, es probable que desee definir métodos como categorías en lugar de la derecha en la metaclase. De esta manera, se puede definir de una vez por todas 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') 
} 
  0

Supongo que se llama inject en Groovy, ya que podría haberse inspirado en inject: en: en Smalltalk: | lista suma | list: = OrderedCollection new add: 1; agregar: 2; agregar: 3; tú mismo. sum: = list inject: 0 en: [: a: b | a + b]. Transcripción cr; espectáculo: suma "prints 6" 02 ene. 132013-01-02 18:41:31


13

¿El método groupBy no está disponible cuando se hizo esta pregunta?

  0

Parece que no, es desde 1.8.1, 2011. La pregunta fue hecha en 2008. Pero en cualquier caso, groupBy es ahora el camino a seguir. 01 abr. 142014-04-01 21:48:03


3

Si lo que necesita es un par de 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 un Multimapa, en el que hay múltiples valores por clave, entonces usted querrá usar el método groupBy

def names = ['Foo', 'Bar', 'Fooey'] 
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]