Trong golang, có cách nào hay để lấy một phần giá trị từ bản đồ không?


89

Nếu tôi có một bản đồ m thì có cách nào tốt hơn để lấy một phần của các giá trị v sau đó không

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

Có một đối tượng địa lý được xây dựng của bản đồ không? Có một chức năng trong gói Go hay đây là mã tốt nhất để làm nếu tôi phải làm vậy?


1
thay vì '_' trong cho vòng lặp, gọi nó là idx và mương idx ++ kinh doanh
Peter Agnew

Không, anh ta không thể, khi bạn phạm vi trên một bản đồ, nó trả về khóa, giá trị chứ không phải chỉ mục, giá trị. Trong ví dụ của mình, anh ấy sử dụng 1 làm khóa đầu tiên và điều đó sẽ làm cho các chỉ mục trong lát v không chính xác vì chỉ mục bắt đầu sẽ là 1 chứ không phải 0, và khi nó lên đến 4 thì nó sẽ nằm ngoài phạm vi. play.golang.org/p/X8_SbgxK4VX
Popmedic

@Popmedic Thực ra, anh ấy có thể. chỉ cần thay thế _bằng idxvà sử dụng idx-1khi gán giá trị lát cắt.
hewiefreeman

3
@ newplayer66, đó là một mô hình rất nguy hiểm.
Popmedic

Câu trả lời:


60

Tiếc là không có. Không có cách tích hợp để làm điều này.

Lưu ý thêm, bạn có thể bỏ qua đối số dung lượng trong quá trình tạo lát cắt của mình:

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

Dung lượng được ngụ ý là bằng với chiều dài ở đây.


Tôi nghĩ có một cách tốt hơn: stackoverflow.com/a/61953291/1162217
Lukas Lukac

58

Như một phần bổ sung cho bài đăng của jimt:

Bạn cũng có thể sử dụng appendthay vì chỉ định rõ ràng các giá trị cho các chỉ số của chúng:

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

Lưu ý rằng độ dài bằng 0 (chưa có phần tử nào) nhưng dung lượng (không gian được cấp phát) được khởi tạo với số phần tử là m. Điều này được thực hiện nên appendkhông cần cấp phát bộ nhớ mỗi khi hết dung lượng của lát cắt v.

Bạn cũng có thể makecắt lát mà không có giá trị dung lượng và để appendphân bổ bộ nhớ cho chính nó.


Tôi đã tự hỏi nếu điều này sẽ chậm hơn bất kỳ (giả sử phân bổ trước)? Tôi đã làm một điểm chuẩn thô với bản đồ [int] int và nó có vẻ chậm hơn khoảng 1-2%. Bất kỳ ý tưởng nếu đây là một cái gì đó để lo lắng về hoặc chỉ đi với nó?
masebase

1
Tôi giả định rằng append sẽ chậm hơn một chút nhưng sự khác biệt đó, trong hầu hết các trường hợp, là không đáng kể. Điểm chuẩn so sánh phân công trực tiếp và phụ thêm .
nemo

1
Hãy cẩn thận trộn điều này với câu trả lời ở trên - nối vào mảng sau khi bạn sử dụng make([]appsv1.Deployment, len(d))sẽ thêm vào một loạt các phần tử trống đã được tạo khi bạn phân bổ len(d)các mục trống.
Anirudh Ramanathan

1

Theo như tôi hiện tại được biết, go không có phương thức để nối các chuỗi / byte vào một chuỗi kết quả mà không tạo ít nhất / hai / bản sao.

Bạn hiện phải phát triển một byte [] vì tất cả các giá trị chuỗi đều là const, THÌ bạn phải sử dụng nội trang chuỗi để ngôn ngữ tạo đối tượng chuỗi 'may mắn', đối tượng này sẽ sao chép bộ đệm vì thứ gì đó ở đâu đó có thể có tham chiếu tới địa chỉ hỗ trợ byte [].

Nếu một byte [] phù hợp thì bạn có thể đạt được một dẫn đầu rất nhỏ so với byte.Join bằng cách thực hiện một phân bổ và thực hiện các lệnh gọi bản sao của chính bạn.

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

Bạn có thể sử dụng mapsgói này :

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

Sau đó, tất cả những gì bạn phải gọi là

values := maps.GetValuesIntString(m)

Đó là loại an toàn cho mapsự kết hợp phổ biến đó . Bạn có thể có generatecác chức năng an toàn kiểu khác cho bất kỳ kiểu nào khác mapbằng cách sử dụng mappercông cụ trong cùng một gói.

Tiết lộ đầy đủ: Tôi là người tạo ra gói này. Tôi tạo ra nó vì tôi thấy mình đã viết lại mapnhiều lần các hàm này .


1

Không nhất thiết phải tốt hơn, nhưng cách rõ ràng hơn để làm điều này là xác định cả CHIỀU DÀI và CÔNG SUẤT như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)
    }

Ví dụ đầy đủ:

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
}

Giải pháp đơn giản nhưng rất nhiều vấn đề. Đọc bài đăng trên blog này để biết thêm chi tiết: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas


Câu trả lời là không "sai". Câu hỏi sử dụng một chỉ mục và không có phần phụ thêm cho phần. Nếu bạn sử dụng append trên slice thì có, việc thiết lập độ dài lên phía trước sẽ rất tệ. Đây là câu hỏi vì nó là play.golang.org/p/nsIlIl24Irn Đã cho rằng câu hỏi đó không phải là câu thành ngữ và tôi vẫn đang học. Một phiên bản cải tiến hơn một chút giống như những gì bạn đang nói là play.golang.org/p/4SKxC48wg2b
masebase

1
Xin chào @masebase, tôi cho là "sai" vì nó nói: "Rất tiếc, không. Không có cách tích hợp nào để thực hiện việc này.", Nhưng có giải pháp "tốt hơn" mà cả hai chúng tôi đang chỉ ra vào 2 năm sau - sử dụng phần phụ () và xác định cả chiều dài và dung lượng. Nhưng tôi thấy quan điểm của bạn rằng gọi nó là "sai" cũng không chính xác. Tôi sẽ thay đổi câu đầu tiên của mình thành: "Không nhất thiết phải tốt hơn, nhưng cách sạch hơn để làm điều này là". Tôi đã nói chuyện với ~ 10 nhà phát triển và mọi người đều đồng ý rằng append () là một cách dễ dàng hơn để chuyển đổi một bản đồ thành một lát cắt mà không cần sử dụng chỉ mục trợ giúp. Tôi cũng biết được điều này trên cách đăng bài
Lukas Lukac
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.