Go言語：私的備忘録

Golangにて遭遇したTipsを私的にメモする場所。
ちょいちょい加筆していきます。

### 構造体のタグを参照する

Json生成時に利用する構造体のタグ。
このタグを自由に使うための方法です（CSV等のカラム名等に便利）。

```go
import (
  "reflect"
)

// タグ付きで構造体を定義
type Record struct {
  Name  string `column:"name"`
  Age   int    `column:"age"`
}

var csv Record = Record{
  Name: "Hoge",
  Age: 20,
}

// タグを1つずつ取得
t := reflect.TypeOf(csv)
fmt.Println(t.Field(0).Tag.Get("column")) //=> name
fmt.Println(t.Field(1).Tag.Get("column")) //=> age

// ループで参照
for i := 0; i < t.NumField(); i++ {
  fmt.Println(t.Field(i).Tag.Get("column")) //=> name -> age
}

// 元のフィールド名はこちら
for i := 0; i < t.NumField(); i++ {
  fmt.Println(t.Field(i).Name) //=> NAME -> Age
}
```

### 構造体のフィールド名に変数の値を用いて代入する

構造体のフィールド名に対し、変数の値をフィールド名として用い代入する方法。

```go
import (
  "reflect"
)

type Record struct {
  Name string
}

var record Record

// フィールド名
var fieldName string = "Name"

// ポインタへの変換必須
p := reflect.ValueOf(&record).Elem()

// reflectでフィールド名を指定して代入
// 但しSetStringは型が異なるとpanicを起こすので注意（よしなに処理するSetやSetInt等もある）
p.FieldByName(fieldName).SetString("hoge")
```

### Mapのキーによるソートは一旦キーのみ配列化してソートしたほうが手軽

▼ Go: sort a Map by key #golang
https://gist.github.com/flaviocopes/bed52cc2ac407137b3931bc864b4e31b

なおsort.Slice()関数を使えばsortインタフェースを定義しなくてもソート可能。
こちらの記事が大変参考になりました。

▼ 今回はソート（sort）のお話。
http://text.baldanders.info/golang/sort/

### JSONをパースして手っ取り早く値を参照する

例えば以下のJSONの場合。

```json
{
  "form1":{
    "subject": "hoge"
    "mailto": [
      "hoge_1@example.com",
      "hoge_2@example.com"
    ]
  },
  "form2":{
    "subject": "fuga"
    "mailto": [
      "fuga_1@example.com",
      "fuga_2@example.com"
    ]
  },
}
```

普通は専用の構造体を用意して代入する。
でもいちいち構造体を用意するのは面倒だし、手っ取り早く値を参照したい場合がある。
そのような場合は一旦interface{}型でまるっと読み込んでしまうと便利。

```go
import (
  "encoding/json"
  "io/ioutil"
)

bytes, err := ioutil.ReadFile(path_to_json)
var jsonData interface{}
err := json.Unmarshal(bytes, &jsonData)
```

これで全データがjsonDataにinterface{}型で格納されるが、
この方法で読み込むと全て`map[string]interface{}`型の階層構造になるのが難点。
そのままでは参照できないので、例えば`form1`の`subject`を参照するのであれば、

```go
jsonData.(map[string]interface{})["f1"].(map[string]interface{})["subject"]
```

のようにinterface{}型への型キャストを繰り返せば可能となる。
長くなるので良いとは言えないが、さくっと参照したい場合に便利です。

### returnで構造体のリファレンスを返してメソッドチェーン

dbrでのサンプルを例に。

```go
dbh.UpdateBySql("UPDATE〜").Exec()
```

* dbhはdbr内のあらゆるメソッドにアクセス可能な状態
* dbhを通じてupdate_builder.goに定義されているUpdateBySql()を実行
* update_builder.goにはUpdateBuilder型が定義されている
* UpdateBuilder型を用いたオブジェクトがreturn &UpdateBuilder{}で返る
* UpdateBuilder型には構造体メソッドExec()も定義されている
* つまりupdate_builder.go内のExec()が実行される

大雑多な解釈だが、

