Ярлык для создания карты из списка в groovy?
Я бы хотел, чтобы это было написано:
Map rowToMap(row) {
def rowMap = [:];
row.columns.each{ rowMap[it.name] = it.val }
return rowMap;
}
учитывая способ работы с GDK, я ожидаю, что смогу сделать что-то вроде:
Map rowToMap(row) {
row.columns.collectMap{ [it.name,it.val] }
}
но я ничего не видел в документах... я что-то упустил? или я просто слишком ленив?
Ответы
Ответ 1
Недавно я столкнулся с необходимостью сделать именно это: преобразование списка в карту. Этот вопрос был опубликован до выхода Groovy версии 1.7.9, поэтому метод collectEntries
еще не существовал. Он работает точно так же, как collectMap
метод который был предложен:
Map rowToMap(row) {
row.columns.collectEntries{[it.name, it.val]}
}
Если по какой-то причине вы застряли со старой версией Groovy, можно использовать метод inject
(как предлагается здесь). Это немного измененная версия, которая принимает только одно выражение внутри закрытия (только ради сохранения символа!):
Map rowToMap(row) {
row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}
Вместо <<
можно использовать оператор +
.
Ответ 2
Проверьте "впрыск". Реальные функциональные программируемые волки называют это "сгибом".
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')
}
Ответ 3
Был ли метод groupBy недоступен, когда задавался этот вопрос?
Ответ 4
Кроме того, если вы используете коллекцию google (http://code.google.com/p/google-collections/), вы можете сделать что-то вроде этого:
map = Maps.uniqueIndex(list, Functions.identity());
Ответ 5
ok... Я играл с этим немного больше, и я думаю, что это довольно классный метод...
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
теперь любой подкласс Map или Collection имеет этот метод...
здесь я использую его для изменения ключа/значения на карте
[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
Ответ 6
Я не могу найти ничего встроенного... но с помощью ExpandoMetaClass я могу это сделать:
ArrayList.metaClass.collectMap = {Closure callback->
def map = [:]
delegate.each {
def r = callback.call(it)
map[r[0]] = r[1]
}
return map
}
это добавляет метод collectMap ко всем массивам ArrayLists... Я не уверен, почему добавление его в List или Collection не сработало. Думаю, что для другого вопроса... но теперь я могу это сделать...
assert ["foo":"oof", "42":"24", "bar":"rab"] ==
["foo", "42", "bar"].collectMap { return [it, it.reverse()] }
из списка в расчетную карту с одним закрытием... именно то, что я искал.
Изменить: причина, по которой я не мог добавить метод в список интерфейсов и коллекцию, был потому, что я этого не делал:
List.metaClass.enableGlobally()
после вызова этого метода вы можете добавлять методы к интерфейсам. В этом случае мой метод collectMap будет работать с такими диапазонами:
(0..2).collectMap{[it, it*2]}
который дает отображение: [0: 0, 1: 2, 2: 4]
Ответ 7
Как насчет этого?
// 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']
Ответ 8
Если вам нужна простая пара ключей и значений, то метод 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]]