Goのスライスを効率的にソートする方法

## はじめに
Go言語はそのシンプルさと効率の良さで人気を集めていると思っている。特に、スライス（動的配列）の操作はGoの重要な特徴の一つです。今回は、Go言語でスライスをソートする具体的な方法を、実用的な例を交えてご紹介しようと思います。

## サンプルコードと説明
```go
package main

import (
	"fmt"
	"sort"
	"time"
)

type Article struct {
	Title     string    // 記事のタイトル
	CreatedAt time.Time // 記事が作成された日時
}

// Articles は複数のArticleを含むスライスです。
type Articles []Article

// SortByCreatedAt はArticlesをCreatedAtフィールドに基づいて昇順にソートします。
func (a Articles) SortByCreatedAt() {
	sort.SliceStable(a, func(i, j int) bool {
		return a[i].CreatedAt.Before(a[j].CreatedAt)
	})
}

func main() {
	// サンプルの記事データを作成します。
	articles := Articles{
		{Title: "Goの便利な機能", CreatedAt: time.Date(2024, 1, 10, 0, 0, 0, 0, time.UTC)},
		{Title: "GoでWebアプリケーションを作る", CreatedAt: time.Date(2024, 1, 5, 0, 0, 0, 0, time.UTC)},
		{Title: "Goの基本", CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)},
	}

	// ソート前の記事を表示します。
	fmt.Println("ソート前:")
	for _, article := range articles {
		fmt.Printf("  %v: %v\n", article.CreatedAt.Format("2006-01-02"), article.Title)
	}

	// 記事をソートします。
	articles.SortByCreatedAt()

	// ソート後の記事を表示します。
	fmt.Println("\nソート後:")
	for _, article := range articles {
		fmt.Printf("  %v: %v\n", article.CreatedAt.Format("2006-01-02"), article.Title)
	}
}

  // ソート前： 
  // 2024-01-10: Goの便利な機能
  // 2024-01-05: GoでWebアプリケーションを作る
  // 2024-01-01: Goの基本

  // ソート後：
  // 2024-01-01: Goの基本
  // 2024-01-05: GoでWebアプリケーションを作る
  // 2024-01-10: Goの便利な機能
```

上記のようなコードがあるとして、実際に`Sort`を実行する関数は`SortByCreatedAt()`です。
このメソッドは、`Articles` 型（`Article` 構造体のスライス）に定義されています。`Article` 構造体には、少なくとも `CreatedAt` というフィールドが含まれており、これは記事が作成された日時を表しています。このメソッドの目的は、`Articles` スライス内の `Article` 要素を、その `CreatedAt` フィールドの値に基づいてソートすることです。

具体的には、`sort.SliceStable` 関数を使用しています。この関数はGo言語の標準ライブラリに含まれる関数で、指定されたスライスを安定的にソートするために使用されます。安定的なソートとは、等しい要素の相対的な順序がソート前後で保持されることを意味します。

1. ソートするスライス：この場合は `a`、つまり `Articles` 型のインスタンスです。
2. 比較関数：`func(i, j int) bool` というシグネチャを持つ関数です。この関数はスライス内の2つの要素（`i`と `j` の位置にある要素）を比較し、`i` 番目の要素が `j` 番目の要素より「小さい」場合に `true` を返します。
この場合の比較関数は、`a[i].CreatedAt.Before(a[j].CreatedAt)` を使用しています。これは `Article` 構造体の `CreatedAt` フィールド`（time.Time 型）`を比較し、`i` 番目の記事が `j` 番目の記事よりも早く作成されたかどうかを判断します。`Before` メソッドは、一つの `time.Time` 値が別の `time.Time` 値よりも時刻的に前であるかを確認するためのものです。

結果として、この `SortByCreatedAt` メソッドは `Articles` スライスを、各 `Article` が作成された時間の昇順（古いものから新しいものへ）でソートします。これは、例えばブログ記事を投稿日時の順に表示する場合などに便利です。

## SortByCreatedAt関数の書き方の違い、パフォーマンス観点
```go 
func (a Articles) SortByCreatedAt() {
	sort.SliceStable(r, func(i, j int) bool {
		return a[i].CreatedAt.UnixNano() < a[j].CreatedAt.UnixNano()
	})
}
------
func (a Articles) SortByCreatedAt() {
	sort.SliceStable(a, func(i, j int) bool {
		return a[i].CreatedAt.Before(a[j].CreatedAt)
	})
}
```
この二つの `SortByCreatedAt` メソッドの結果には基本的な違いはありませんが、比較の方法とパフォーマンスの観点で微妙な差異があります。
1. `r[i].CreatedAt.UnixNano() < r[j].CreatedAt.UnixNano()` を使用する方法:
この方法では、`time.Time` 型の `CreatedAt` フィールドを `UnixNano` メソッドでナノ秒単位の整数に変換して比較しています。
ナノ秒単位での比較は非常に正確で、ほんのわずかな時間差も検出できます。
ただし、整数比較のため、特定の条件下でのオーバーフローの可能性がわずかに存在します（非常に稀ですが、理論上は可能）。

2. `a[i].CreatedAt.Before(a[j].CreatedAt)` を使用する方法:
`Before` メソッドは、一つの `time.Time` 値が別の `time.Time` 値よりも時刻的に前であるかを判断します。
この方法は、`time.Time` 型の値を直接比較するため、より自然で読みやすいコードになります。
`Before` メソッドは、内部的に `time.Time` 型の値をナノ秒まで比較するので、精度は `UnixNano` メソッドを使用する方法と同等です。

パフォーマンスの観点からは、これらの方法の間に顕著な差はないと考えられます。両方とも、比較にかかる時間は非常に短く、実際のソートアルゴリズムの実行時間に比べれば無視できる程度です。しかし、`Before` メソッドを使う方がコードの可読性が高く、Goの標準ライブラリの関数を利用するため、一般的には好まれる方法です。

結論として、どちらの方法も同等の結果と精度を提供しますが、`Before` メソッドを使用する方が一般的なGoの慣習により適合し、コードの可読性が高いと言えます。

### 参考文献
https://pkg.go.dev/sort#SliceStable