จัดเรียงค่าแผนที่ไปตามคีย์


96

เมื่อทำซ้ำในแผนที่ที่ส่งคืนในโค้ดซึ่งส่งคืนโดยฟังก์ชันหัวข้อคีย์จะไม่ปรากฏตามลำดับ

ฉันจะทำให้คีย์อยู่ในลำดับ / จัดเรียงแผนที่เพื่อให้คีย์เรียงลำดับและค่าสอดคล้องกันได้อย่างไร

นี่คือรหัส


เป็นไปได้ที่จะซ้ำกันของHow to iterate through a map in golang in order?
RedGrittyBrick

คำตอบ:


160

ไปบล็อก: Go แผนที่ในการดำเนินการมีคำอธิบายที่ดีเยี่ยม

เมื่อทำการวนซ้ำบนแผนที่ที่มีการวนรอบช่วงจะไม่มีการระบุลำดับการวนซ้ำและไม่รับประกันว่าจะเหมือนกันจากการวนซ้ำครั้งหนึ่งไปอีกครั้ง ตั้งแต่ Go 1 รันไทม์จะสุ่มลำดับการวนซ้ำแผนที่เนื่องจากโปรแกรมเมอร์อาศัยลำดับการวนซ้ำที่เสถียรของการนำไปใช้ก่อนหน้านี้ หากคุณต้องการลำดับการทำซ้ำที่เสถียรคุณต้องรักษาโครงสร้างข้อมูลแยกต่างหากที่ระบุคำสั่งนั้น

นี่คือโค้ดตัวอย่างเวอร์ชันแก้ไขของฉัน: http://play.golang.org/p/dvqcGPYy3-

package main

import (
    "fmt"
    "sort"
)

func main() {
    // To create a map as input
    m := make(map[int]string)
    m[1] = "a"
    m[2] = "c"
    m[0] = "b"

    // To store the keys in slice in sorted order
    keys := make([]int, len(m))
    i := 0
    for k := range m {
        keys[i] = k
        i++
    }
    sort.Ints(keys)

    // To perform the opertion you want
    for _, k := range keys {
        fmt.Println("Key:", k, "Value:", m[k])
    }
}

เอาท์พุต:

Key: 0 Value: b
Key: 1 Value: a
Key: 2 Value: c

41
สิ่งนี้สามารถปรับปรุงได้โดยใช้keys := make([]int, len(m))แล้วแทรกด้วยดัชนีkeys[i] = kแทนappend
jpillora

18

ตามข้อกำหนดGoลำดับของการวนซ้ำบนแผนที่ไม่ได้กำหนดไว้และอาจแตกต่างกันไประหว่างการทำงานของโปรแกรม ในทางปฏิบัติไม่เพียง แต่ไม่ได้กำหนด แต่เป็นการสุ่มโดยเจตนา เนื่องจากเคยเป็นสิ่งที่คาดเดาได้และนักพัฒนาภาษา Go ไม่ต้องการให้ผู้คนอาศัยพฤติกรรมที่ไม่ระบุรายละเอียดดังนั้นพวกเขาจึงสุ่มตัวอย่างโดยเจตนาเพื่อให้การพึ่งพาพฤติกรรมนี้เป็นไปไม่ได้

สิ่งที่คุณจะต้องทำคือดึงคีย์ออกเป็นชิ้น ๆ เรียงลำดับจากนั้นวางช่วงบนชิ้นดังนี้:

var m map[keyType]valueType
keys := sliceOfKeys(m) // you'll have to implement this
for _, k := range keys {
    v := m[k]
    // k is the key and v is the value; do your computation here
}

ส่วนรอเป็นส่วนหนึ่งของอาร์เรย์ ฉันจะสร้างเฉพาะคีย์ในแผนที่ได้อย่างไร?
gramme.ninja

