PR

Go言語(Golang)の「maps」パッケージの使い方|サンプルコードで解説

2. 基礎

こんにちは。Tomoyuki(@tomoyuki65)です。

Go言語(Golang)には、キーと値をセットで管理するデータ構造として、map(マップ)があります。

そんなmapに対してコピーや比較など、様々な操作を簡単に行うための標準ライブラリとして、「maps」パッケージがあります。

この記事では、そんな「maps」パッケージの使い方についてご紹介します。

 

Go言語(Golang)の「maps」パッケージの使い方|サンプルコードで解説

「maps」パッケージには様々な関数がありますが、まずはよく使われる可能性が高い以下の4つを覚えましょう。

 

Clone:mapを浅くコピーする

mapを浅くコピー(値がスライスやポインタなら参照先のデータが共有される)したい場合は、Clone関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    // 商品と価格のマップ
    productPrices := map[string]int{
        "リンゴ": 100,
        "バナナ": 150,
        "みかん": 120,
    }

    // 浅いコピー
    copied := maps.Clone(productPrices)

    // コピーしたマップのリンゴの価格を変更
    copied["リンゴ"] = 300

    // リンゴの価格をログ出力
    fmt.Println(productPrices["リンゴ"])
    fmt.Println(copied["リンゴ"])

    // マップ全体をログ出力
    fmt.Println(productPrices)
    fmt.Println(copied)
}

※深いコピー(全ての要素を元のデータとは独立させるようにコピー)をしたい場合は、Clone後にスライスやポインタなどが参照するデータもコピーする処理を別途書く必要があります。

 

実行結果

100
300
map[みかん:120 バナナ:150 リンゴ:100]
map[みかん:120 バナナ:150 リンゴ:300]

 

Equal:2つのmapのキーと値の組み合わせが等しいか比較する

2つのmapのキーと値の組み合わせが等しいか比較したい場合は、Equal関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    mapA := map[string]int{
        "リンゴ": 100,
        "バナナ": 200,
    }

    mapB := map[string]int{
       "バナナ": 200,
       "リンゴ": 100,
    }

    mapC := map[string]int{
        "バナナ": 200,
        "リンゴ": 500,
    }

    // キーと値が一致するかを比較する
    fmt.Println(maps.Equal(mapA, mapB))
    fmt.Println(maps.Equal(mapB, mapC))
    fmt.Println(maps.Equal(mapC, mapA))
}

※mapの要素の並び順は関係なく、キーと値の組み合わせが一致すればtrueになる。

 

実行結果

true
false
false

 

Copy:mapの要素を別のmapに移す(追加・上書き)

あるmapの要素を別のmapに移すように追加や上書きをしたい場合は、Copy関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    mapA := map[string]int{
        "リンゴ": 100,
        "バナナ": 150,
    }

    mapB := map[string]int{
        "リンゴ": 50,
        "みかん": 120,
    }

    // mapBに対して、mapAの要素を移す(追加・上書き)
    maps.Copy(mapB, mapA)

    // ログ出力
    fmt.Println(mapA)
    fmt.Println(mapB)
}

※キーが存在したら上書き、キーが存在しなければ追加されます。

 

実行結果

map[バナナ:150 リンゴ:100]
map[みかん:120 バナナ:150 リンゴ:100]

 

DeleteFunc:指定した条件に一致する要素を削除する

mapに対して、指定した条件に一致する要素を削除したい場合は、DeleteFunc関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    productPrices := map[string]int{
        "リンゴ": 100,
        "バナナ": 150,
        "みかん": 120,
        "桃": 300,
        "スイカ": 500,
        "メロン": 1000,
    }

    // 値が500以上の要素を削除する
    maps.DeleteFunc(productPrices, func(key string, value int) bool {
        return value >= 500
    })

    // ログ出力
    fmt.Println(productPrices)
}

※パラメータに渡した条件関数がtrueを返す要素を削除する。

 

実行結果

map[みかん:120 バナナ:150 リンゴ:100 桃:300]

 

スポンサーリンク

map(マップ)のイテレータを取得して使う方法も合わせて覚える

次にmap(マップ)のイテレータ(データを一つずつ取り出して処理できる仕組み)を取得して使う方法についても合わせて覚えておきましょう。

※Go1.23からイテレータをrangeでループ処理できるようになってます。

 

Keys:mapのキーをイテレータで取得する

mapのキーをイテレータで取得したい場合は、Keys関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    productPrices := map[string]int{
        "リンゴ": 100,
        "バナナ": 150,
        "みかん": 120,
    }

    // キーを取得
    for key := range maps.Keys(productPrices) {
        fmt.Println(key)
    }
}

※イテレータからはキーを順番に取得しますが、順番は保証されません。

 

実行結果

リンゴ
バナナ
みかん

 

Values:mapの値をイテレータで取得する

