pagetakaBlog

最近リフォームと鳥取県日南町の記事多め。写真、PC、ネット等の話題も

Go言語…爺の暴走:Mapに苦戦、Python辞書似だが整列嫌い。別段取りで順番に画面表示。キーを増やし便利に…遠い即妙[スクリプト付き]

しばらく、Go言語の記事UPできませんでした。本日は、爺的に大変難関だった「Map」を備忘録として残します。
今回のスクリプトは、パッケージmainのmain.goの盲腸のような使い方をしています(後述)。あらかじめお断りしておきます。

現在書いているパッケージから離れず、データベース接続無しで、Go言語のMapを整理しようと思い、Microsoft:Copilotに助けてもらいました。Goスクリプトが動く環境を前提にしています。

【Go言語:Mapを使う。表示順毎回異なる。サンプルスクリプトの盲腸的実行】
【Go言語:Mapを使う。表示順毎回異なる。サンプルスクリプトの盲腸的実行】

Map は何でしょう?

GolangのMapについて説明できるほどの理解・知識、爺にはありませぬ。Microsoft:Copilotに教えてもらいながら、爺的に大切だと思った点を箇条書きにします。

  • Go言語のデータ型のひとつで、Pythonの辞書型に似ている
  • キーと値がセットになっている
  • キーと値のセットを複数保持できる
  • キーの重複・衝突を回避する保存方法が担保されている
  • 複数種類のキー、複数種類の値を保持できる
  • キーの並び順は一定でないので必要ならソートした結果を利用する。Mapそのものをソートしても担保がないのを覚えておくのが大切かと
  • ハッシュテーブルとして保存され、キー順に並んでなくても動作パフォーマンスに支障なし。人間が見た目に整理されているのと、PC的に都合よく保存されているのには距離がある
  • Map名[キー]で値を「参照型」として取得できる
  • 存在しないキーの値参照など要注意。あらかじめキーの存在チェックが有効
  • セットの追加・削除、キー削除・変更、Mapの削除・名前変更など、それぞれ信頼できるサイトから情報を得てください
  • 爺はやってないのでよくわかりませんが、Copilotによると「並列処理時にmapは安全でない」「mapのサイズが増えると(略)メモリ使用量が変動することがある」とのこと、ご留意のほど

齟齬などありましたら、優し目に教えてください。

package main 内での関数往復でテストスクリプト実行。

この記事の先頭付近で触れた、「盲腸的」なスクリプトのコトです。Go言語はmainパッケージのmain関数から開始する手はずになっています。Pythonだとテスト的に別のスクリプトを書いて試すというのができますが、Goの場合mainというのが引っかかりになり、少しストレスが溜まってました。Copilotに相談したらいくつかの回避策を教えてくれました。そのうちのひとつ「盲腸的」にテストスクリプトを試す方法について、書いておきます。

スクリプトは次のように構成しています。同じワークスペースのmainパッケージ内に、main.go と test.go のふたつのファイルがあるという想定です。

