ゴーで理解地図

最新のプログラミング言語のほとんどには、dictionaryまたはhashタイプの概念があります。 これらのタイプは通常、valueにマップされるkeyとペアでデータを格納するために使用されます。

Goでは、mapデータ型は、ほとんどのプログラマーがdictionary型と考えるものです。 キーに値をマッピングして、Goにデータを保存する便利な方法であるキーと値のペアを作成します。 マップは、キーワードmap、角括弧[ ]内のキーデータ型、値データ型を使用して作成されます。 次に、キーと値のペアは、どちらかの側の波括弧の中に配置されます\ {}:

map[key]value{}

通常、Goでマップを使用して、IDに含まれる情報などの関連データを保持します。 データを含むマップは次のようになります。

map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}

中括弧に加えて、キーと値のペアを接続するコロンがマップ全体にあります。 コロンの左側の単語がキーです。 キーは、stringsintsなど、Goの任意のcomparableタイプにすることができます。

サンプルマップのキーは次のとおりです。

  • "name"

  • "animal"

  • "color"

  • "location"

コロンの右側の単語は値です。 値は任意のデータ型にすることができます。 サンプルマップの値は次のとおりです。

  • "Sammy"

  • "shark"

  • "blue"

  • "ocean"

他のデータ型と同様に、マップを変数内に保存し、印刷することができます。

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
fmt.Println(sammy)

これはあなたの出力になります:

Outputmap[animal:shark color:blue location:ocean name:Sammy]

キーと値のペアの順序がずれている可能性があります。 Goでは、マップのデータ型は順不同です。 順序に関係なく、キーと値のペアはそのまま残り、関係の意味に基づいてデータにアクセスできます。

マップアイテムへのアクセス

関連するキーを参照することにより、マップの値を呼び出すことができます。 マップはデータを格納するためのキーと値のペアを提供するため、Goプログラムで重要かつ有用なアイテムになる可能性があります。

サミーのユーザー名を分離したい場合は、sammy["name"]を呼び出すことで分離できます。マップと関連キーを保持する変数。 それを印刷してみましょう:

fmt.Println(sammy["name"])

出力として値を受け取ります。

OutputSammy

マップはデータベースのように動作します。スライスの場合のように整数を呼び出して特定のインデックス値を取得する代わりに、キーに値を割り当て、そのキーを呼び出して関連する値を取得します。

キー"name"を呼び出すと、そのキーの値である"Sammy"を受け取ります。

同様に、同じ形式を使用して、sammyマップの残りの値を呼び出すことができます。

fmt.Println(sammy["animal"])
// returns shark

fmt.Println(sammy["color"])
// returns blue

fmt.Println(sammy["location"])
// returns ocean

マップデータタイプでキーと値のペアを使用することにより、キーを参照して値を取得できます。

キーと値

一部のプログラミング言語とは異なり、Goには、マップのキーまたは値を一覧表示するためのconvenience関数がありません。 この例は、辞書用のPythonの.keys()メソッドです。 ただし、range演算子を使用して反復を行うことができます。

for key, value := range sammy {
    fmt.Printf("%q is the key for the value %q\n", key, value)
}

Goでマップを移動すると、2つの値が返されます。 最初の値がキーになり、2番目の値が値になります。 Goは、これらの変数を正しいデータ型で作成します。 この場合、マップキーはstringであったため、keyも文字列になります。 valueも文字列です。

Output"animal" is the key for the value "shark"
"color" is the key for the value "blue"
"location" is the key for the value "ocean"
"name" is the key for the value "Sammy"

キーだけのリストを取得するには、範囲演算子を再度使用できます。 キーにアクセスする変数を1つだけ宣言できます。

keys := []string{}

for key := range sammy {
    keys = append(keys, key)
}
fmt.Printf("%q", keys)

プログラムは、キーを格納するスライスを宣言することから始まります。

出力には、マップのキーのみが表示されます。

Output["color" "location" "name" "animal"]

繰り返しますが、キーはソートされていません。 それらを並べ替える場合は、sortパッケージのsort.Strings関数を使用します。

sort.Strings(keys)

この機能を使用すると、次の出力が表示されます。

Output["animal" "color" "location" "name"]

同じパターンを使用して、マップ内の値のみを取得できます。 次の例では、スライスを事前に割り当てて割り当てを回避し、プログラムをより効率的にします。

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}