* UpdateBySql()の実行により「Update処理である」事を確定し
* Update処理に必要なデータをUpdateBuilder型に定義した上で
* update_builder.go内のExec()を実行させる事ができる

という事。
なるほどメソッドチェーンという感じでした。

### インターフェース型を引数に渡す、返り値で受ける

##### ▼ 引数に渡す
sortパッケージではLen()/Less()/Swap()の3メソッドを満たす型（構造体メソッドとして持つ型）を引数に渡すことで、内部でこのメソッドを呼んで結果を返してくれる。なので3つのメソッドを目的に応じて定義すれば、様々なsortパターンを実現することが可能になっている。

* sortパッケージ内にインターフェース型が定義されており
* インターフェースを満たす型を用意し（3つのメソッドさえ持っていれば独自に定義した型で良い）
* それをsortに渡す事で独自メソッドで処理された結果を得ることができる

##### ▼ 返り値で受ける
dbrパッケージではSQLをExec()した場合、LastInsertId()/RowsAffected()メソッドを持つsql. Resultインターフェース型で結果が帰ってくる。なので返り値を受け取る変数をresultとした場合、result.RowsAffected()で更新された行数を得ることが可能になっている。

* dbrパッケージ内でsql.Resultインターフェース型を満たす型（2つのメソッドを持つ型）が用意され
* それをExec()メソッドの返り値として渡されて
* 実行元で実行可能にしている

つまりこれは、sortパッケージは内部処理を呼び出し元に委譲することで利用の幅（バリエーション）をもたせ、dbrパッケージでは実行されたメソッド（Select/Update/Delete...）別に結果をインターフェースのメソッドとして定義し、返すことで、一つの共通型でそれぞれの結果を得られる仕組みを提供している。

* sortは一つのメソッドの結果をユーザーが自由にカスタマイズ可能にするため
* dbrは異なるメソッドの結果を共通のメソッドで得るため

インターフェースも使い方で色々とできるんだな、と思ったのでメモ。
 
### r.URLをstringとして処理したい

r.URLは`type *url.URL型`なので、そのままではstring型としての処理ができない。
その場合String()で参照できる。

```go
strings.Replace(r.URL.String(), "www", "", 1)
```

### 実行時間の計測

```go
start := time.Now()
end := time.Now()
result := end.Sub(start).Seconds()
```

### 空のmapを代入可能な状態で生成する

```go
var var1 map[string]string = map[string]string{}
var1["hoge"] = "fuga" // OK

var var2 map[string]string
var2["hoge"] = "fuga" // NG（からデータがない状態でのこの方法はNG）
var2 = map[string]string{"hoge": "huga"} // OK
```

関数内では型推論による定義を行う。

```go
hoge := map[string]string{}
hoge["fuga"] = "hogefuga" // OK
```

### URL関連や環境変数の操作メモ

```go
fmt.Println(os.Environ()) // 環境変数
fmt.Println(r.URL.Host) // hoge.fuga.com
fmt.Println(r.URL.Path) // /index.html

os.Setenv("HOGE", r.URL.Host) // 環境変数を設定
fmt.Println(os.Getenv("HOGE")) // 環境変数を参照
```

### map[string]interface{}で定義された値の参照

例えば以下の定義から`id`を参照しようとするとエラーが発生する。

```go
hoge := map[string]interface{}{
	"id": 1,
}
fmt.Println(hoge["id"]) // type interface {} does not support indexing
```

以下のエラーが発生する。

```go
type interface {} does not support indexing
```

### slice同士の連結

連結する側のslice末尾に「...」を忘れないこと。
Split等の関数実行時も「...」を付ければ連結できる。

```go
result = append(result, source...)
result = append(result, strings.Split("1,2,3", ",")...)
```

### strconv の Atoi と Itoa

返り値の個数が異るため無駄にはまる。
Atoiはerrorも受け取る必要があるので注意！

```go
result, err := strconv.Atoi(target)
result      := strconv.Itoa(target)
```

## 1文字単位に変換

string型は内部はsliceなのでruneで1=1文字単位で参照できる。
例えば先頭から10文字を取得する場合はこんな感じ。

```go
text := string([]rune(text)[:10])
```

## timeパッケージによる日時の取扱について