// main.go
package main
import (
    "os"
func main(){
testX()    // 関数往復、戻り値無し =盲腸的関数へ
os.Exit(1)  // 強制終了
//makeconnDB(DB)  // 本来なら次に実行する関数
}
// test.go
package main
func testX() {
// いろんな処理(本記事後半にスクリプト掲載)
}

テスト的なスクリプトが不要になったら、func main 内のtestX() と os.Exit(1)の行を削除すれば、何事も無かったように本線を走らせることができます。なお、importのosも不要になる可能性もありますね。

盲腸的に使うテスト用スクリプト

package main

import (
	"fmt"
	"sort"
)

func testX() {
	var dataMap = map[string][]any{
		"1332": {"ニッスイ", 50},
		"1515": {"日鉄鉱業", 1050},
		"1518": {"三井松島", 3800},
		"1861": {"熊谷組", 2050},
		"2264": {"森永乳業", 3050},
		"3002": {"グンゼ", 3100},
		"3101": {"東洋紡", 3100},
		"3863": {"日本製紙", 3150},
		"5019": {"出光興産", 3300},
		"6586": {"マキタ", 3600},
		"7947": {"エフピコ", 3200},
	}

	fmt.Println("testX start")
	// 単に表示するだけの処理:CDをキーにした状態担保されず
	fmt.Println("\ndataMapを表示:この時点で整序担保なし")
	for cd, rows := range dataMap {		
		fmt.Printf("cd:%s\t%s   \t33業種:%d\n", cd, rows[0], rows[1])
	}

	// cdをキーにして整序した状態で表示する処理
	sortedCD_Keys := make([]string, 0, len(dataMap))
    for cd := range dataMap {
        sortedCD_Keys = append(sortedCD_Keys, cd)
    }    
    sort.Strings(sortedCD_Keys)	// **CDを昇順にソート**
	fmt.Println("\n銘柄コードcdを抽出し整序したスライス表示")
	fmt.Println("sortedCD_Keys:", sortedCD_Keys)

	fmt.Println("\n整序済み銘柄コードsortedCD_Keysを使い、dataMapから順にピックアップし表示")
	for _, cd := range sortedCD_Keys {	// cd順に表示する		
		rows := dataMap[cd]
		fmt.Printf("cd:%s\t%s   \t33業種:%d\n", cd, rows[0], rows[1])
	}

	// 業種コードを主キーにしたMAP作成←dataMap改修
	fieldCDMap := make(map[int] map[string] string)
	for cd, cdData := range dataMap {
		companyName := cdData[0].(string)
		fieldCode, ok := cdData[1].(int)
		if !ok {
			fmt.Println("業種コードの型が int ではありません:", cdData[1])
			continue // **型変換エラーを防ぐ**
		}
		if _, exists := fieldCDMap[fieldCode]; !exists {
			fieldCDMap[fieldCode] = make(map[string]string)
		}
		fieldCDMap[fieldCode][cd] = companyName
	}
	fmt.Println("\nfieldCDMap start:業種コードを主キーにしたfieldCDMap作成←整序担保なし")
	for fieldCode, cdData := range fieldCDMap {
		fmt.Printf("fieldCD:%d\n", fieldCode)
		for cd, companyName := range cdData {
			fmt.Printf("\tcd:%s\t%s\n", cd, companyName)
		}
	}
	fmt.Println("fieldCDMap end")
	fmt.Println("\ntestX end")
}

掲載スクリプトの概略

載せたスクリプトを動かすには、Go稼働環境が必要です。掲載のままだとmain.goファイルが必要です。

株式に関するデータを例にMapを理解しようという目論見です。DBなしに使うので、スクリプト内にdataMapを宣言し書き込みました。
map[string]anyのうち[string]は銘柄コードを指しています。最近アルファベットが使用されていて文字として扱う必要があります。anyは、文字の通り、なんでもいくつか…というような意味かと思います。
"1332": {"ニッスイ", 50}, は 「:」を挟んで左側がkey(キー)で右側が値(今回の場合複数)です。会社名と33業種分類を収めています。string、intの型になっています。
その後の流れは、スクリプト内のコメントを、ターミナル表示も同様にご確認ください。

  1. dataMapを画面表示:銘柄コード順に並んでおらず、何回かやってみても同じ並びにはならないみたい
  2. 銘柄コードを昇順にしたsortedCD_Keysを作成
  3. sortedCD_Keysを使い、ヒットした銘柄と値を順に表示(dataMapがsortされたわけではない)
  4. 値のひとつ業種コードを主キーにしたfieldCDMapをdataMapからつくりだす(fieldCDMapの業種順は担保されない)
  5. 前項で業種コードを主キーにしたfieldCDMapができたので、これを業種コード別に画面表示する(業種順は担保されない)

GolangのMapはキー順に並ばないので、利用時に別段の手立てを使い狙い順に抽出し表示する。

Mapで爺が一番混乱したのは、構造はわかっているのに、並び順が定かでないという状態です。学校のクラスに生徒がN人いるとして、座っている場所が定かではないので、先生が出席番号順に名前を呼んで確認する、というようなコトに似ています。それが毎回異なる・変化するというので、授業一コマ毎に席が変わるのにも似ているようです。

人間が確認するには、出席順に並んだデータ・値を順に確認するのが便利なようですが、PC的には異なるようです。ハッシュ・データという方法で保管されていて、探すときに便利なようにできている、のだそうです。

ここで大切なのは、Mapを整理して並ばせるより、こう並んだデータを得たいという、導きになる変数(スライス)を用意し、その順に従って、キーと値を取得し、画面表示なり他のデータと引き合わせたりする、という考え方のような気がします。

値をキーに昇格させ二重キーとして使う。

Mapは二つ以上のキーを持つことができるようです。今回は、銘柄コード(cd)をキーにしました。値のひとつであった「33業種分類コード」をキーのひとつに変換し主キーとして使うというのもcopilotに教えてもらってできました。

当初予定していた以外の利用というのもありそうで、値をキーに変換したり、二重キーにしたり、いろんなことができるのだと…爺は感想だけで、理解したとまでには至ってないのです。備忘録として残すのが精いっぱいです。