Edited at

interface{} をソートする

More than 3 years have passed since last update.


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}]
}

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)
}
}

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