items := make([]string, len(sammy))

var i int

for _, v := range sammy {
    items[i] = v
    i++
}
fmt.Printf("%q", items)

まず、キーを保存するスライスを宣言します。必要なアイテムの数がわかっているため、スライスをまったく同じサイズで定義することで、潜在的なメモリ割り当てを回避できます。 次に、インデックス変数を宣言します。 キーが必要ないため、ループを開始するときに_演算子を使用して、キーの値を無視します。 出力は次のようになります。

Output["ocean" "Sammy" "shark" "blue"]

マップ内のアイテムの数を決定するには、組み込みのlen関数を使用できます。

sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
fmt.Println(len(sammy))

出力には、マップ内のアイテムの数が表示されます。

Output4

Goにはキーと値を取得するための便利な関数が付属していませんが、必要なときにキーと値を取得するのに数行のコードしか必要ありません。

存在の確認

Goのマップは、要求されたキーが欠落している場合、マップの値タイプのゼロ値を返します。 このため、保存されたゼロと欠落キーを区別する代替方法が必要です。

存在しないことがわかっているマップで値を検索し、返される値を見てみましょう。

counts := map[string]int{}
fmt.Println(counts["sammy"])

次の出力が表示されます。

Output0

キーsammyがマップになかったとしても、Goは0の値を返しました。 これは、値のデータ型がintであり、Goはすべての変数に対してゼロ値を持っているため、0のゼロ値を返すためです。

多くの場合、これは望ましくなく、プログラムのバグにつながります。 マップで値を検索すると、Goは2番目のoptional値を返すことができます。 この2番目の値はboolであり、キーが見つかった場合はtrueになり、キーが見つからなかった場合はfalseになります。 Goでは、これはokイディオムと呼ばれます。 2番目の引数をキャプチャする変数に任意の名前を付けることができますが、Goでは、常にokという名前を付けます。

count, ok := counts["sammy"]

キーsammycountsマップに存在する場合、oktrueになります。 それ以外の場合、okはfalseになります。

ok変数を使用して、プログラムで何をするかを決定できます。

if ok {
    fmt.Printf("Sammy has a count of %d\n", count)
} else {
    fmt.Println("Sammy was not found")
}

これにより、次の出力が得られます。

OutputSammy was not found

Goでは、変数宣言と条件チェックをif / elseブロックと組み合わせることができます。 これにより、このチェックに単一のステートメントを使用できます。

if count, ok := counts["sammy"]; ok {
    fmt.Printf("Sammy has a count of %d\n", count)
} else {
    fmt.Println("Sammy was not found")
}

Goでマップから値を取得するときは、プログラムのバグを回避するために、その存在も確認することを常にお勧めします。

マップの変更

マップは変更可能なデータ構造であるため、変更できます。 このセクションのマップアイテムの追加と削除を見てみましょう。

マップアイテムの追加と変更

メソッドまたは関数を使用せずに、キーと値のペアをマップに追加できます。 これを行うには、maps変数名に続けて角括弧[ ]で囲まれたキー値を使用し、等しい=演算子を使用して新しい値を設定します。

map[key] = value

実際には、usernamesという名前のマップにキーと値のペアを追加することでこの作業を確認できます。

usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}

usernames["Drew"] = "squidly"
fmt.Println(usernames)

出力には、マップ内の新しいDrew:squidlyのキーと値のペアが表示されます。

Outputmap[Drew:squidly Jamie:mantisshrimp54 Sammy:sammy-shark]

マップは順序付けられずに返されるため、このペアはマップ出力のどこでも発生する可能性があります。 後でプログラムファイルでusernamesマップを使用すると、追加のキーと値のペアが含まれます。

この構文を使用して、キーに割り当てられた値を変更することもできます。 この場合、既存のキーを参照し、別の値を渡します。

特定のネットワーク上のユーザーのフォロワーを追跡するfollowersというマップについて考えてみます。 今日、ユーザー"drew"のフォロワーが急増したため、"drew"キーに渡された整数値を更新する必要があります。 Println()関数を使用して、マップが変更されたことを確認します。

followers := map[string]int{"drew": 305, "mary": 428, "cindy": 918}
followers["drew"] = 342
fmt.Println(followers)