1
ใน Go คำว่า "slice" หมายถึงโครงสร้างข้อมูลที่คล้ายคลึงกับอาร์เรย์ Java เป็นเพียงปัญหาเรื่องศัพท์ สำหรับการรับกุญแจคุณต้องกำหนดระยะอย่างชัดเจนบนแผนที่และสร้างชิ้นส่วนในขณะที่คุณไปดังนี้: play.golang.org/p/ZTni0o19Lr
Joshlf

อ่าโอเค. ขอบคุณสำหรับการสอนฉัน ตอนนี้มันกำลังพิมพ์ทั้งหมดแล้วก็คี่ทั้งหมด play.golang.org/p/K2y3m4Zzqdฉันจะเอามันไปสลับกันได้อย่างไร?
gramme.ninja

1
คุณจะต้องจัดเรียงชิ้นส่วนที่คุณได้รับกลับมา (หรืออีกวิธีหนึ่งคือจัดเรียงใน mapKeys ก่อนกลับ) คุณจะต้องตรวจสอบแพ็คเกจการจัดเรียง
joshlf

15

คำตอบทั้งหมดในที่นี้มีพฤติกรรมเก่าของแผนที่ ใน Go 1.12+ คุณสามารถพิมพ์ค่าแผนที่และจะจัดเรียงตามคีย์โดยอัตโนมัติ สิ่งนี้ถูกเพิ่มเข้ามาเนื่องจากช่วยให้สามารถทดสอบค่าแผนที่ได้อย่างง่ายดาย

func main() {
    m := map[int]int{3: 5, 2: 4, 1: 3}
    fmt.Println(m)

    // In Go 1.12+
    // Output: map[1:3 2:4 3:5]

    // Before Go 1.12 (the order was undefined)
    // map[3:5 2:4 1:3]
}

ขณะนี้แผนที่ได้รับการพิมพ์ตามลำดับคีย์เพื่อความสะดวกในการทดสอบ กฎการสั่งซื้อคือ:

  • เมื่อทำได้ศูนย์จะเปรียบเทียบค่าต่ำ
  • ints ลอยและสตริงเรียงลำดับตาม <
  • NaN เปรียบเทียบน้อยกว่าการลอยตัวที่ไม่ใช่ NaN
  • บูลเปรียบเทียบเท็จก่อนจริง
  • คอมเพล็กซ์เปรียบเทียบของจริงตามด้วยจินตภาพ
  • ตัวชี้เปรียบเทียบตามที่อยู่เครื่อง
  • ค่าช่องจะเปรียบเทียบตามที่อยู่เครื่อง
  • โครงสร้างเปรียบเทียบแต่ละเขตข้อมูลในทางกลับกัน
  • อาร์เรย์เปรียบเทียบแต่ละองค์ประกอบในทางกลับกัน
  • ค่าอินเทอร์เฟซเปรียบเทียบก่อนโดยการไตร่ตรองพิมพ์อธิบายประเภทคอนกรีตแล้วตามด้วยมูลค่าคอนกรีตตามที่อธิบายไว้ในกฎก่อนหน้า

<nil>เมื่อพิมพ์แผนที่ค่าคีย์ที่ไม่สะท้อนเช่นน่านมีการแสดงผลก่อนหน้านี้เป็น ในรุ่นนี้จะมีการพิมพ์ค่าที่ถูกต้อง

อ่านเพิ่มเติมที่นี่


9
ดูเหมือนจะใช้ได้กับแพ็คเกจ fmt และการพิมพ์เท่านั้น คำถามถามว่าจะจัดเรียงแผนที่อย่างไรไม่ใช่จะพิมพ์แผนที่เรียงลำดับอย่างไร?
ทิม

1
เขาแชร์ลิงค์สนามเด็กเล่น ในนั้นเขาพิมพ์แผนที่
Inanc Gumus

2

