Verknüpfung zum Erstellen einer Karte aus einer Liste in groovy?


106

Ich hätte gerne eine Abkürzung dafür:

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

Angesichts der Art und Weise, wie das GDK-Zeug ist, würde ich erwarten, in der Lage zu sein, etwas zu tun wie:

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

aber ich habe nichts in den Dokumenten gesehen ... vermisse ich etwas? oder bin ich einfach viel zu faul


2
Amirs Kommentar ist jetzt die richtige Antwort: stackoverflow.com/a/4484958/27561
Robert Fischer

Antworten:


119

Ich bin kürzlich auf die Notwendigkeit gestoßen, genau das zu tun: eine Liste in eine Karte umzuwandeln. Diese Frage wurde gestellt, bevor Groovy Version 1.7.9 herauskam, daher existierte die Methode collectEntriesnoch nicht. Es funktioniert genau wie die vorgeschlagenecollectMap Methode :

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

Wenn Sie aus irgendeinem Grund mit einer älteren Groovy-Version nicht weiterkommen, kann die injectMethode auch verwendet werden (wie hier vorgeschlagen ). Dies ist eine leicht modifizierte Version, die nur einen Ausdruck innerhalb des Verschlusses enthält (nur zum Speichern von Zeichen!):

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

Der +Operator kann auch anstelle von verwendet werden <<.


28

Überprüfen Sie "injizieren". Echte funktionale Programmier-Wonks nennen es "Fold".

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

Und wenn Sie schon dabei sind, möchten Sie Methoden wahrscheinlich als Kategorien definieren, anstatt direkt in der MetaClass. Auf diese Weise können Sie es einmal für alle Sammlungen definieren:

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

Anwendungsbeispiel:

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

Ich denke, es heißt in Groovy injizieren, da es möglicherweise von injizieren: in: in Smalltalk: | inspiriert wurde Listensumme | list: = OrderedCollection new add: 1; hinzufügen: 2; hinzufügen: 3; du selber. sum: = list injizieren: 0 in: [: a: b | a + b]. Transkript cr; zeigen: Summe. "druckt 6"
OlliP

13

War die groupBy- Methode nicht verfügbar, als diese Frage gestellt wurde?


Scheint nein zu sein - es ist seit dem 1.8.1, 2011. Die Frage wurde 2008 gestellt. Aber auf jeden Fall ist groupBy jetzt der richtige Weg.
mvmn

Wie Sie in der Dokumentation zu groupBy sehen können, werden Elemente grundsätzlich in Gruppen gruppiert, wobei jede Gruppe Elemente enthält, die einem bestimmten Schlüssel entsprechen. Daher lautet der Rückgabetyp Map<K, List<V>>Es scheint, dass das OP nach einer Methode mit dem Rückgabetyp sucht Map<K, V>, sodass groupBy in diesem Fall nicht funktioniert.
Krzysiek Przygudzki

6

Wenn Sie ein einfaches Schlüssel-Wert-Paar benötigen, sollte die Methode collectEntriesausreichen. Beispielsweise

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

Wenn Sie jedoch eine Struktur ähnlich einer Multimap wünschen, in der es mehrere Werte pro Schlüssel gibt, möchten Sie die groupByMethode verwenden

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


5

ok ... ich habe ein bisschen mehr damit gespielt und ich denke das ist eine ziemlich coole Methode ...

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

Jetzt hat jede Unterklasse von Map oder Collection diese Methode ...

hier verwende ich es, um den Schlüssel / Wert in einer Karte umzukehren

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

und hier benutze ich es, um eine Karte aus einer Liste zu erstellen

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

Jetzt füge ich dies einfach in eine Klasse ein, die aufgerufen wird, während meine App startet, und diese Methode ist in meinem gesamten Code verfügbar.

BEARBEITEN:

um die Methode allen Arrays hinzuzufügen ...

Object[].metaClass.collectMap = collectMap

1

Ich kann nichts Eingebautes finden ... aber mit der ExpandoMetaClass kann ich Folgendes tun:

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

Dadurch wird die Methode collectMap zu allen ArrayLists hinzugefügt. Ich bin mir nicht sicher, warum das Hinzufügen zu List oder Collection nicht funktioniert hat. Ich denke, das ist eine andere Frage. Aber jetzt kann ich das tun.

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

Von der Liste zur berechneten Karte mit einem Abschluss ... genau das, wonach ich gesucht habe.

Bearbeiten: Der Grund, warum ich die Methode nicht zu den Schnittstellen Liste und Sammlung hinzufügen konnte, war, dass ich dies nicht getan habe:

List.metaClass.enableGlobally()

Nach diesem Methodenaufruf können Sie Schnittstellen Methoden hinzufügen. In diesem Fall bedeutet dies, dass meine collectMap-Methode für Bereiche wie diesen funktioniert:

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

was die Karte ergibt: [0: 0, 1: 2, 2: 4]


0

Was ist mit so etwas?

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

Das ist im Grunde das gleiche wie in meiner Frage ... Ich hatte nur map [it.k] = it.v anstelle von .putAt ich suchte nach einem Einzeiler.
Danb
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.