ในโกลังมีวิธีที่ดีในการหาค่าจากแผนที่หรือไม่?


90

ถ้าฉันมีแผนที่ m จะมีวิธีที่ดีกว่าในการหาค่า v หรือไม่

package main
import (
  "fmt"
)

func main() {
    m := make(map[int]string)

    m[1] = "a"
    m[2] = "b"
    m[3] = "c"
    m[4] = "d"

    // Can this be done better?
    v := make([]string, len(m), len(m))
    idx := 0
    for  _, value := range m {
       v[idx] = value
       idx++
    }

    fmt.Println(v)
 }

มีคุณลักษณะที่สร้างขึ้นของแผนที่หรือไม่? มีฟังก์ชั่นในแพ็คเกจ Go หรือไม่หรือนี่เป็นรหัสที่ดีที่สุดที่ต้องทำหากต้องทำ


1
แทนที่จะเป็น '_' ใน for loop ของคุณเรียกว่า idx และทิ้งธุรกิจ idx ++
Peter Agnew

ไม่เขาไม่สามารถเมื่อคุณอยู่ในช่วงบนแผนที่มันจะส่งคืนคีย์ค่าไม่ใช่ดัชนี ในตัวอย่างของเขาเขาใช้ 1 เป็นคีย์แรกและนั่นจะทำให้ดัชนีใน slice v ไม่ถูกต้องเนื่องจากดัชนีเริ่มต้นจะเป็น 1 ไม่ใช่ศูนย์และเมื่อถึง 4 มันจะอยู่นอกช่วง play.golang.org/p/X8_SbgxK4VX
Popmedic

@ Popmedic จริงๆแล้วเขาทำได้ เพียงแค่เปลี่ยน_กับidxและใช้idx-1เมื่อกำหนดค่าชิ้น
hewiefreeman

3
@ newplayer66 นั่นเป็นรูปแบบที่อันตรายมาก
Popmedic

คำตอบ:


60

น่าเสียดายที่ไม่มี ไม่มีวิธีในตัวที่จะทำเช่นนี้

หมายเหตุด้านข้างคุณสามารถละเว้นอาร์กิวเมนต์ความจุในการสร้างชิ้นของคุณ:

v := make([]string, len(m))

ความจุโดยนัยจะเท่ากับความยาวที่นี่


1
ฉันคิดว่ามีวิธีที่ดีกว่า: stackoverflow.com/a/61953291/1162217
Lukas Lukac

58

นอกเหนือจากโพสต์ของ jimt:

คุณยังสามารถใช้appendแทนที่จะกำหนดค่าให้กับดัชนีอย่างชัดเจน:

m := make(map[int]string)

m[1] = "a"
m[2] = "b"
m[3] = "c"
m[4] = "d"

v := make([]string, 0, len(m))

for  _, value := range m {
   v = append(v, value)
}

โปรดทราบว่ามีความยาวเป็นศูนย์ (องค์ประกอบไม่นำเสนอยัง) แต่ความจุ (พื้นที่จัดสรร) mจะเริ่มต้นด้วยจำนวนขององค์ประกอบของ สิ่งนี้ทำได้โดยappendไม่จำเป็นต้องจัดสรรหน่วยความจำทุกครั้งที่ความจุของชิ้นส่วนvหมด

คุณยังสามารถmakeแบ่งส่วนที่ไม่มีค่าความจุและปล่อยให้appendจัดสรรหน่วยความจำให้เอง


ฉันสงสัยว่าสิ่งนี้จะช้ากว่านี้หรือไม่ (สมมติว่าเป็นการจัดสรรล่วงหน้า)? ฉันทำเกณฑ์มาตรฐานคร่าวๆด้วย map [int] int และดูเหมือนช้าลงประมาณ 1-2% ความคิดใด ๆ หากนี่เป็นสิ่งที่ต้องกังวลหรือแค่ไปกับมัน
masebase

1
ฉันจะถือว่าต่อท้ายช้าลงเล็กน้อย แต่ความแตกต่างนั้นในกรณีส่วนใหญ่เล็กน้อย เกณฑ์มาตรฐานเปรียบเทียบมอบหมายโดยตรงและผนวก
nemo

1
โปรดใช้ความระมัดระวังในการผสมสิ่งนี้กับคำตอบด้านบนการต่อท้ายอาร์เรย์หลังจากที่คุณใช้make([]appsv1.Deployment, len(d))จะต่อท้ายองค์ประกอบว่างจำนวนมากที่สร้างขึ้นเมื่อคุณจัดสรรlen(d)รายการว่าง
Anirudh Ramanathan

2

ไม่จำเป็นต้องดีกว่า แต่วิธีที่สะอาดกว่านี้คือการกำหนดทั้งSlice LENGTH และ CAPACITYเช่นtxs := make([]Tx, 0, len(txMap))

    // Defines the Slice capacity to match the Map elements count
    txs := make([]Tx, 0, len(txMap))

    for _, tx := range txMap {
        txs = append(txs, tx)
    }

ตัวอย่างเต็ม:

package main

import (
    "github.com/davecgh/go-spew/spew"
)

type Tx struct {
    from  string
    to    string
    value uint64
}