ในการตอบกลับเจมส์เครกเบอร์ลีย์ของคำตอบ เพื่อให้การออกแบบที่สะอาดและสามารถนำกลับมาใช้ใหม่ได้เราอาจเลือกแนวทางที่เน้นวัตถุมากกว่า วิธีนี้สามารถผูกมัดกับประเภทของแผนที่ที่ระบุได้อย่างปลอดภัย สำหรับฉันวิธีนี้ให้ความรู้สึกสะอาดและเป็นระเบียบ

ตัวอย่าง:

package main

import (
    "fmt"
    "sort"
)

type myIntMap map[int]string

func (m myIntMap) sort() (index []int) {
    for k, _ := range m {
        index = append(index, k)
    }
    sort.Ints(index)
    return
}

func main() {
    m := myIntMap{
        1:  "one",
        11: "eleven",
        3:  "three",
    }
    for _, k := range m.sort() {
        fmt.Println(m[k])
    }
}

ตัวอย่างสนามเด็กเล่นแบบขยายที่มีแผนที่หลายประเภท

โน๊ตสำคัญ

ในทุกกรณีแผนที่และชิ้นส่วนที่เรียงลำดับจะถูกแยกออกจากช่วงเวลาที่การforวนซ้ำบนแผนที่rangeเสร็จสิ้น หมายความว่าหากแผนที่ได้รับการแก้ไขหลังจากตรรกะการจัดเรียง แต่ก่อนที่คุณจะใช้คุณอาจประสบปัญหาได้ (ไม่ด้าย / ไปประจำอย่างปลอดภัย) หากมีการเปลี่ยนแปลงการเข้าถึงการเขียนแผนที่แบบขนานคุณจะต้องใช้ mutex รอบ ๆ การเขียนและการforวนซ้ำที่เรียงลำดับ

mutex.Lock()
for _, k := range m.sort() {
    fmt.Println(m[k])
}
mutex.Unlock()

1

ถ้าเช่นฉันคุณพบว่าคุณต้องการรหัสการเรียงลำดับเดียวกันเป็นหลักในมากกว่าหนึ่งที่หรือเพียงแค่ต้องการลดความซับซ้อนของโค้ดคุณสามารถแยกการเรียงลำดับตัวเองไปยังฟังก์ชันแยกต่างหากซึ่งคุณส่งผ่านฟังก์ชันที่ทำ งานจริงที่คุณต้องการ (ซึ่งแน่นอนว่าจะแตกต่างกันไปในแต่ละไซต์การโทร)

ด้วยแผนที่ที่มีประเภทคีย์Kและประเภทค่าVซึ่งแสดงเป็น<K>และ<V>ด้านล่างฟังก์ชันการจัดเรียงทั่วไปอาจมีลักษณะคล้ายกับเทมเพลต Go-code นี้ (ซึ่ง Go เวอร์ชัน 1 ไม่รองรับตามที่เป็นอยู่):

/* Go apparently doesn't support/allow 'interface{}' as the value (or
/* key) of a map such that any arbitrary type can be substituted at
/* run time, so several of these nearly-identical functions might be
/* needed for different key/value type combinations. */
func sortedMap<K><T>(m map[<K>]<V>, f func(k <K>, v <V>)) {
    var keys []<K>
    for k, _ := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)  # or sort.Ints(keys), sort.Sort(...), etc., per <K>
    for _, k := range keys {
        v := m[k]
        f(k, v)
    }
}

จากนั้นเรียกใช้ด้วยแผนที่อินพุตและฟังก์ชัน (รับ(k <K>, v <V>)อาร์กิวเมนต์อินพุต) ที่เรียกใช้บนองค์ประกอบแผนที่ตามลำดับคีย์ที่เรียงลำดับ

ดังนั้นเวอร์ชันของรหัสในคำตอบที่ Mingu โพสต์อาจมีลักษณะดังนี้:

package main

import (
    "fmt"
    "sort"
)

