🐰

Finderのリスト表示と同じソートでSwiftの[String]を並び替える

2023/08/24に公開
1

Swiftのソート

localizedStandardCompare(_:)

Finderのリスト順と同じソートにするには、NSString の localizedStandardCompare(_:) を使うと良い。
ソート結果はLocaleの設定に依存するので注意。

https://developer.apple.com/documentation/foundation/nsstring/1409742-localizedstandardcompare

let sorted1 = objects.sorted {
	$0.localizedStandardCompare($1) == .orderedAscending
}

sorted(using:)

Swiftなら、NSString の localizedStandardCompare(_:) の代わりに SortComparator の sorted(using:) も使える。引数には String.Comparator.localizedStandard を与える。ソート結果はLocaleの設定に依存するので注意。
iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0から利用可能。

https://developer.apple.com/documentation/foundation/sortcomparator/3802507-localizedstandard

// `.localizedStandard` は currentLocale に依存するため、
// Playground等では強制的にオーバーライドしてあげる。
// 一般のランタイムではシステム設定のままでも良い。
extension NSLocale {
	@objc
	static let currentLocale = NSLocale(localeIdentifier: "ja_JP")
}

let sorted2 = objects.sorted(using: .localizedStandard)

sorted(using:) + SortDescriptor

ソート対象が複雑な構造のオブジェクトである場合、KeyPath指定のSortDescriptorを使う方法もある。

// @available(macOS 14, iOS 17, tvOS 17, watchOS 10, *)
// `comparator`のデフォルト値は`.localizedStandard`のなので省略もできる
array.sorted(using: SortDescriptor(\.customProperty, comparator: .localizedStandard))

簡単な実験

漢字から始まるテキストの配列が検証しやすいので、実験用に適当にアニメ作品のタイトル一覧を用意した。
(タイトルの選定には特に意味はない)

let objects: [String] = [
	"天空の城ラピュタ",
	"蟲師",
	"アキバ冥途戦争",
	"夜は短し歩けよ乙女",
	"アルドノア・ゼロ",
	"約束のネバーランド",
	"冴えない彼女の育てかた",
	"86 Eighty Six",
	"異世界おじさん",
	"あそびあそばせ",
	"四畳半神話大系",
	"とある科学の超電磁砲",
	"サマータイムレンダ",
	"SPY×FAMILY",
	"かぐや様は告らせたい",
	"リコリス・リコイル",
	"陰の実力者になりたくて!",
	"Vivy -Fluorite Eye’s Song-",
	"天気の子",
	"ようこそ実力主義の教室へ",
	"進撃の巨人",
	"陰からマモル!",
	"ヴァイオレット・エヴァーガーデン",
	"この素晴らしい世界に祝福を!",
	"Dr. STONE",
]

これをいくつかの条件で昇順ソートしてみる。

ソート結果:Finderのリスト表示

検証用にFinderのリスト表示(名前・昇順、日本語環境)の結果を掲載。一旦これを理想結果とする。

ソート結果:localizedStandardCompare(_:)

NSStringのlocalizedStandardCompare(_:)では、APIの説明にあるようにFinderのリスト表示と同じ結果が得られた。ロケールはja_JP。

86 Eighty Six
Dr. STONE
SPY×FAMILY
Vivy -Fluorite Eye’s Song-
アキバ冥途戦争
あそびあそばせ
アルドノア・ゼロ
ヴァイオレット・エヴァーガーデン
かぐや様は告らせたい
この素晴らしい世界に祝福を!
サマータイムレンダ
とある科学の超電磁砲
ようこそ実力主義の教室へ
リコリス・リコイル
異世界おじさん
陰からマモル!
陰の実力者になりたくて!
冴えない彼女の育てかた
四畳半神話大系
進撃の巨人
天気の子
天空の城ラピュタ
夜は短し歩けよ乙女
約束のネバーランド
蟲師

ソート結果:sorted(using: .localizedStandard)

ロケール設定の結果の違いも見るため、3パターンを検証した。

ja_JP(日本語・日本国)

ロケールがja_JPの場合。日本語環境のFinderのリスト表示と同じ結果が得られた。
数字、アルファベット、仮名、漢字の順。

仮名は区別せず音の順。

アキバ、あそび、アルドノア

漢字は特定の日本語音読みの五十音順のように見える。

異(イ)、陰(イン)
四(シ)、進(シン)、天(テン)
夜(ヤ)、約(ヤク)

86 Eighty Six
Dr. STONE
SPY×FAMILY
Vivy -Fluorite Eye’s Song-
アキバ冥途戦争
あそびあそばせ
アルドノア・ゼロ
ヴァイオレット・エヴァーガーデン
かぐや様は告らせたい
この素晴らしい世界に祝福を!
サマータイムレンダ
とある科学の超電磁砲
ようこそ実力主義の教室へ
リコリス・リコイル
異世界おじさん
陰からマモル!
陰の実力者になりたくて!
冴えない彼女の育てかた
四畳半神話大系
進撃の巨人
天気の子
天空の城ラピュタ
夜は短し歩けよ乙女
約束のネバーランド
蟲師

en_US(英語・米国)

ロケールがen_USの場合。
数字、アルファベット、仮名、漢字の順。

漢字の順番はUnicode順っぽく見える。

冴 U+51B4
四 U+56DB
夜 U+591C
天 U+5929
異 U+7570
約 U+7D04
蟲 U+87F2
進 U+9032
陰 U+9670

86 Eighty Six
Dr. STONE
SPY×FAMILY
Vivy -Fluorite Eye’s Song-
アキバ冥途戦争
あそびあそばせ
アルドノア・ゼロ
ヴァイオレット・エヴァーガーデン
かぐや様は告らせたい
この素晴らしい世界に祝福を!
サマータイムレンダ
とある科学の超電磁砲
ようこそ実力主義の教室へ
リコリス・リコイル
冴えない彼女の育てかた
四畳半神話大系
夜は短し歩けよ乙女
天気の子
天空の城ラピュタ
異世界おじさん
約束のネバーランド
蟲師
進撃の巨人
陰からマモル!
陰の実力者になりたくて!

zh_TW(中国語・台湾)

ロケールがzh_TW(台湾)の場合。
数字、漢字、アルファベット、仮名の順。

漢字の順番はわからなかったが、多分現地語読みで自然な順番とかになっていそう。

86 Eighty Six
天気の子
天空の城ラピュタ
四畳半神話大系
冴えない彼女の育てかた
夜は短し歩けよ乙女
約束のネバーランド
異世界おじさん
陰からマモル!
陰の実力者になりたくて!
進撃の巨人
蟲師
Dr. STONE
SPY×FAMILY
Vivy -Fluorite Eye’s Song-
アキバ冥途戦争
あそびあそばせ
アルドノア・ゼロ
ヴァイオレット・エヴァーガーデン
かぐや様は告らせたい
この素晴らしい世界に祝福を!
サマータイムレンダ
とある科学の超電磁砲
ようこそ実力主義の教室へ
リコリス・リコイル

Discussion