func main() {
    // Extra touch pre-defining the Map length to avoid reallocation
    txMap := make(map[string]Tx, 3)
    txMap["tx1"] = Tx{"andrej", "babayaga", 10}
    txMap["tx2"] = Tx{"andrej", "babayaga", 20}
    txMap["tx3"] = Tx{"andrej", "babayaga", 30}

    txSlice := getTXsAsSlice(txMap)
    spew.Dump(txSlice)
}

func getTXsAsSlice(txMap map[string]Tx) []Tx {
    // Defines the Slice capacity to match the Map elements count
    txs := make([]Tx, 0, len(txMap))
    for _, tx := range txMap {
        txs = append(txs, tx)
    }

    return txs
}

วิธีแก้ปัญหาง่ายๆ แต่มี gotchas มากมาย อ่านบล็อกโพสต์นี้สำหรับรายละเอียดเพิ่มเติม: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas


คำตอบคือไม่ "ผิด" คำถามใช้ดัชนีและไม่มีการต่อท้ายสำหรับชิ้นส่วน หากคุณใช้ผนวกกับชิ้นส่วนใช่การตั้งค่าความยาวไว้ด้านหน้าจะไม่ดี นี่คือคำถามที่เป็น play.golang.org/p/nsIlIl24Irn ยอมรับว่าคำถามนั้นไม่ใช่สำนวนและฉันก็ยังเรียนรู้อยู่ รุ่นปรับปรุงเล็กน้อยเหมือนที่คุณพูดถึงคือ play.golang.org/p/4SKxC48wg2b
masebase

1
สวัสดี @masebase ฉันคิดว่า "ผิด" เพราะระบุว่า: "น่าเสียดายไม่มีไม่มีวิธีในตัวที่จะทำสิ่งนี้" แต่มีวิธีแก้ปัญหาที่ "ดีกว่า" ที่เราทั้งสองกำลังชี้ให้เห็นในตอนนี้ในอีก 2 ปีต่อมาโดยใช้การผนวก () และกำหนดทั้งความยาวและความจุ แต่ฉันเห็นประเด็นของคุณว่าการเรียกมันว่า "ผิด" นั้นไม่ถูกต้องเช่นกัน ฉันจะเปลี่ยนประโยคแรกเป็น: "ไม่จำเป็นต้องดีกว่า แต่วิธีที่สะอาดกว่านี้คือ" ฉันได้พูดคุยกับผู้พัฒนา ~ 10 คนและทุกคนเห็นด้วยกับการผนวก () เป็นวิธีที่สะอาดกว่าในการแปลงแผนที่เป็นสไลซ์โดยไม่ต้องใช้ดัชนีตัวช่วย ฉันได้เรียนรู้สิ่งนี้เช่นกันเกี่ยวกับวิธีการโพสต์
Lukas Lukac

1

เท่าที่ฉันทราบในขณะนี้ go ไม่มีวิธีการเชื่อมต่อสตริง / ไบต์เข้ากับสตริงผลลัพธ์โดยไม่ต้องทำอย่างน้อย / สอง / สำเนา

ขณะนี้คุณต้องเพิ่ม [] ไบต์เนื่องจากค่าสตริงทั้งหมดเป็น const จากนั้นคุณต้องใช้สตริงในตัวเพื่อให้ภาษาสร้างอ็อบเจ็กต์สตริง 'มีความสุข' ซึ่งจะคัดลอกบัฟเฟอร์เนื่องจากบางสิ่งบางอย่างอาจมีการอ้างอิง ไปยังที่อยู่สำรอง [] ไบต์

ถ้า [] ไบต์เหมาะสมคุณจะได้รับโอกาสในการขายมากกว่าไบต์เล็กน้อยเข้าร่วมฟังก์ชันโดยทำการจัดสรรหนึ่งครั้งและทำสำเนาเรียกตัวคุณเอง

package main
import (
  "fmt"
)

func main() {
m := make(map[int]string)

m[1] = "a" ;    m[2] = "b" ;     m[3] = "c" ;    m[4] = "d"

ip := 0

/* If the elements of m are not all of fixed length you must use a method like this;
 * in that case also consider:
 * bytes.Join() and/or
 * strings.Join()
 * They are likely preferable for maintainability over small performance change.

for _, v := range m {
    ip += len(v)
}
*/

ip = len(m) * 1 // length of elements in m
r := make([]byte, ip, ip)
ip = 0
for  _, v := range m {
   ip += copy(r[ip:], v)
}

// r (return value) is currently a []byte, it mostly differs from 'string'
// in that it can be grown and has a different default fmt method.

fmt.Printf("%s\n", r)
}

1

คุณสามารถใช้mapsแพ็คเกจนี้:

go get https://github.com/drgrib/maps

จากนั้นสิ่งที่คุณต้องโทรคือ

values := maps.GetValuesIntString(m)

เป็นประเภทที่ปลอดภัยสำหรับmapชุดค่าผสมทั่วไปนั้น คุณสามารถgenerateใช้ฟังก์ชันชนิดอื่นที่ปลอดภัยสำหรับประเภทอื่น ๆmapโดยใช้mapperเครื่องมือในแพ็คเกจเดียวกัน

การเปิดเผยข้อมูลทั้งหมด: ฉันเป็นผู้สร้างแพ็คเกจนี้ ฉันสร้างมันขึ้นมาเพราะฉันพบว่าตัวเองเขียนฟังก์ชันเหล่านี้mapซ้ำ ๆ

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