Groovy의 목록에서지도를 만드는 단축키? return rowMap; } GDK

나는 이것에 대해 약간의 정렬을 원합니다.

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

하지만 문서에서 아무것도 보지 못했습니다 … 뭔가 빠졌나요? 아니면 내가 너무 게으른가요?



답변

저는 최근에 정확히 목록을지도로 변환해야 할 필요성을 발견했습니다. 이 질문은 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]}
}

+연산자도 대신 사용될 수있다 <<.


답변

“주입”을 확인하십시오. 실제 함수형 프로그래밍은이를 “폴드”라고 부릅니다.

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')
}


답변

이 질문을 받았을 때 groupBy 메서드를 사용할 수 없었습니까 ?


답변

필요한 것이 간단한 키-값 쌍 collectEntries이면 방법으로 충분합니다. 예를 들면

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

그러나 키당 여러 값이있는 멀티 맵과 유사한 구조를 원한다면 groupBy방법 을 사용하고 싶을 것입니다.

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


답변

또한 Google 컬렉션 ( http://code.google.com/p/google-collections/ )을 사용하는 경우 다음과 같이 할 수 있습니다.

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


답변

그래 … 조금 더 해봤는데이게 꽤 멋진 방법 인 것 같아 …

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]

이제 내 앱이 시작될 때 호출되는 클래스에 이것을 팝하고이 메서드는 내 코드 전체에서 사용할 수 있습니다.

편집하다:

모든 배열에 메서드를 추가하려면 …

Object[].metaClass.collectMap = collectMap


답변

내장 된 항목을 찾을 수 없지만 ExpandoMetaClass를 사용하면 다음과 같이 할 수 있습니다.

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

이것은 모든 ArrayLists에 collectMap 메소드를 추가합니다 … 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]