func sortedMapIntString(m map[int]string, f func(k int, v string)) {
    var keys []int
    for k, _ := range m {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, k := range keys {
        f(k, m[k])
    }
}

func main() {
    // Create a map for processing
    m := make(map[int]string)
    m[1] = "a"
    m[2] = "c"
    m[0] = "b"

    sortedMapIntString(m,
        func(k int, v string) { fmt.Println("Key:", k, "Value:", v) })
}

sortedMapIntString()ฟังก์ชั่นที่สามารถนำมาใช้สำหรับการใด ๆmap[int]string(สมมติว่าการเรียงลำดับเดียวกันคือต้องการ) ทำให้การใช้งานแต่ละที่จะเพียงแค่สองบรรทัดของรหัส

ข้อเสีย ได้แก่ :

  • อ่านยากกว่าสำหรับคนที่ไม่คุ้นเคยกับการใช้ฟังก์ชันเป็นชั้นหนึ่ง
  • อาจจะช้าลง (ฉันยังไม่ได้ทำการเปรียบเทียบประสิทธิภาพ)

ภาษาอื่น ๆ มีวิธีแก้ปัญหามากมาย:

  • หากการใช้<K>และ<V>(เพื่อแสดงถึงประเภทของคีย์และค่า) ดูคุ้นเคยเล็กน้อยเทมเพลตโค้ดนั้นจะไม่แตกต่างจากเทมเพลต C ++ มากนัก
  • Clojure และภาษาอื่น ๆ รองรับแผนที่ที่จัดเรียงเป็นประเภทข้อมูลพื้นฐาน
  • ในขณะที่ฉันไม่รู้ว่า Go สร้างrangeประเภทเฟิร์สคลาสเพื่อให้สามารถแทนที่ด้วยแบบกำหนดเองได้ordered-range(แทนที่rangeโค้ดดั้งเดิม) แต่ฉันคิดว่าภาษาอื่น ๆ บางภาษามีตัวทำซ้ำที่มีประสิทธิภาพเพียงพอที่จะทำสิ่งเดียวกันให้สำเร็จ สิ่ง.

2
อาจเป็นการดีที่จะชี้ให้เห็นว่าสำหรับผู้เริ่มต้นไม่รองรับไวยากรณ์ <K>, <V> ใน Go
justinhj

รัฐรหัสของคุณ: ไปเห็นได้ชัดว่าไม่สนับสนุน / ช่วยให้อินเตอร์เฟซ {} 'เป็นค่า (หรือคีย์) ของแผนที่ นี่ไม่เป็นความจริง. var m map[interface{}]interface{}ถูกกฎหมายอย่างสมบูรณ์ Playground
Tim

Go apparently doesn't support/allow 'interface{}' as the value (or key) of a map such that any arbitrary type can be substituted at run timeแต่จริงๆแล้วความคิดเห็นบล็อกรัฐ หาก (นอกเหนือจากการใช้unsafeหรือคล้ายกัน) คุณสามารถแสดงให้เห็นว่าสามารถแทนที่ประเภทที่กำหนดเองได้อย่างไรในขณะรันไทม์ดังนั้นการจัดเรียงรูทีนการเรียงทั่วไปเพียงครั้งเดียวจะเป็นการดีมาก! (ฉันคิดไม่ออกเองเมื่อหลายเดือนก่อน)
James Craig Burley

0

นี่เป็นตัวอย่างโค้ดในการจัดเรียงแผนที่ โดยทั่วไปนี่คือสิ่งที่พวกเขาให้:

var keys []int
for k := range myMap {
    keys = append(keys, k)
}
sort.Ints(keys)

// Benchmark1-8      2863149           374 ns/op         152 B/op          5 allocs/op

และนี่คือสิ่งที่ฉันขอแนะนำให้ใช้แทน :

keys := make([]int, 0, len(myMap))
for k := range myMap {
    keys = append(keys, k)
}
sort.Ints(keys)

// Benchmark2-8      5320446           230 ns/op          80 B/op          2 allocs/op

รหัสเต็มสามารถพบได้ในนี้ไปสนามเด็กเล่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.