日時処理を行うtimeパッケージですが、独自の仕様満載で慣れるまで戸惑います。
とはいえ便利な機能も多々ありますので、備忘録としてまとめておきます。
ほんと色々な関数が用意されているので、一度ドキュメントに目を通すのがオススメです。
https://golang.org/pkg/time/

なおこちらの記事をとても参考にさせていただきました。
- Goで時刻を取り扱う
http://qiita.com/taizo/items/acbee530bd33c803dab4
ありがとうございます。

```go
// 現在時刻から色々と参照（type Timeに色々と定義されている）
now := time.Now()
fmt.Println(now.Unix())                  // 1486544245
fmt.Println(now.Month())                 // 2
fmt.Println(reflect.TypeOf(now.Month())) // time.Month型（time.Dateに使える！）

// 任意の時刻を定義する
location, _ := time.LoadLocation("Asia/Tokyo")
var month time.Month = 2                                                                                                     // 定数の代わりに任意の数値で指定する場合はtime.Month型で定義
current := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond(), location) // 現在日時をセット
future := time.Date(2017, month, 9, 17, 0, 0, 0, location)                                                                   // 未来の日時をセット
past := time.Date(2017, month, 7, 17, 0, 0, 0, location)                                                                     // 過去の日時をセット
fmt.Println(current)                                                                                                         // 現在日時（2017-02-08 17:59:11.942314121 +0900 JST）

// 自由なフォーマットに整形して出力
fmt.Println(now.Format("2006年01月02日 15時04分05秒"))     // 2017年02月08日 21時20分21秒
fmt.Println(current.Format("2006年01月02日 15時04分05秒")) // 2017年02月08日 21時20分21秒
fmt.Println(future.Format("2006年01月02日 15時04分05秒"))  // 2017年02月09日 17時00分00秒
fmt.Println(past.Format("2006年01月02日 15時04分05秒"))    // 2017年02月07日 17時00分00秒

// 2つの時刻を比較する
fmt.Println(time.Since(past))                 // pastから現在時刻までの経過時間（25h4m16.724052151s）
fmt.Println(reflect.TypeOf(time.Since(past))) // time.Duration型（const定義と同じ型！）
fmt.Println(future.After(past))               // futureはpastよりも未来か（true）
fmt.Println(past.After(future))               // pastはfutureよりも未来か（false）

// 定数に基準となる時刻単位がセットされている
fmt.Println(time.Hour)                 // 1h0m0s
fmt.Println(time.Minute)               // 1m0s
fmt.Println(time.Second)               // 1s
fmt.Println(reflect.TypeOf(time.Hour)) // time.Duration型（time.Sinceで取得する型と同じ！）

// 現在時刻に指定期間を加味した時刻を得る
fmt.Println(now.Add(time.Hour))          // 2017-02-08 19:24:39.432408145 +0900 JST（一時間後）
fmt.Println(now.Add(-time.Hour))         // 2017-02-08 19:24:39.432408145 +0900 JST（一時間前）
fmt.Println(now.Add((time.Since(past)))) // 2017-02-09 19:52:25.842717409 +0900 JST（time.Sinceで取得した時間経過分）

// 入力フォーマットを定義し「2014-12-31 00:00:00 +0000 UTC」（ISO-8601形式）で出力させる
var timeformat = "2006-01-02T15:04:05 MST"                        // 入力される日時のフォーマット定義（各値は固定値から選ぶ）
formatted, _ := time.Parse(timeformat, "2017-02-08T20:28:06 JST") // 右辺に変換対象となる日時パラメータ
fmt.Println(formatted)                                            // 2017-02-08 20:28:06 +0900 JST

// 曜日名を得る
fmt.Println(time.Weekday(2)) // Tuesday
```

### サンプル
指定日時が現在日時から7日以内か確認。
New!マーク等の表示に使えます。


