Go言語

【Go言語】sortパッケージでのソート処理

Go言語でソートするときは、sortパッケージを使う。

sortパッケージは、スライスやユーザーが独自に定義したコレクションをソートするプリミティブを用意している。

標準パッケージで対応している型

  • float64
  • int
  • string

[]string(文字列のスライス)のソート

string型のスライスをソートするときには、sort.Stringsを使う。(昇順)
sort.Strings関数は、sort.Sort(sort.StringSlice(s))を呼んでいるだけ。

逆順にしたい場合は、sort.Reverseを使う。

package main

import (
    "fmt"
    "sort"
)

func main() {
    var names []string
    names = []string{"Ryo", "Akari", "Chen", "Eri", "Gon", "Jin", "Ken"}
    fmt.Printf("ソート前: %s\n", names) // ソート前: [Ryo Akari Chen Eri Gon Jin Ken]
    sort.Strings(names)             // sort.Sort(sort.StringSlice(names))
    fmt.Printf("ソート後: %s\n", names) // ソート後: [Akari Chen Eri Gon Jin Ken Ryo]

    // 逆順にソート
    sort.Sort(sort.Reverse(sort.StringSlice(names)))
    fmt.Printf("逆順にソート: %s\n", names) // 逆順にソート: [Ryo Ken Jin Gon Eri Chen Akari]
}

Go Playground: https://play.golang.org/p/89-HDjq0zHC

[]int(数値のスライス)のソート

int型のスライスをソートするときには、sort.Intsを使う。(昇順)
sort.Ints関数は、sort.Sort(sort.IntSlice(s))を呼んでいるだけ。

逆順にしたい場合は、sort.Reverseを使う。

package main

import (
    "fmt"
    "sort"
)

func main() {
    numbers := []int{4, 1, 2, 5, 3}
    fmt.Printf("ソート前: %d\n", numbers) // ソート前: [4 1 2 5 3]
    sort.Ints(numbers)                // sort.Sort(sort.IntSlice(numbers))
    fmt.Printf("ソート後: %d\n", numbers) // ソート後: ソート後: [1 2 3 4 5]

    // 逆順にソート
    sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
    fmt.Printf("逆順にソート: %d\n", numbers) // 逆順にソート: [5 4 3 2 1]
}

Go Playground: https://play.golang.org/p/XTbb9XZxjtw

[]float64(浮動小数点数のスライス)のソート

float64型のスライスをソートするときには、sort.Float64sを使う。(昇順)
sort.Float64s関数は、sort.Sort(sort.Float64Slice(s))を呼んでいるだけ。

逆順にしたい場合は、sort.Reverseを使う。

package main

import (
    "fmt"
    "sort"
)

func main() {
    numbers := []float64{1.1, 1.4, 1.2, 1.5, 1.3}
    fmt.Printf("ソート前: %g\n", numbers) // ソート前: [1.1 1.4 1.2 1.5 1.3]
    sort.Float64s(numbers)            // sort.Sort(sort.Float64Slice(numbers))
    fmt.Printf("ソート後: %g\n", numbers) // ソート後: ソート後: [1.1 1.2 1.3 1.4 1.5]

    // 逆順にソート
    sort.Sort(sort.Reverse(sort.Float64Slice(numbers)))
    fmt.Printf("逆順にソート: %g\n", numbers) // 逆順にソート: [1.5 1.4 1.3 1.2 1.1]
}

Go Playground: https://play.golang.org/p/GgUsorCj7Kg

独自のルールや型でソートする

ソートのルールを独自に実装したり、string, float64, int以外の型のスライスをソートする場合。

2通りの方法がある。

  • Slice, SliceStableを使う。SliceStableの場合は安定ソートとなる。
  • Interfaceを満たす型を定義して、Sort, Stableを使う。Stableの場合は安定ソートとなる。

Slice, SliceStableの場合

文字列の長さ、structのフィールドでソートする例。
次のstructがあるとする。

type User struct {
    Name string
    Age int
}

このAgeフィールドでソートする。

package main

import (
    "fmt"
    "sort"
)

type User struct {
    Name string
    Age  int
}

func main() {
    // []stringを文字数(len)が少ない順にソート
    languages := []string{"Ruby", "JavaScript", "Go", "PHP", "Python"}
    sort.Slice(languages, func(i, j int) bool { return len(languages[i]) < len(languages[j]) })
    fmt.Printf("ソート後: %s\n", languages) // ソート後: [Go PHP Ruby Python JavaScript]

    // []Userを年齢順にソート
    users := []User{
        {"Sato", 7},
        {"Suzuki", 55},
        {"Takahashi", 24},
        {"Yamada", 75},
    }
    sort.Slice(users, func(i, j int) bool { return users[i].Age < users[j].Age })
    fmt.Println("ソート後:", users) // [{Sato 7} {Takahashi 24} {Suzuki 55} {Yamada 75}]
}

Go Playground: https://play.golang.org/p/wLtOeDl6wN3

Sort, Stableの場合

先程と同じようにUser.Ageでソートする。

ソートしたい型のスライスに sort.Interface を実装してあげれば、sort.Sort関数でソートできる。

type Interface interface {
    // Len is the number of elements in the collection.
    // スライスの要素数
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    // 要素同士の比較ロジック。インデックスiの要素がインデックスjの要素の前にくるべきかどうかを判定する処理。
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    // インデックスiの要素とjの要素を交換する処理。
    Swap(i, j int)
}

実装してみるとこんな感じ。

package main

import (
    "fmt"
    "sort"
)

type User struct {
    Name string
    Age  int
}

// ByAge はsort.Interfaceの実装
type ByAge []User

func (a ByAge) Len() int {
    return len(a)
}

func (a ByAge) Less(i, j int) bool {
    return a[i].Age < a[j].Age
}

func (a ByAge) Swap(i, j int) {
    a[i], a[j] = a[j], a[i]
}

func main() {
    // []Userを年齢順にソート
    users := []User{
        {"Sato", 7},
        {"Suzuki", 55},
        {"Takahashi", 24},
        {"Yamada", 75},
    }
    sort.Sort(ByAge(users))
    fmt.Println("ソート後:", users) // ソート後: [{Sato 7} {Takahashi 24} {Suzuki 55} {Yamada 75}]
    sort.Sort(sort.Reverse(ByAge(users)))
    fmt.Println("逆順ソート後:", users) // 逆順ソート後: [{Yamada 75} {Suzuki 55} {Takahashi 24} {Sato 7}]
}

Go Playground: https://play.golang.org/p/aNUHPtgISe4

 

最初に紹介した、sort.Stringssort.Ints, sort.Float64sも同じ。
sort.Stringsの実装は、sort.Sort(sort.StringSlice(s))を呼んでいるだけで、このsort.StringSliceはsort.Interfaceの実装。
 

参考図書

Tour of Goを終えた人にオススメです!!

イチオシ記事

1

自己紹介 フリーランスエンジニアをしているヨノと申します。 独学でプログラミングを学び、ソシャゲ・SaaS開発などを経て、2018年からフリーランスエンジニアとして活動しています。 主にバックエンド中 ...

2

はじめまして、フリーランスエンジニアのヨノと申します。 自己紹介 独学でプログラミングを学び、ソシャゲ・SaaS開発などを経て、2018年からフリーランスエンジニアとして活動しています。 主にバックエ ...

3

ネット上で色々言われているフリーランスエンジニア....。「本当はどうなの?」と思っている人は多いでしょう。 そこで本記事ではフリーランスエンジニア5年生の私が、ネット上の意見も引用しながら実態を解説 ...

-Go言語