golang에서 맵에서 값 조각을 얻는 좋은 방법이 있습니까?


89

내가 맵을 가지고 있다면 m 값의 조각을 얻는 더 좋은 방법이 있습니까?

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 루프에서 '_'대신 idx라고 부르고 idx ++ 비즈니스
Peter Agnew

아니, 그는 할 수 없습니다, 당신이지도에 범위를 지정하면 인덱스가 아닌 키, 값, 값을 반환합니다. 그의 예에서 그는 1을 첫 번째 키로 사용하며 시작 인덱스가 0이 아닌 1이되고 4에 도달하면 범위를 벗어나므로 슬라이스 v의 인덱스가 올바르지 않게됩니다. play.golang.org/p/X8_SbgxK4VX
Popmedic

@Popmedic 사실, 그래 그는 할 수 있습니다. 다만 교체 _idx사용할 idx-1슬라이스 값을 할당 할 때.
hewiefreeman

3
@ newplayer66, 그것은 매우 위험한 패턴입니다.
Popmedic 19-02-12

답변:


60

불행하게도. 이를 수행하는 기본 제공 방법은 없습니다.

참고로, 슬라이스 생성에서 용량 인수를 생략 할 수 있습니다.

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

용량은 여기의 길이와 동일 함을 의미합니다.


더 나은 방법이 있다고 생각합니다 : 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)
}

길이는 0 (아직 요소가 없음)이지만 용량 (할당 된 공간)은의 요소 수로 초기화됩니다 m. 이렇게 append하면 슬라이스 용량이 부족할 때마다 메모리를 할당 할 필요가 없습니다 v.

make용량 값이없는 슬라이스 도 append자체 메모리를 할당 할 수 있습니다.


이것이 더 느린 지 궁금합니다 (전면 할당 가정)? 나는 map [int] int로 대략적인 벤치 마크를 수행했고 그것은 약 1-2 % 느려진 것 같았습니다. 이것이 걱정할만한 것이거나 그냥 함께 갈 아이디어가 있습니까?
masebase

1
append가 약간 느리다고 가정하지만 그 차이는 대부분의 경우 무시할 수 있습니다. 직접 할당과 추가를 비교하는 벤치 마크 .
nemo

1
이것을 위의 대답과 조심스럽게 혼합하십시오. 사용 후 배열에 make([]appsv1.Deployment, len(d))추가하면 len(d)빈 항목 을 할당 할 때 생성 된 빈 요소가 추가됩니다 .
Anirudh Ramanathan

1

내가 현재 아는 한, go에는 최소한 / two / 복사본을 만들지 않고 문자열 / 바이트를 결과 문자열에 연결하는 방법이 없습니다.

모든 문자열 값이 const이므로 현재 [] 바이트를 늘려야합니다. 그런 다음 언어가 '축복 된'문자열 객체를 생성하도록 문자열 내장을 사용해야합니다. 어딘가에 참조가있을 수 있으므로 버퍼를 복사합니다. [] 바이트를 뒷받침하는 주소로.

[] 바이트가 적합하면 하나의 할당을 만들고 복사를 수행하여 자신을 호출함으로써 bytes.Join 함수에 대해 약간의 리드를 얻을 수 있습니다.

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조합에 대해 형식이 안전합니다 . 동일한 패키지에서 도구 generatemap사용하는 다른 유형에 대해 다른 유형 안전 함수를 사용할 수 있습니다 mapper.

전체 공개 : 나는이 패키지의 작성자입니다. 이 기능을 map반복적으로 다시 작성했기 때문에 만들었습니다 .


1

반드시 더 좋은 것은 아니지만이를 수행하는 더 깨끗한 방법은 슬라이스 길이와 용량을 다음 과 같이 정의하는 것입니다.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
}

간단한 솔루션이지만 많은 문제가 있습니다. 자세한 내용은이 블로그 게시물을 참조하세요. 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 명의 개발자들과 이야기했고, 모든 사람들은 append ()가 도우미 인덱스를 사용하지 않고 맵을 슬라이스로 변환하는 더 깨끗한 방법이라는 데 동의했습니다. 나는 게시하는 도중에도 이것을 배웠다
Lukas Lukac
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.