```go
func getNew() bool {

	// 指定日時を分割
	r := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z`)
	date := r.FindStringSubmatch(varDate)

	// int型へキャスト（この辺が融通きかない…）
	yer, _ := strconv.Atoi(date[1])
	num, _ := strconv.Atoi(date[2])
	var mon time.Month = time.Month(num) // 月はtime.Month型なので注意！
	day, _ := strconv.Atoi(date[3])
	hur, _ := strconv.Atoi(date[4])
	min, _ := strconv.Atoi(date[5])
	sec, _ := strconv.Atoi(date[6])

	// 7日前の日時をセット（実際はconstとか切ってね）
	loc, _ := time.LoadLocation("Asia/Tokyo")
	target := time.Date(yer, mon, day, hur, min, sec, 0, loc)
	target = target.Add(24 * ７ * time.Hour)

	// Before()でbool判定結果を返す
	return time.Now().Before(target)
}
```

## Regexで後方参照

\1や$1では簡単に取り出せない。

```go
import "regexp"
...
r := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z`)
resultSlice := r.FindStringSubmatch(target)
fmt.Println(resultSlice) // [2016-02-07T10:36:25Z 2016 02 07 10 36 25]
```

## 連番付きフィールドからループで値を取得する

例えばこんな構造体。

```go
type Hoge　strunc {
　　　　Field1 string
　　　　Field2 string
　　　　...
}
```

通常フィールドへの参照は`.`で行い、フィールド名に変数は利用できません。
JSのようにハッシュによる参照`Hoge["Field" + i]`ができないため、golangでは困ってしまいます。
そんな時は`reflect`パッケージで値を操作します。

```go
package main

import (
    "fmt"
    "reflect"
    "strconv"
)

type Hoge struct {
    Field1 string
    Field2 string
}

func main() {
    result := []string{}
    rv := reflect.ValueOf(Hoge{Field1: "value1", Field2: "value2"})

    for i := 1; i <= 2; i++ {
        label := "Field" + strconv.Itoa(i)               // iをstring型に変換
        st := rv.FieldByName(label)                      // reflect.Value型
        result = append(result, st.Interface().(string)) // 一旦interface{}型に変換してからstring型へキャスト
    }

    fmt.Println(reflect.TypeOf(result[0])) // string
    fmt.Println(result)                    // [value1 value2]
}
```

reflectで返される値は`reflect.value型`になります。
reflect.value型はそのままstring型へキャストできないため、一旦interface{}型に変換してから型アサーションでstring型に変換します。

元データがgolang標準型であれば問題ないのですが、例えばDBから取得した値で`sql.NullString`等を利用している場合、値が`{{String:"Fuga", Valid: true}}`のようにネスト構造のため困ってしまいます。
そんな時はメソッドチェーンで解決できます。

```go:
st := rv.FieldByName(label).FieldByName("String")
```

## Interfaceの理解に困ったら

sort.goのソースを読む。
https://golang.org/src/sort/sort.go

`Len()`、`Less()`、`Swap()`の扱い方が非常に参考になりました。
処理タイプごとに構造体作って、構造体メソッドとして`Len()`、`Less()`、`Swap()`を定義しておく。つまりは独自構造体にも同様にメソッド追加しておけば独自のsort処理が作れるよーという感じ。

## 同一パッケージ内で同じメソッド名を同居させたい

基本ですが具体例で解説。

```go:user.go
    package models
    type User struct { ... } // DB用の型
    func (d *User) SelectById(id int) {
        ...
    }
```

```go:book.go
    package models
    type Book struct { ... } // DB用の型
    func (d *Book) SelectById(id int) {
        ...
    }
```

上記の例では同じパッケージ名`package models`を利用している。
通常同一パッケージ内での同名メソッドは許可されないが、構造体メソッド化することで可能になる。これはDB操作など、一意のメソッド名で統一したい場合に便利。また構造体がベースなので`u.User.Hoge`のようにフィールドにもシームレスにアクセス可能。

つまりgolangの構造体はclass的振る舞いが可能ということ。
golangの柔軟性を垣間見た気がしました。

ちなみに同一パッケージ名利用のさらなる利点は、配置場所を一つのディレクトリでまかなえる点。
上記では`models`ディレクトリに`user.go`、`book.go`を直接配置可能であり、importも`***/models`の一回でまかなえます。これが異なるパッケージで独立していたら`user/user.go`、`book/book.go`という感じになり、importも個別に行う必要があります。