ทางลัดสำหรับการสร้างแผนที่จากรายการใน groovy?


107

ฉันต้องการบางอย่างสำหรับสิ่งนี้:

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

แต่ฉันไม่เห็นอะไรในเอกสาร ... ฉันขาดอะไรไปหรือเปล่า? หรือฉันแค่ขี้เกียจเกินไป?


2
ตอนนี้ความคิดเห็นของ Amir เป็นคำตอบที่ถูกต้อง: stackoverflow.com/a/4484958/27561
Robert Fischer

คำตอบ:


120

ฉันเพิ่งเจอความจำเป็นที่จะต้องทำอย่างนั้นนั่นคือการแปลงรายการเป็นแผนที่ คำถามนี้ถูกโพสต์ก่อนที่ 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]}
}

ผู้ประกอบการยังสามารถนำมาใช้แทน+<<


28

ลองดู "ฉีด" การเขียนโปรแกรมที่ใช้งานได้จริงเรียกว่า "พับ"

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

และในขณะที่คุณทำอยู่คุณอาจต้องการกำหนดวิธีการเป็นหมวดหมู่แทนที่จะอยู่ใน metaClass ด้วยวิธีนี้คุณสามารถกำหนดครั้งเดียวสำหรับคอลเล็กชันทั้งหมด:

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

ฉันเดาว่ามันชื่อ injection ใน Groovy เนื่องจากอาจได้รับแรงบันดาลใจจากการฉีด: เข้า: ใน Smalltalk: | ผลรวมรายการ | รายการ: = OrderCollection เพิ่มใหม่: 1; เพิ่ม: 2; เพิ่ม: 3; ตัวคุณเอง sum: = list injection: 0 เข้าไป: [: a: b | a + b] การถอดเสียง cr; แสดง: ผลรวม "ภาพพิมพ์ 6"
OlliP

13

เป็นgroupByวิธีการไม่สามารถใช้ได้เมื่อคำถามนี้ถูกถาม?


ดูเหมือนจะไม่ใช่ - ตั้งแต่ 1.8.1 ปี 2011 คำถามถูกถามในปี 2008 แต่ไม่ว่าในกรณีใด groupBy คือหนทางที่จะไปต่อ
mvmn

ดังที่คุณเห็นในเอกสารสำหรับ groupBy โดยพื้นฐานแล้วจะจัดกลุ่มองค์ประกอบต่างๆเป็นกลุ่มโดยแต่ละกลุ่มมีองค์ประกอบที่ตรงกับคีย์หนึ่ง ๆ ดังนั้นประเภทการส่งคืนจึงMap<K, List<V>>ดูเหมือนว่า OP กำลังมองหาวิธีการที่มีประเภทการส่งคืนMap<K, V>ดังนั้น groupBy จึงไม่ทำงานในกรณีนี้
Krzysiek Przygudzki

6

หากสิ่งที่คุณต้องการคือคู่คีย์ - ค่าธรรมดาวิธีนี้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]]


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

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 ทั้งหมด ... ฉันไม่แน่ใจว่าทำไมเพิ่มลงใน 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]


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

นั่นก็เหมือนกับในคำถามของฉัน ... ฉันเพิ่งมี map [it.k] = it.v แทน .putAt ฉันกำลังมองหาซับหนึ่ง
danb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.