scorciatoia per creare una mappa da un elenco in groovy?
-
09-06-2019 - |
Domanda
Vorrei una mano per questo:
Map rowToMap(row) {
def rowMap = [:];
row.columns.each{ rowMap[it.name] = it.val }
return rowMap;
}
visto come sono le cose di GDK, mi aspetterei di essere in grado di fare qualcosa del tipo:
Map rowToMap(row) {
row.columns.collectMap{ [it.name,it.val] }
}
ma non ho visto nulla nei documenti...mi sto perdendo qualcosa?o sono semplicemente troppo pigro?
Soluzione
Recentemente mi sono imbattuto nella necessità di fare esattamente questo:convertire un elenco in una mappa.Questa domanda è stata pubblicata prima che uscisse la versione 1.7.9 di Groovy, quindi il metodo collectEntries
non esisteva ancora.Funziona esattamente come collectMap
metodo quello è stato proposto:
Map rowToMap(row) {
row.columns.collectEntries{[it.name, it.val]}
}
Se per qualche motivo sei bloccato con una versione precedente di Groovy, il file inject
può essere utilizzato anche il metodo (come proposto Qui).Questa è una versione leggermente modificata che accetta solo un'espressione all'interno della chiusura (solo per salvare il carattere!):
Map rowToMap(row) {
row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}
IL +
è anche possibile utilizzare l'operatore al posto dell' <<
.
Altri suggerimenti
Controlla "iniettare".I veri esperti di programmazione funzionale lo chiamano "fold".
columns.inject([:]) { memo, entry ->
memo[entry.name] = entry.val
return memo
}
E, già che ci sei, probabilmente vorrai definire i metodi come Categorie invece che direttamente sulla metaClasse.In questo modo, puoi definirlo una volta per tutte le raccolte:
class PropertyMapCategory {
static Map mapProperty(Collection c, String keyParam, String valParam) {
return c.inject([:]) { memo, entry ->
memo[entry[keyParam]] = entry[valParam]
return memo
}
}
}
Utilizzo di esempio:
use(PropertyMapCategory) {
println columns.mapProperty('name', 'val')
}
Era la raggruppa per metodo non disponibile quando è stata posta questa domanda?
Inoltre, se utilizzi le raccolte Google (http://code.google.com/p/google-collections/), puoi fare qualcosa del genere:
map = Maps.uniqueIndex(list, Functions.identity());
OK...Ci ho giocato un po' di più e penso che sia un metodo piuttosto interessante...
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 Map o Collection ha 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 lo inserisco semplicemente in una classe che viene chiamata all'avvio della mia app e questo metodo è disponibile in tutto il mio codice.
MODIFICARE:
per aggiungere il metodo a tutti gli array...
Object[].metaClass.collectMap = collectMap
Se ciò di cui hai bisogno è una semplice coppia chiave-valore, allora il metodo collectEntries
dovrebbe bastare.Per esempio
def names = ['Foo', 'Bar']
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar]
Ma se vuoi una struttura simile a una Multimap, in cui ci sono più valori per chiave, allora dovresti usare il metodo groupBy
metodo
def names = ['Foo', 'Bar', 'Fooey']
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]
non riesco a trovare nulla di integrato...ma usando 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 aggiunge il metodo CollectMap a tutti gli ArrayList...Non sono sicuro del motivo per cui l'aggiunta all'elenco o alla raccolta non ha funzionato.Immagino che sia per un'altra domanda...ma ora posso farlo...
assert ["foo":"oof", "42":"24", "bar":"rab"] ==
["foo", "42", "bar"].collectMap { return [it, it.reverse()] }
dall'elenco alla mappa calcolata con una chiusura...esattamente quello che stavo cercando.
Modificare:il motivo per cui non ho potuto aggiungere il metodo alle interfacce List e Collection era perché non l'ho fatto:
List.metaClass.enableGlobally()
dopo la chiamata al metodo, puoi aggiungere metodi alle interfacce.che in questo caso significa che il mio metodo CollectMap funzionerà su intervalli come questo:
(0..2).collectMap{[it, it*2]}
che restituisce la mappa:[0:0, 1:2, 2:4]
Che ne dici di qualcosa del genere?
// 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']