2008-08-20 21 views
93

Я хотел бы некоторые sorthand для этого:ярлык для создания карты из списка в groovy?

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

данный способ материал ГДК, я бы ожидать, чтобы быть в состоянии сделать что-то вроде:

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

, но у меня нет видел что-нибудь в документах ... я что-то упустил? или я просто слишком ленив?

+2

комментарий Амира теперь правильный ответ: http://stackoverflow.com/a/4484958/27561 – 2015-05-24 18:01:16

ответ

101

Я недавно столкнулся с необходимостью сделать именно это: преобразование списка в карту. Этот вопрос был опубликован до выхода Groovy версии 1.7.9, поэтому метод collectEntries еще не существует. Это работает точно так же, как collectMap метод that was proposed:

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

Если по каким-то причинам вы застряли с более старой версией Groovy, метод inject также может быть использован (как это было предложено here). Это слегка измененный вариант, который принимает только одно выражение внутри закрытия (только ради экономии символов!):

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

Оператор + также может быть использован вместо <<.

1

я ничего не могу найти встроенный ... но используя ExpandoMetaClass я могу это сделать:

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

это добавляет метод collectMap всем ArrayLists ... Я не уверен, почему добавив его в список или коллекция не работает .. Я думаю, что это еще вопрос ... но теперь я могу это сделать ...

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

из списка расчетной карты с одним закрытием ... именно то, что я находясь в поиске.

Edit: почему я не мог добавить метод в список интерфейсов и сбор, потому что я не сделал этого:

List.metaClass.enableGlobally() 

после этого вызова метода, вы можете добавить методы к интерфейсам .. который в данном случае означает, что мой метод collectMap будет работать на диапазонах, как это:

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

, который дает карту: [0: 0, 1: 2, 2: 4]

5

нормально ... Я играл с этим немного больше, и я думаю, что это довольно прохладно способ ...

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 

прямо сейчас любой подкласс карты или коллекции имеют этого метода ...

здесь я использую его, чтобы полностью изменить ключ/значение в карте

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

и здесь я использую его, чтобы создать карту из списка

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

теперь я просто поп это в классе, который вызывается, как мое приложение начинается и этот метод доступен на протяжении всего моего кода.

EDIT:

добавить метод ко всем массивам ...

Object[].metaClass.collectMap = collectMap 
0

А что-то вроде этого?

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

Это в основном то же самое, что и в моем вопросе ... Я просто имел карту [it.k] = it.v вместо .putAt Я смотрел для одного вкладыша. – danb 2008-10-13 01:43:39

28

Проверьте «впрыскивать». Реальные функциональные программисты призывают его «свернуть».

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

И, в то время как вы на него, вы, вероятно, хотите, чтобы определить методы как категории, а не права на метаклассе. Таким образом, вы можете определить его один раз для всех коллекций:

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

Пример использования:

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

Я предполагаю, что он назван инъекцией в Groovy, поскольку он, возможно, был вдохновлен инъекцией: in: in Smalltalk: | сумма списка | список: = OrderedCollection new add: 1; добавить: 2; add: 3; сам. sum: = list inject: 0 to: [: a: b | a + b]. Стенограмма cr; показать: сумма. «prints 6» – OlliP 2013-01-02 18:41:31

13

был метод groupBy не доступен, когда этот вопрос был задан?

+0

Кажется, что нет - это с тех пор, как 1.8.1, 2011. Вопрос был задан в 2008 году. Но в любом случае groupBy теперь действительно способ. – mvmn 2014-04-01 21:48:03

3

Если вам нужна простая пара ключ-значение, тогда достаточно использовать метод collectEntries. Например

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

Но если вы хотите структуру, подобную Multimap, в котором есть несколько значений для каждого ключа, то вы хотите использовать groupBy методы

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