0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go言語でmap[string]anyを使う

Posted at

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>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?