per creare una mappa da una lista in groovy?


93

Vorrei un po sorthand per questo:

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

dato il modo in cui il materiale è GDK, mi aspetto di essere in grado di fare qualcosa di simile:

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

ma non ho visto qualcosa nei documenti ... mi manca qualcosa? o sono semplicemente troppo pigro?

+2

commento di Amir è ora il risposta corretta: http://stackoverflow.com/a/4484958/27561 24 mag. 152015-05-24 18:01:16

101

Ho recentemente sono imbattuto la necessità di fare esattamente questo: la conversione di un elenco in una mappa. Questa domanda è stata pubblicata prima della versione 1.7.9 di Groovy, quindi il metodo collectEntries non esisteva ancora. Funziona esattamente come il metodo di collectMapthat was proposed:

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

Se per qualche motivo si è bloccato con una versione precedente Groovy, il metodo inject può anche essere usato (come proposto here). Questa è una versione leggermente modificata che richiede un solo espressione all'interno della chiusura (solo per il gusto di risparmio carattere!):

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

L'operatore + può anche essere usato al posto del <<.


1

Non riesco a trovare niente di costruito nel ... ma usando l'ExpandoMetaClass posso fare questo:

ArrayList.metaClass.collectMap = {Closure callback-> 
    def map = [:] 
    delegate.each { 
     def r = callback.call(it) 
     map[r[0]] = r[1] 
    } 
    return map 
} 

questo si aggiunge il metodo CollectMap a tutti ArrayLists ... Io non sono sicuro perché aggiungerla to List o Collection non ha funzionato .. Direi che è per un'altra domanda ... ma ora posso fare questo ...

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

dall'elenco a calcolata Mappa con una chiusura ... esattamente quello che ero cercando.

Edit: il motivo per cui non ho potuto aggiungere il metodo a elenco interfacce e Collezione era perché non ho fatto questo:

List.metaClass.enableGlobally() 

dopo quella chiamata di metodo, è possibile aggiungere metodi alle interfacce .. che in questo caso significa il mio metodo CollectMap funziona su gamme in questo modo:

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

da cui si ricava la mappa: [0: 0, 1: 2, 2: 4]


5

Inoltre, se siete uso collezioni google (http://code.google.com/p/google-collections/), si può fare qualcosa di simile:

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

5

ok ... Ho giocato con questo un po 'di più e credo che questo è un metodo piuttosto fresco ...

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 

ora qualsiasi sottoclasse di mappa o di Raccolta avere questo metodo ...

qui lo uso per invertire la chiave/valore in una mappa

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

e qui lo uso per creare una mappa da un elenco

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

ora mi basta inserire questo in una classe che viene chiamato come la mia app sta iniziando e questo metodo è disponibile in tutto il mio codice.

EDIT:

per aggiungere il metodo a tutte le matrici ...

Object[].metaClass.collectMap = collectMap 

0

Che dire qualcosa di simile?

// 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

Questo è praticamente lo stesso della mia domanda ... Ho appena avuto la mappa [it.k] = it.v invece di .putAt Stavo cercando per un solo rivestimento. 13 ott. 082008-10-13 01:43:39


28

Controllare "iniettare". I veri esperti di programmazione funzionale chiamano "fold".

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

E, già che ci siete, probabilmente desidera definire metodi come categorie invece che proprio sulla metaclasse. In questo modo, è possibile definire una volta per tutte le collezioni:

class PropertyMapCategory { 
    static Map mapProperty(Collection c, String keyParam, String valParam) { 
     return c.inject([:]) { memo, entry -> 
      memo[entry[keyParam]] = entry[valParam] 
      return memo 
     } 
    } 
} 

Esempio di utilizzo:

use(PropertyMapCategory) { 
    println columns.mapProperty('name', 'val') 
} 
  0

Credo che sia nominato iniettare in Groovy come potrebbe essere stata ispirata da iniettare: in: in Smalltalk: | lista somma | lista: = OrderedCollection new add: 1; aggiungere: 2; aggiungere: 3; te stesso. sum: = list inject: 0 in: [: a: b | a + b]. Trascrizione cr; mostra: somma. "Stampe 6" 02 gen. 132013-01-02 18:41:31


13

era il metodo groupBy non è disponibile quando questa domanda è stato chiesto?

  0

sembra che nessuno - è dal 1.8.1, 2011. La domanda è stata posta nel 2008. Ma in ogni caso, groupBy ora è la strada da percorrere in effetti. 01 apr. 142014-04-01 21:48:03


3

Se ciò di cui si ha bisogno è una semplice coppia chiave-valore, il metodo collectEntries dovrebbe essere sufficiente. Per esempio

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

Ma se volete una struttura simile a un Multimap, in cui ci sono più valori per tasto, allora che ci si vuole utilizzare il metodo groupBy

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