- Golang で文字列をソートするのに、今まで sort パッケージを使っていましたが、slices パッケージを使ったほうが、何かと便利なことが分かりました。たとえば、次のような文字列があるとします。
lines := []string {
"11月12日",
"1月19日",
"3月4日",
"8月19日",
"7月4日",
"10月1日",
"1月2日"
}
これを月日順に並べ替えるのに
slices.Sort(lines)
としただけでは、次の結果になります。
10月1日
11月12日
1月19日
1月2日
3月4日
7月4日
8月19日
- それで、「月」と「日」の前の数字をいったん int に替え、書式を MMDD 形式にしてから、あらためてソートすることにしました。そのために slices.SortFunc を使いました。
slices.SortFunc(lines, func(a, b string) int {
re := regexp.MustCompile(`([\d]+)月([\d]+)日`)
group := re.FindStringSubmatch(a)
x, _ := strconv.Atoi(group[1])
y, _ := strconv.Atoi(group[2])
z1 := fmt.Sprintf("%02d%2d", x, y)
group = re.FindStringSubmatch(b)
x, _ = strconv.Atoi(group[1])
y, _ = strconv.Atoi(group[2])
z2 := fmt.Sprintf("%02d%2d", x, y)
return strings.Compare(z1, z2)
})
上のコードは cmp をインポートした上で、
group := re.FindStringSubmatch(a)
x, _ := strconv.Atoi(group[1])
y, _ := strconv.Atoi(group[2])
z1 := x * 100 + y
group = re.FindStringSubmatch(b)
x, _ = strconv.Atoi(group[1])
y, _ = strconv.Atoi(group[2])
z2 := x * 100 + y
return cmp.Compare(z1, z2)
として、整数同士の比較を使うこともできました。slices.SortFunc の引数に文字配列と、それを操作する function を渡すと、function で指定したようにソートを行ってくれます。並べ替え用のインデックス文字列などをもとのデータにつけて操作する必要がないのがとても便利です。結果は次の通りです。
1月2日
1月19日
3月4日
7月4日
8月19日
10月1日
11月12日
- 上記を逆順にするには
slices.Reverse(lines)
だけで、簡単にできました。
- ソートした結果を新しい文字配列に格納するのに、
newlist := slices.Sort(list)
は使えません。私は次のようにしていました。
package main
import ("fmt"; "slices")
func main() {
lines := []string {"Ted", "Jim", "Rob", "Bob", "Alice", "Liz", "Kate"}
newlist := mySort(list)
for _, newline := range newlist {
fmt.Println(newline)
}
}
func mySort(input []string) []string {
slices.Sort(input)
return input
}
けれどもこれは、slices.Clone を使えば、
slices.Sort(list)
newlist = slices.Clone(list)
と、簡単に書くことができます。
- slices.Clone は、一時リストを作るときに便利です。
tmplist = slices.Clone(list)
slices.Sort(tmplist)
slices.Compact(tmplist)
var uniq []string
for _, item := range tmplist {
if len(item) > 0 {
uniq = append(uniq, item)
}
}
とすると、もとのリストに影響を与えないで、繰り返しを除いたリストを作成できした。
- slices.Clone, slices.Sort, slices.Compact の組み合わせを使わない場合は、次のようにすれば、同じ結果がえられます。
keys := make(map[string]bool)
var uniq []string
for _, item := range list {
if _, item := keys[entry]; !value {
keys[item] = true
uniq = append(uniq, item)
}
}
ソートのアルゴリズムなど、深堀りしていくと面白いかもしれませんが、まずは、基本的なものを使いこなせてからと思っています。