Logo
About

golangの基本型でないSliceソートし,検索する

2017/10/24

はじめに#


Golangでスライスをソートしたい時に記事を探していたところ,Intなどの固定型ならsortパッケージの関数一つでsort可能ということがわかりました.
他の型のスライスも綺麗にできるだろうと調べてみるとインターフェースLen,Less,Swapを実装してSortを行えるという記事が多くでてきます.
しかしGoさんならもっと簡単な関数を実装しているはずと信じてさらに調べると去年sort.Sliceなるものがマージされていました!(という記事を見つけた)
ということで比較!

ついでに日付検索もします

環境#


インターフェース実装でのSort#


package main

import (
	"time"
	"sort"
	"fmt"
)

type Times []time.Time

// Len, Less, Swapを定義
func (t Times) Len() int {
	return len(t)
}

func (t Times) Less(i, j int) bool {
	return t[i].Before(t[j])
}

func (t Times) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}

func main() {
	const DFmt = "2006-01-02"

	times := make([]time.Time, 3)
	times[0], _ = time.Parse(DFmt, "2017-01-01")
	times[1], _ = time.Parse(DFmt, "2017-01-03")
	times[2], _ = time.Parse(DFmt, "2017-01-02")

	sort.Sort(Times(times))

	for _, t := range times {
		fmt.Printf("%+v\n", t)
	}
}

/* 実行結果
2017-01-01 00:00:00 +0000 UTC
2017-01-02 00:00:00 +0000 UTC
2017-01-03 00:00:00 +0000 UTC
*/

sort.Sliceを使用#


package main

import (
	"time"
	"sort"
	"fmt"
)

func main() {
	const DFmt = "2006-01-02"

	times := make([]time.Time, 3)
	times[0], _ = time.Parse(DFmt, "2017-01-01")
	times[1], _ = time.Parse(DFmt, "2017-01-03")
	times[2], _ = time.Parse(DFmt, "2017-01-02")

    // iを小さくすれば昇順
	sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) })

	for _, t := range times {
		fmt.Printf("%+v\n", t)
	}
}

/* 実行結果
2017-01-01 00:00:00 +0000 UTC
2017-01-02 00:00:00 +0000 UTC
2017-01-03 00:00:00 +0000 UTC
*/

やっていることとしてはあまり変わらないですが拡張しない限りsort.Sliceで良いと思います

おまけの日付検索#


goの検索では昇順になったリストでsort.Searchが使えます.
このSearchは2分探索になっていて昇順リストでしか使えませんが高速検索できます.

package main

import (
	"time"
	"sort"
	"fmt"
)

func main() {
	const DFmt = "2006-01-02"

	times := make([]time.Time, 3)
	times[0], _ = time.Parse(DFmt, "2017-01-01")
	times[1], _ = time.Parse(DFmt, "2017-01-03")
	times[2], _ = time.Parse(DFmt, "2017-01-02")

	// "2017-01-02"を検索 expect: index 1
	search := times[2]

	// iを小さくすれば昇順
	sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) })

	index := sort.Search(len(times), func(i int) bool { return times[i].After(search) || times[i].Format(DFmt) == search.Format(DFmt) })
	if index == len(times) {
		fmt.Println("could not find")
	}
	fmt.Println("found in index ", index)
}

おわりに#


Golangは進化していっているので2年前の記事とかだと古かったりします.
そのため古い記事でスマートではないコードがあれば最近のを探すようにしましょう.
このコードも古い!とかあればコメントにお願いします.