skrót do tworzenia mapy z listy w groovy?


93

Chciałbym trochę sorthand dla tego:

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

biorąc pod uwagę sposób, w jaki rzeczy GDK to, czego można oczekiwać, aby być w stanie zrobić coś takiego:

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

ale nie mam widziałeś coś w dokumentach ... czy czegoś brakuje? czy jestem po prostu zbyt leniwy?

+2

komentarz Amir jest teraz poprawna odpowiedź: http://stackoverflow.com/a/4484958/27561 24 maj. 152015-05-24 18:01:16

101

Niedawno natknąłem się na potrzebę zrobienia dokładnie tego: przekształcenie listy w mapę. To pytanie zostało opublikowane przed wydaniem wersji 1.7.9 Groovy, więc metoda collectEntries jeszcze nie istniała. To działa dokładnie tak, jak w metodzie collectMapthat was proposed:

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

Jeśli z jakiegoś powodu utkniesz ze starszą wersją Groovy, metoda inject może być również używany (jak zaproponowano here). Jest to nieco zmodyfikowana wersja, która trwa tylko jeden wyraz wewnątrz zamknięcia (tylko przez wzgląd na oszczędności znaków!):

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

Operator + może być również stosowany zamiast <<.


1

nie mogę znaleźć nic zbudowany w ... lecz stosując ExpandoMetaClass mogę to zrobić:

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

to dodaje metodę collectMap wszystkim ArrayLists ... Nie jestem pewien, dlaczego dodanie go do listy lub Collection nie działa .. Myślę, że to dla innego pytanie ... ale teraz mogę to zrobić ...

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

z listy do obliczonej mapa z jednym zamknięciem ... dokładnie to, co ja szukam.

Edit: powód nie mogę dodać metodę do listy interfejsów i Collection było, bo nie to zrobić:

List.metaClass.enableGlobally() 

po tym wywołaniu metody, można dodać metody do interfejsów, które .. oznacza w tym przypadku moja metoda collectMap będzie działać na zakresach tak:

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

który daje mapę: [0: 0, 1: 2, 2: 4]


5

Ponadto, jeśli jesteś wykorzystanie kolekcje google (http://code.google.com/p/google-collections/), można zrobić coś takiego:

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

5

ok ... Grałem z tym trochę więcej i myślę, że to całkiem fajny sposób ...

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 

teraz każdy podklasa mapą lub Kolekcja mieć tej metody ...

tutaj używam go do odwrócenia klucz/wartość w Mapie

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

i tutaj używam go do utworzenia mapy z listy

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

teraz po prostu pop to w klasie, która jest wywoływana jako moja aplikacja jest uruchamianie i ta metoda jest dostępna w całym kodzie.

EDIT:

dodać metodę do wszystkich tablic ...

Object[].metaClass.collectMap = collectMap 

0

Co o czymś takim?

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

To w zasadzie to samo, co w moim pytaniu ... Po prostu miałem mapę [it.k] = it.v zamiast .putAt Szukałem dla jednego liniowca. 13 paź. 082008-10-13 01:43:39


28

Sprawdź "wstrzyknąć". Prawdziwi funkcjonalni programistom nazywają to "fold".

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

A gdy jesteś na to, prawdopodobnie chcesz zdefiniować jako kategorie metod zamiast prawej stronie metaklasą. W ten sposób, można go zdefiniować raz dla wszystkich kolekcji:

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

Przykład użycia:

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

Przypuszczam, że jest to nazwa wstrzyknięcia w Groovy, ponieważ mogło być zainspirowane przez wstrzyknięcie: do: w Smalltalk: | lista sum | lista: = Sortowane nowe nowe dodane: 1; dodaj: 2; dodać: 3; siebie. suma: = wstrzyknij listę: 0 do: [: a: b | a + b]. Transkrypcja cr; pokaż: suma. "drukuje 6" 02 sty. 132013-01-02 18:41:31


13

była metoda groupBy niedostępna gdy pytano?

  0

Wygląda jak nie - to od 1.8.1, 2011. Pytanie zadawano w 2008 roku. Ale w każdym przypadku groupBy jest teraz drogą, którą należy przejść. 01 kwi. 142014-04-01 21:48:03


3

Jeśli potrzebna jest prosta para klucz-wartość, powinna wystarczyć metoda collectEntries. Na przykład

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

Ale jeśli chcesz strukturę podobną do Multimap, w którym znajdują się liczne wartości na klucz, to chciałby użyć metody groupBy

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