Ярлык для создания карты из списка в заводной?

голоса
93

Я хотел бы некоторые sorthand для этого:

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] }
}

но я ничего в документации не видел ... я что-то отсутствует? или я просто слишком ленив?

Задан 20/08/2008 в 19:37
источник пользователем
На других языках...                            


8 ответов

голоса
102

Я недавно наткнулся на необходимость делать именно это: преобразование списка в карту. Этот вопрос был размещен перед тем 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]}
}

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

Ответил 13/04/2011 d 07:47
источник пользователем

голоса
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')
}
Ответил 13/10/2008 d 19:48
источник пользователем

голоса
13

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

Ответил 19/12/2010 d 21:34
источник пользователем

голоса
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]

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

РЕДАКТИРОВАТЬ:

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

Object[].metaClass.collectMap = collectMap
Ответил 21/08/2008 d 00:28
источник пользователем

голоса
5

Кроме того , если вы используете Google коллекции ( http://code.google.com/p/google-collections/ ), вы можете сделать что - то вроде этого:

  map = Maps.uniqueIndex(list, Functions.identity());
Ответил 20/08/2008 d 23:32
источник пользователем

голоса
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]]
Ответил 09/03/2017 d 08:06
источник пользователем

голоса
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()] }

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

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

List.metaClass.enableGlobally()

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

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

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

Ответил 20/08/2008 d 23:25
источник пользователем

голоса
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']
Ответил 29/09/2008 d 18:41
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more