出力には、drewの更新された値が表示されます。

Outputmap[cindy:918 drew:342 mary:428]

フォロワーの数が305の整数値から342に急増したことがわかります。

このメソッドを使用して、キーと値のペアをユーザー入力付きのマップに追加できます。 コマンドラインで実行され、ユーザーからの入力で名前と関連するユーザー名を追加できるusernames.goという簡単なプログラムを作成しましょう。

usernames.go

package main

import (
    "fmt"
    "strings"
)

func main() {
    usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}

    for {
        fmt.Println("Enter a name:")

        var name string
        _, err := fmt.Scanln(&name)

        if err != nil {
            panic(err)
        }

        name = strings.TrimSpace(name)

        if u, ok := usernames[name]; ok {
            fmt.Printf("%q is the username of %q\n", u, name)
            continue
        }

        fmt.Printf("I don't have %v's username, what is it?\n", name)

        var username string
        _, err = fmt.Scanln(&username)

        if err != nil {
            panic(err)
        }

        username = strings.TrimSpace(username)

        usernames[name] = username

        fmt.Println("Data updated.")
    }
}

usernames.goで、最初に元のマップを定義します。 次に、名前を反復処理するループを設定します。 ユーザーに名前を入力し、保存する変数を宣言するよう要求します。 次に、エラーが発生したかどうかを確認します。その場合、プログラムはpanicで終了します。 Scanlnはキャリッジリターンを含む入力全体をキャプチャするため、入力からスペースを削除する必要があります。これは、strings.TrimSpace関数を使用して行います。

ifブロックは、名前がマップに存在するかどうかを確認し、フィードバックを出力します。 名前が存在する場合、ループの先頭に戻ります。 名前がマップにない場合、ユーザーにフィードバックを提供し、関連する名前の新しいユーザー名を要求します。 プログラムは再度エラーがあるかどうかを確認します。 エラーなしで、キャリッジリターンが削除され、ユーザー名の値が名前キーに割り当てられ、データが更新されたというフィードバックが出力されます。

コマンドラインでプログラムを実行しましょう:

go run usernames.go

次の出力が表示されます。

OutputEnter a name:
Sammy
"sammy-shark" is the username of "Sammy"
Enter a name:
Jesse
I don't have Jesse's username, what is it?
JOctopus
Data updated.
Enter a name:

テストが終了したら、CTRL + Cを押してプログラムをエスケープします。

これは、マップをインタラクティブに変更する方法を示しています。 この特定のプログラムでは、CTRL + Cを指定してプログラムを終了するとすぐに、ファイルの読み取りと書き込みを処理する方法を実装しない限り、すべてのデータが失われます。

要約すると、map[key] = value構文を使用して、マップにアイテムを追加したり、値を変更したりできます。

マップアイテムの削除

マップデータタイプ内でキーと値のペアを追加して値を変更できるように、マップ内のアイテムを削除することもできます。

マップからキーと値のペアを削除するには、組み込み関数delete()を使用できます。 最初の引数は、削除元のマップです。 2番目の引数は、削除するキーです。

delete(map, key)

許可のマップを定義しましょう:

permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16:"modify"}

modify権限は不要になったため、マップから削除します。 次に、地図を印刷して、削除されたことを確認します。

permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16: "modify"}
delete(permissions, 16)
fmt.Println(permissions)

出力は削除を確認します:

Outputmap[1:read 2:write 4:delete 8:create]

delete(permissions, 16)は、キーと値のペア16:"modify"permissionsマップから削除します。

すべての値のマップをクリアする場合は、同じタイプの空のマップと同じ値に設定することにより、マップをクリアできます。 これにより、使用する新しい空のマップが作成され、古いマップはガベージコレクターによってメモリからクリアされます。

permissionsマップ内のすべてのアイテムを削除しましょう。

permissions = map[int]string{}
fmt.Println(permissions)

出力は、キーと値のペアがない空のマップがあることを示しています。

Outputmap[]

マップは変更可能なデータ型であるため、追加、変更、およびアイテムの削除とクリアが可能です。

結論

このチュートリアルでは、Goのマップデータ構造について説明しました。 マップはキーと値のペアで構成され、インデックス作成に依存せずにデータを保存する方法を提供します。 これにより、その意味と他のデータ型との関係に基づいて値を取得できます。