lối tắt để tạo Bản đồ từ Danh sách trong Groovy?


106

Tôi muốn một số phân loại cho điều này:

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

với cách thức của công cụ GDK, tôi mong đợi có thể làm được những điều như:

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

nhưng tôi chưa thấy bất cứ thứ gì trong tài liệu ... tôi có thiếu thứ gì không? hay là tôi quá lười biếng?


2
Nhận xét của Amir bây giờ là câu trả lời chính xác: stackoverflow.com/a/4484958/27561
Robert Fischer

Câu trả lời:


119

Gần đây tôi đã nhận ra rằng cần phải làm chính xác điều đó: chuyển đổi danh sách thành bản đồ. Câu hỏi này đã được đăng trước khi Groovy phiên bản 1.7.9 ra mắt, vì vậy phương pháp collectEntriesnày vẫn chưa tồn tại. Nó hoạt động chính xác như collectMapphương pháp đã được đề xuất :

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

Nếu vì lý do nào đó mà bạn gặp khó khăn với phiên bản Groovy cũ hơn, injectphương pháp này cũng có thể được sử dụng (như được đề xuất ở đây ). Đây là một phiên bản được sửa đổi một chút chỉ có một biểu thức bên trong đóng (chỉ nhằm mục đích lưu ký tự!):

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

Các +nhà điều hành cũng có thể được sử dụng thay cho <<.


28

Kiểm tra "tiêm". Lập trình chức năng thực sự gọi nó là "gấp".

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

Và, khi đang ở đó, bạn có thể muốn xác định các phương thức dưới dạng Danh mục thay vì ngay trên metaClass. Bằng cách đó, bạn có thể xác định nó một lần cho tất cả các Bộ sưu tập:

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

Ví dụ sử dụng:

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

Tôi đoán nó được đặt tên là tiêm ở Groovy vì nó có thể được lấy cảm hứng từ tiêm: thành: trong Smalltalk: | danh sách tổng hợp | list: = OrderCollection mới thêm: 1; thêm: 2; thêm: 3; bản thân bạn. sum: = list insert: 0 into: [: a: b | a + b]. Bảng điểm cr; show: sum. "bản in 6"
OlliP

13

groupby phương pháp không có sẵn khi câu hỏi này được hỏi?


Có vẻ như không - đó là từ ngày 1.8.1 năm 2011. Câu hỏi được đặt ra vào năm 2008. Nhưng trong mọi trường hợp, groupBy thực sự là con đường để đi.
mvmn

Như bạn có thể thấy trong tài liệu về groupBy, về cơ bản, nó nhóm các phần tử thành các nhóm, trong đó mỗi nhóm chứa phần tử khớp với một khóa nhất định. Do đó, kiểu trả về của nó là Map<K, List<V>>Có vẻ như OP đang tìm kiếm một phương thức có kiểu trả về Map<K, V>, vì vậy groupBy không hoạt động trong trường hợp này.
Krzysiek Przygudzki

6

Nếu những gì bạn cần là một cặp khóa-giá trị đơn giản, thì phương thức collectEntriesnày là đủ. Ví dụ

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

Nhưng nếu bạn muốn một cấu trúc tương tự như Multimap, trong đó có nhiều giá trị cho mỗi khóa, thì bạn muốn sử dụng groupByphương pháp

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


5

ok ... Tôi đã chơi với điều này nhiều hơn một chút và tôi nghĩ đây là một phương pháp khá hay ...

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

bây giờ bất kỳ lớp con nào của Bản đồ hoặc Tập hợp đều có phương thức này ...

ở đây tôi sử dụng nó để đảo ngược khóa / giá trị trong Bản đồ

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

và ở đây tôi sử dụng nó để tạo bản đồ từ danh sách

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

bây giờ tôi chỉ đưa nó vào một lớp được gọi khi ứng dụng của tôi đang khởi động và phương thức này có sẵn trong mã của tôi.

BIÊN TẬP:

để thêm phương thức vào tất cả các mảng ...

Object[].metaClass.collectMap = collectMap

1

Tôi không thể tìm thấy bất kỳ thứ gì được tích hợp sẵn ... nhưng bằng cách sử dụng ExpandoMetaClass, tôi có thể thực hiện việc này:

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

điều này sẽ thêm phương thức collectMap vào tất cả ArrayLists ... Tôi không chắc tại sao việc thêm nó vào Danh sách hoặc Bộ sưu tập không hoạt động .. Tôi đoán đó là cho một câu hỏi khác ... nhưng bây giờ tôi có thể làm điều này ...

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

từ Danh sách đến Bản đồ được tính toán với một lần đóng ... chính xác những gì tôi đang tìm kiếm.

Chỉnh sửa: lý do tôi không thể thêm phương thức vào Danh sách và Bộ sưu tập giao diện là vì tôi đã không làm điều này:

List.metaClass.enableGlobally()

sau cuộc gọi phương thức đó, bạn có thể thêm các phương thức vào giao diện .. trong trường hợp này có nghĩa là phương thức collectMap của tôi sẽ hoạt động trên các phạm vi như sau:

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

tạo ra bản đồ: [0: 0, 1: 2, 2: 4]


0

Những gì về một cái gì đó như thế này?

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

Về cơ bản, điều đó giống như trong câu hỏi của tôi ... Tôi chỉ có map [it.k] = it.v thay vì .putTôi đang tìm kiếm một lớp lót.
danb 13/10/08
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.