プログラミング
記事内に商品プロモーションを含む場合があります

Go言語|整数・文字列・構造体のソートの書き方(sortパッケージの使い方)

tadanori

Go言語のソートはかなり便利だと思います。この記事ではGo言語のソートについて解説します。

はじめに

Go言語では複雑なソートもかなり簡単に記述することができます。この記事では、整数型、文字列、浮動小数点のソートなどの基本的なものから、構造体をソートする方法まで解説します。

整数型(int)のソート

昇順にソート

整数型を昇順ソートする場合は、sort.Ints()を利用します。

a := []int{4, 3, 10, 2, 1, 5}

sort.Ints(a)
fmt.Println(a)
// [1 2 3 4 5 10]

降順ソート

大きい順(降順)にソートしたい場合は、以下のようにsort.Sort()Sort.Reverse()とSort.IntSlice()を使います。

個人的には、この方法より後で説明するsort.Slice()を使った方が簡単だと思います。

a := []int{4, 3, 10, 2, 1, 5}

sort.Sort(sort.Reverse((sort.IntSlice(a))))
fmt.Println(a)
// [10 5 4 3 2 1]

文字列型(string)のソート

文字列の昇順ソートにはsort.Strings()を利用します

s := []string{"test", "hello", "world", "abc"}

sort.Strings(s)
fmt.Println(s)
// [abc hello test world]

浮動小数点型(float64)のソート

浮動小数点を昇順ソートするには、sort.Float64()を利用します。

f := []float64{5.6, 22.3, 1.0, 0.44, 2.11}

sort.Float64s(f)
fmt.Println(f)
// [0.44 1 2.11 5.6 22.3]

色々な条件でソート(スライスをソート)

Go言語のsort.Slice()がかなり便利です。複雑なソートもこれを利用すれば記述できます。ソートについては、この関数の使い方だけ覚えておいたら良いかと思います。

sort.Slice関数

sort.Sliceは、第一引数がソートしたいスライス、第二引数がソートを行うためのless関数になります。

例えば、整数型を降順ソートしたい場合は以下のように記述します。

func(i, j int)boolは、条件を満たした場合にtrueを返す関数で、条件によりソートが行われます。

a := []int{4, 3, 10, 2, 1, 5}

sort.Slice(a, func(i, j int) bool {
	return a[i] > a[j]
})
fmt.Println(a)
// [10 5 4 3 2 1]

sort.Sliceは、安定ソートではありません。同値の場合に元の要素順を保ちたい場合はsort.SliceStableを利用します。使い方は同じです。

この書き方で、任意のスライスを好きな条件でソートすることができます

特定の要素でソート

以下のような構造体がある場合に、要素でソートしたい場合は、sort.Slice()を利用します。

type S struct {
	x, y int
}

xでソートしたい場合には、ソート条件をs[i].x < s[j].xとします。

yでソートしたい場合には、s[i].y < s[j].yとします。

このように、構造体の特定の要素でソートすることも簡単にできます。


type S struct {
	x, y int
}
s := []S{{3, 10}, {1, 20}, {5, 1}}

sort.Slice(s, func(i, j int) bool {
	return s[i].x < s[j].x
})

fmt.Println(s)
// [{1 20} {3 10} {5 1}]

sort.Slice(s, func(i, j int) bool {
	return s[i].y < s[j].y
})

fmt.Println(s)
// [{5 1} {3 10} {1 20}]

複数のキーでソート

少し複雑なキーでソートする例を紹介します。この例では、$\frac{x_i}{y_i} <\frac{x_j}{y_j}$の順でソートさせます。

除算をそのまま使ってソートしても良いですが、整数で除算を行うと丸めが発生しますし、浮動小数点に変換して除算を行うと誤差が発生し、ソート順がおかしくなってしまう可能性があります。

ここでは、式を以下のように展開してソートを行います。

$$\begin{eqnarray}
\frac{x_i}{y_i} &<&\frac{x_j}{y_j} \\
x_i \times y_j &<& x_j \times y_i
\end{eqnarray}$$

sort.Sliceを使うと、このように、左辺がs[i].x*s[j].y, 右辺がs[j].x*s[i].yというようなソートも簡単に記述することができます。

以下がプログラム例になります。

type S struct {
	x, y int
}
s := []S{{3, 10}, {1, 20}, {5, 2}, {1, 3}, {3, 4}}

sort.Slice(s, func(i, j int) bool {
	return s[i].x*s[j].y < s[j].x*s[i].y
})
fmt.Println(s)
// [{1 20} {3 10} {1 3} {3 4} {5 2}]

PyThonの場合

同様のソートをPythonで行う場合は以下のようにします。

from functools import cmp_to_key

def compare(obj1, obj2):
    return (obj1[0] * obj2[1]) - (obj2[0] * obj1[1])

s = [(3, 10), (1, 20), (5, 2), (1, 3), (3, 4)]
s.sort(key=cmp_to_key(compare))

print(s)

cmp_to_keyで比較を行う関数を設定します。compareは、obj1<obj2であれば負を、同じであれば0を、obj1>obj2であれば正の値を返す関数です。

要素xは昇順、要素yは降順

少し複雑な例として、「要素xについては昇順、要素xが同値の場合は要素をyについて降順」でソートする場合の例です。

以下、コードになります。func(i,j int)boolの条件を以下のように指定するだけで、複雑なパターンのソートを行うことができます。

type S struct {
	x, y int
}
s := []S{{3, 10}, {1, 22}, {5, 20}, {1, 20}, {5, 2}, {1, 3}, {3, 4}}

sort.Slice(s, func(i, j int) bool {
	if s[i].x == s[j].x {
		return s[i].y > s[j].y
	}
	return s[i].x < s[j].x
})
fmt.Println(s)
// [{1 22} {1 20} {1 3} {3 10} {3 4} {5 20} {5 2}]

PyThonの場合

同様のソートをPythonで行う場合は以下のようにします。

sorted_a = sorted(a, key=lambda x: (x[0], -x[1]))

まとめ

以上、Go言語のソートについて説明しました。Go言語のソートは結構使いやすいと思います。特にsort.Sliceを使いこなせば複雑なソートも簡単に記述できます。覚えておいて損はないと思います。

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました