mapとは?
Go言語のmapは、pythonの辞書(dict)に相当します。
python の辞書
# python dict
my_dict = {key1: value1, key2: value2, key3: value3}
Go言語の map
//var my_dict map[string]any でも良いが、makeの方が早い
my_dict := make(map[string]any)
my_dict["key1"] = "value1"
my_dict["key2"] = "value2"
my_dict["key3"] = "value3"
[注意点 1] Go言語のスライスは、順番が固定されているが、mapは順番が固定されていない。
my_slice := []string{"a","b","c"}
for i := range my_slice {
fmt.Printf("slice[%v] %v \n", i, my_slice[i])
}
for k,v := range my_dict {
// k が違うタイミングで発生する事があります
fmt.Printf("dict[%v] %v \n", k, v)
}
anyとは
Go言語の「型なし変数」となります。
スクリプト系の javaScript, python, VisualBasic for applicationのVariant型などで「型なし変数」が使われます。
Go言語の ver 1.18からデータ型の表記が "interface{}"から"any"に変更されました。
コンパイラーのエラーなどは、まだ表記が古いままなので、覚えておくと良いでしょう。
Go言語の古いバージョンと共存する場合は、type定義しておけば良いでしょう。
type any = interface{}
any の場合、初期値は、nilとなります。
map[string]anyとは
今回は、汎用性が高い [string]型のキーですが、Go言語のmapのキーは、map[int]any. map[float64]anyなど、いろいな型がつかえます。
my_dict := make(map[string]any)
my_dict["key1"] = "value1"
my_dict["key2"] = int64(1)
my_dict["key3"] = float64(3.141592)
my_dict["key4"] = true
my_dict["key5"] = time.Now()
for k,v := range my_dict {
fmt.Printf("dict[%v] %v \n", k, v)
// switch文で、変数に入れると自動で型変換されます。
switch x := v.(type) {
case string:
fmt.Printf("str: %v \n", x)
case int,int8,int16,int32,int64:
fmt.Printf("int: %v \n", x + x)
case float32,float64:
fmr.Printf("float: %v \n", x * x)
case bool:
fmt.Printf("bool: %v \n", x)
case time.Time:
fmt.Printf("time: %v \n", x.Format("2006/01/02"))
}
}
[注意点 2] if文の比較は、型宣言する事
// エラーにも なりません、素通りします。
// たぶんメモリのポインターを参照してます。
if my_dict["key1"] == "value1" {
fmt.Printf("key1: %v \n", my_dict["key1"])
}
// ポインターが入るので、nilが使えます
if my_dict["key1"] != nil {
fmt.Printf("key1: %v \n", my_dict["key1"])
}
// if文が動きます。キーが無いとパニック エラーになります。
if my_dict["key1"].(string) == "value1" {
fmt.Printf("key1: %v \n", my_dict["key1"])
}
// キーがあるのか判定
if _,ok := my_dict["key1"]; ok {
if my_dict["key1"].(string) == "value1" {
fmt.Printf("key1: %v \n", my_dict["key1"])
}
}
SQLxで使う
import "database/sql"では、構造体でテータベースの項目を定義する必要がありますが、SQLxならば必要ありません。ORM不要!!
データベース管理ソフトで、テーブルの項目を変更しても、プログラム変更不要!!
import "github.com/jmoiron/sqlx"の場合、NamedExec(),NamedQuery()で使えます。
db ,err := sqlx.Open("sqlite3","./test.db3")
if err != nil {
fmt.Printf("(Error) %v \n",err)
return
}
defer db.Close()
sql := "SELECT * FROM TTest WHERE stamp BETWEEN :first AND :last;"
sqlcmd := make(map[string]any)
sqlcmd["first"] = "2025-01-01"
sqlcmd["last"] = "2025-12-31"
rows, err := db.NamedQuery(sql, sqlcmd)
recs := make([]map[string]any)
for rows.Next() {
rec := make(map[string]any)
err = rows.MapScan(rec)
if err != nil {
fmt.Printf("(Error) %v \n", err)
break
}
recs = append(recs, rec)
}
for i := range recs {
rec := recs[i]
for k,v := rec {
fmt.Printf("[%v] %v ,", k, v)
}
fmt.Println()
}
mapのキーに項目名が自動的に入ります、データ型も元のテーブルの項目型に設定されます。
NULL項目にも対応してます!!
[注意点 3]
postgreSQLは、日付型があるが、SQLite3など日付型が無いデータベースもあります。
日付は、ISO形式( 2006-01-02 15:04:05 )の方が文字でも並び替えが確実です。
タイムスタンプではあれば、更に下の桁(ミリ秒,マイクロ秒)や国情報(GMT-07:00)も付けた方が良い場合もあります。
[注意点 4]
ORACLEの文字型が、VARCHARの場合、[]byteで入るので、string([]byte)で変換されば、日本語入り文字列も普通に使えます。(shift-jisだと更に複雑になる)
JSONとの組み合わせ
json.Marshal() / json.Unmarshal()で、相互変換する
[注意点 5] jsonは、文字型, 浮動小数点型, ブール, nullと扱えるデータ型が限られる。
Go言語の場合は、構造体の宣言が必要です。
項目名の先頭が大文字/小文字で、かなり扱いが違います。
jsonの指定方法は、最近のバージョンで、いろいろなオプションがあるようです。
type Element struct {
Name string `json:"name"`
Value float64 `json:"value"`
Childs []Element `json:"childs"`
}
Go言語の構造体は、入れ子が可能です。
HTTPとの組み合わせ
HTML formタグの POST をサーバーで受け取ります。
ParseForm()で、r.Form が mapに変換されます。
SQLxで、データベースを事前に開いておくか? Go言語の待ち行列に送って、別で登録するなどが可能です。
func hand1(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
fmt.Printf("(Error) %v", err)
}
for k, v := range r.Form {
fmt.Printf("[%v] %v ,", k, v)
}
fmt.Println()
// 事前にdb.Open()している場合
sql := "INSERT INTO TTest (name,value) VALUES(:name,:value);"
_, err = db.NamedExec(sql, r.Form)
if err != nil {
fmt.Printf("(Error) %v", err)
}
}
<form method="post">
<input type="text" name="name" value="yamada">
<input type="number" name="value" value="3.141592">
<input type="submit" value="送信">
</form>