mapの値をイテレータで取得したい場合は、Values関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    productPrices := map[string]int{
        "リンゴ": 100,
        "バナナ": 150,
        "みかん": 120,
    }

    // 値を取得
    for value := range maps.Values(productPrices) {
        fmt.Println(value)
    }
}

※イテレータからは値を順番に取得しますが、順番は保証されません。

 

実行結果

100
150
120

 

All:mapのキーと値の組をイテレータで取得する

mapのキーと値の組をイテレータで取得したい場合は、All関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    productPrices := map[string]int{
        "リンゴ": 100,
        "バナナ": 150,
        "みかん": 120,
    }

    // キーと値の組みを取得
    for key, value := range maps.All(productPrices) {
        fmt.Println(key, value)
    }
}

※mapを直接ループ処理する従来の「for key, value := range productPrices {} 」と同じ結果になりますが、All関数を利用するとイテレータを利用してループ処理させている点が異なります。そのため、イテレータを他の関数に渡すような処理をしたい場合は、All関数を利用する可能性があります。

 

実行結果

みかん 120
リンゴ 100
バナナ 150

 

スポンサーリンク

必要に応じて応用的な操作を覚える

その他、必要に応じて応用的な操作に関する関数についても覚えたい場合は以下の通りです。

 

EqualFunc:独自の比較関数を使って2つのmapを比較する

独自の比較関数を使って2つのmapを比較したい場合は、EqualFunc関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    mapA := map[string]int{
        "リンゴ": 100,
        "バナナ": 200,
    }

    mapB := map[string]int{
        "リンゴ": 200,
        "バナナ": 299,
    }

    // mapAとmapBの同一キーの値の合計が500未満かをチェック
    result := maps.EqualFunc(mapA, mapB, func(a, b int) bool {
        return a+b < 500
    })

    // 結果をログ出力
    fmt.Println(result)
}

※比較するmapはキーの種類や型が一致している必要があります。

 

実行結果

true

 

Collect:キーと値の組を持つイテレータからmapを作成する

キーと値の組を持つイテレータからmapを作成したい場合は、Collect関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    // イテレータを手動で作成
    iter := func(yield func(string, int) bool) {
        // breakなどでループ処理が終了した場合にイテレータも終了するための条件
        if !yield("リンゴ", 100) {
            return
        }

        if !yield("バナナ", 200) {
            return
        }

        // 最後に渡す値
        yield("みかん", 150)
    }

    // イテレータからmapを作成
    productPrices := maps.Collect(iter)

    // mapをログ出力
    fmt.Println(productPrices)
}

※yieldは、イテレータから呼び出し元に値を渡すための関数です。

 

実行結果

map[みかん:150 バナナ:200 リンゴ:100]

 

Insert:イテレータのキーと値の組み合わせを別のmapに移す(追加・上書き)

あるイテレータのキーと値の組み合わせを別のmapに移すように追加や上書きをしたい場合は、Insert関数を利用すると可能です。

package main

import (
    "fmt"
    "maps"
)

func main() {
    // 商品と価格のマップ
    productPrices := map[string]int{
        "リンゴ": 50,
        "スイカ": 500,
    }

    // イテレータを手動で作成
    iter := func(yield func(string, int) bool) {
        // breakなどでループ処理が終了した場合にイテレータも終了するための条件
        if !yield("リンゴ", 100) {
            return
        }

        if !yield("バナナ", 200) {
            return
        }

        // 最後に渡す値
        yield("みかん", 150)
    }

    // productPricesに対して、iterの要素を移す(追加・上書き)
    maps.Insert(productPrices, iter)

    // mapをログ出力
    fmt.Println(productPrices)
}

※mapから移すならCopy関数、イテレータから移すならInsert関数を使います。

 

実行結果

map[みかん:150 スイカ:500 バナナ:200 リンゴ:100]

 

スポンサーリンク

最後に

今回は「maps」パッケージの使い方についてご紹介しました。

全てを一気に覚える必要はないため、まずはよく使われる可能性がある4種類や、イテレータ(データを一つずつ取り出して処理できる仕組み)に関する3種類を覚えましょう。

これからmapsパッケージの使い方を学ぶ方は、ぜひ参考にしてみて下さい。

 

SubstackでITエンジニア向けニュースレター(メルマガ)を始めました!
こんにちは。Tomoyuki(@tomoyuki65)です。最近はどのSNSもアルゴリズム(どの投稿を誰に表示するかを決める仕組み)中心のタイムライン(投稿が流れてくる画面)になっていて、フォローしている人の投稿が見つけにくく、逆にフォロー...

 

この記事を書いた人
Tomoyuki

SE→ブロガーを経て、現在はSoftware Engineer(Web/Gopher)をしています!

Tomoyukiをフォローする
2. 基礎
スポンサーリンク
Tomoyukiをフォローする

コメント

タイトルとURLをコピーしました