interface{} をソートする

  • 6
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Slice のソート

シンプルに型アサーション (Type Assertions) を使います。

type Person struct {
    Name string
    Age  int
}

type People []Person

func (p People) Len() int           { return len(p) }
func (p People) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p People) Less(i, j int) bool { return p[i].Age < p[j].Age }

func Sort(v interface{}) interface{} {
    s, ok := v.(sort.Interface)
    if !ok {
        return v
    }
    sort.Sort(s)
    return s
}

func main() {
    people := People{{"Bob", 31}, {"John", 42}, {"Michael", 17}, {"Jenny", 26}}
    fmt.Println(people)  // [{Bob 31} {John 42} {Michael 17} {Jenny 26}]
    Sort(people)
    fmt.Println(people)  // [{Michael 17} {Jenny 26} {Bob 31} {John 42}]
}
see this code in play.golang.org

interface{}sort.Interface を実装した Slice であると期待できる場合、この方法で十分でしょう。

入れ子になった Slice のソート

入れ子になった Slice もソートしたい場合は、構造体のフィールドと Slice のインデックスを走査します。

func Sort(v interface{}) {
    switch values := reflect.ValueOf(v); values.Kind() {
    case reflect.Slice:
        for i := 0; i < values.Len(); i++ {
            if values.Index(i).CanSet() {
                Sort(values.Index(i).Interface())
            }
        }
    case reflect.Struct:
        for i := 0; i < values.NumField(); i++ {
            if values.Field(i).CanSet() {
                Sort(values.Field(i).Interface())
            }
        }
    case reflect.Ptr:
        Sort(values.Elem().Interface())
    }
    if s, ok := v.(sort.Interface); ok {
        sort.Sort(s)
        return
    }
    switch s := v.(type) {
    case []int:
        sort.Ints(s)
    case []float64:
        sort.Float64s(s)
    case []string:
        sort.Strings(s)
    }
}
see this code in play.golang.org

sort.Interface が実装されていないフィールドが []int, []float64, []string のいずれかだったときは sort パッケージに用意されているデフォルト実装でソートを行うようにしています。