ぼくにはマイナンバーないのかな。そうだ(犬)登録番号があった。
今回の概要
前回は、2次元の配列を1次元に変換してからソートを行なった後、1次元を2次にして出力していました。
今回は、2次元のままソートして出力できないか考えてみました。
ファイルが小さくなる原因
前回「 input.csv 」から「 output.csv 」への変換後のファイルの大きさが小さくなりましたが、その原因が分かりました。
それは、改行文字が違うことが原因のようです。
次の画像は今回のもので、左がインプット前、左がアウトプット後のファイルをメモ帳で開いたものです。
ここで、2つ画像の下のバーを見てください。
左は「Windows(CRLF)」、右は「Unix(LF)」となっています。
CRLF は2バイト、LF は1バイトですので小さくなるはずです。
今回も、「 output.csv 」が小さくなりました。
104万レコードだから104万バイト=>1000Kバイト少なくなり、計算が合います。
1次元→2次元 ソースコード
package main import ( "encoding/csv" "log" "os" "golang.org/x/text/encoding/japanese" "golang.org/x/text/transform" ) func QuickSort(elements [][]string, left int, right int) { if left < right { posi := getposi(elements, left, right) QuickSort(elements, left, posi-1) QuickSort(elements, posi+1, right) } } func getposi(elements [][]string, left int, right int) int { //軸を右端 pivot := elements[right][0] //開始位置 i := left //昇順にするため左に小さいもののエリア for j := left; j < right; j++ { //pivotより小さいものが見つかった if pivot > elements[j][0] { swap(elements[i], elements[j]) //次の交換位置 i += 1 } } //pivotの位置iが定まったので交換 swap(elements[i], elements[right]) //位置を返す return i } func swap(element1 []string, element2 []string) { for i := 0; i < len(element1); i++ { val := element1[i] element1[i] = element2[i] element2[i] = val } } func main() { in, err := os.Open("./input.csv") if err != nil { log.Fatal(err) } defer in.Close() r := csv.NewReader(transform.NewReader(in, japanese.ShiftJIS.NewDecoder())) // records => [][]string records, err := r.ReadAll() if err != nil { log.Fatal(err) } QuickSort(records, 0, len(records)-1) // 書き込み先ファイル out, err := os.Create("./output.csv") if err != nil { log.Fatal(err) } defer out.Close() // 書き込み w := csv.NewWriter(transform.NewWriter(out, japanese.ShiftJIS.NewEncoder())) w.WriteAll(records) }
複数列の条件への拡張
任意の列でソートができれば、複数列を条件にしてソートできるか試してみました。
関数 getposi( func getposi )を
func getposi(elements [][]string, left int, right int) int {
//軸を右端
pivot := elements[right][0]
//開始位置
i := left
//昇順にするため左に小さいもののエリア
for j := left; j < right; j++ {
//pivotより小さいものが見つかった
if pivot > elements[j][0] {
swap(elements[i], elements[j])
//次の交換位置
i += 1
}
}
//pivotの位置iが定まったので交換
swap(elements[i], elements[right])
//位置を返す
return i
}
次のように入れ替えて実行してみました。
func getposi(elements [][]string, left int, right int) int { //軸を右端 pivot := elements[right][0] + elements[right][1] //開始位置 i := left //昇順にするため左に小さいもののエリア for j := left; j < right; j++ { //pivotより小さいものが見つかった if pivot > (elements[j][0] + elements[j][1]) { swap(elements[i], elements[j]) //次の交換位置 i += 1 } } //pivotの位置iが定まったので交換 swap(elements[i], elements[right]) //位置を返す return i }
できました。
しかも、かかった時間も変わらず意図したとおりのものが出力されました。
ただただ、恐るべし GO です。
ここまでの完成形
package main import ( "encoding/csv" "log" "os" "golang.org/x/text/encoding/japanese" "golang.org/x/text/transform" ) func QuickSort(elements [][]string, left int, right int) { if left < right { posi := getposi(elements, left, right) QuickSort(elements, left, posi-1) QuickSort(elements, posi+1, right) } } func getposi(elements [][]string, left int, right int) int { //軸を右端 pivot := elements[right][0] + elements[right][1] //開始位置 i := left //昇順にするため左に小さいもののエリア for j := left; j < right; j++ { //pivotより小さいものが見つかった if pivot > (elements[j][0] + elements[j][1]) { swap(elements[i], elements[j]) //次の交換位置 i += 1 } } //pivotの位置iが定まったので交換 swap(elements[i], elements[right]) //位置を返す return i } func swap(element1 []string, element2 []string) { for i := 0; i < len(element1); i++ { val := element1[i] element1[i] = element2[i] element2[i] = val } } func main() { in, err := os.Open("./input.csv") if err != nil { log.Fatal(err) } defer in.Close() r := csv.NewReader(transform.NewReader(in, japanese.ShiftJIS.NewDecoder())) // records => [][]string records, err := r.ReadAll() if err != nil { log.Fatal(err) } QuickSort(records, 0, len(records)-1) // 書き込み先ファイル out, err := os.Create("./output.csv") if err != nil { log.Fatal(err) } defer out.Close() // 書き込み w := csv.NewWriter(transform.NewWriter(out, japanese.ShiftJIS.NewEncoder())) w.WriteAll(records) }
より柔軟に
ピボットを複数にして上下関係を決めれば柔軟にソートできます。
比較する関数( getposi )の修正を次のようして実行してみました。
ただ、ソート時間はかかるようになり、上の例ですと5秒ほど要しました。
func getposi(elements [][]string, left int, right int) int { //軸を右端 pivot1 := elements[right][0] pivot2 := elements[right][1] //開始位置 i := left //昇順にするため左に小さいもののエリア for j := left; j < right; j++ { //pivot1より小さいものが見つかった if pivot1 > elements[j][0] { swap(elements[i], elements[j]) //次の交換位置 i += 1 } else if pivot1 == elements[j][0] { //pivot2より小さいものが見つかった if pivot2 > elements[j][1] { swap(elements[i], elements[j]) i += 1 } } } //pivotの位置iが定まったので交換 swap(elements[i], elements[right]) //位置を返す return i }