để tạo Bản đồ từ Danh sách theo kiểu groovy?


93

tôi muốn một số sorthand cho việc này:

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

cho cách những thứ GDK là, tôi mong đợi để có thể làm điều gì đó như:

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

nhưng tôi đã không nhìn thấy bất cứ thứ gì trong tài liệu ... tôi có thiếu gì đó không? hoặc tôi chỉ là quá lười biếng?

+2

bình luận Amir bây giờ là câu trả lời đúng: http://stackoverflow.com/a/4484958/27561 24 may. 152015-05-24 18:01:16

101

Gần đây tôi đã bắt gặp sự cần thiết 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 phiên bản Groovy 1.7.9 xuất hiện, do đó phương thức collectEntries chưa tồn tại. Nó hoạt động chính xác như collectMap phương pháp that was proposed:

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

Nếu vì một số lý do bạn đang bị mắc kẹt với một phiên bản Groovy cũ, phương pháp inject cũng có thể được sử dụng (như đề xuất here). Đây là một phiên bản sửa đổi một chút mà chỉ mất một biểu thức bên trong đóng cửa (chỉ vì lợi ích của tiết kiệm nhân vật!):

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

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


1

tôi không thể tìm thấy bất cứ điều gì được xây dựng trong ... nhưng sử dụng ExpandoMetaClass tôi có thể làm điều này:

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

này cho biết thêm phương pháp collectMap cho tất cả ArrayLists ... Tôi không chắc chắn lý do tại sao nó thêm vào danh sách hoặc Bộ sưu tập đã không làm việc .. 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 lại bản đồ tính với một kết thúc ... chính xác những gì tôi đã tìm kiếm.

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

List.metaClass.enableGlobally() 

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

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

trong đó sản lượng bản đồ: [0: 0, 1: 2, 2: 4]


5

Ngoài ra, nếu bạn đang sử dụng bộ sưu tập của google (http://code.google.com/p/google-collections/), bạn có thể làm một cái gì đó như thế này:

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

5

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

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 

nay bất kỳ lớp con của Map hoặc Bộ sưu tập có phương pháp này ...

ở đây tôi sử dụng nó để đảo ngược chìa khóa/giá trị trong một 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 ra một bản đồ từ một danh sách

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

bây giờ tôi chỉ cần bật này vào một lớp học mà được gọi là ứng dụng của tôi đang bắt đầu và phương pháp này hiện có sẵn trên khắp mã của tôi.

EDIT:

để thêm phương pháp để tất cả các mảng ...

Object[].metaClass.collectMap = collectMap 

0

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

Về cơ bản giống như trong câu hỏi của tôi ... Tôi chỉ có bản đồ [it.k] = it.v thay vì .putAt Tôi đang tìm kiếm cho một lớp lót. 13 oct. 082008-10-13 01:43:39


28

Kiểm tra "tiêm". Các chương trình 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à bạn có thể muốn xác định phương pháp làm 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') 
} 
  0

Tôi đoán nó được đặt tên là tiêm trong Groovy vì nó có thể đã được lấy cảm hứng từ tiêm: vào: trong Smalltalk: | danh sách tổng hợp | danh sách: = OrderedCollection mới thêm: 1; thêm: 2; thêm: 3; bản thân bạn. tổng: = danh sách tiêm: 0 vào: [: a: b | a + b]. Bản ghi cr; show: sum. "prints 6" 02 jan. 132013-01-02 18:41:31


13

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

  0

Có vẻ như không - kể từ 1.8.1, 2011. Câu hỏi được hỏi vào năm 2008. Nhưng trong mọi trường hợp, groupBy giờ đây là con đường thực sự. 01 apr. 142014-04-01 21:48:03


3

Nếu những gì bạn cần là cặp khóa-giá trị đơn giản, thì phương pháp collectEntries 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 có một cấu trúc tương tự như một Multimap, trong đó có nhiều giá trị cho mỗi khoá, sau đó bạn muốn sử dụng các groupBy phương pháp

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