LoginSignup
9
5

More than 3 years have passed since last update.

【Swift】Arrayからユニークな順序付きリストへの変換を超絶シンプルに書く

Last updated at Posted at 2019-12-03

SwiftでArrayから重複なしの順序を保ったままのリストを生成したい!

と思って調べてみると、この辺の記事が参考になりますが、reduce()を使って愚直に全てをなめていく実装が散見されます:eyes:

今回はもっとスマートさを求めてシンプルに書けないか考えていきます:muscle:
(よりシンプルに書くことに特化しているのでパフォーマンスは気にしてません:yum:

Setは使える?

SwiftにはSetがあります。が、答えはノーです。
なぜなら、Setは重複しない順序を持たないリストだからです。
やってみます。

let array: [String] = ["hoge", "fuga", "hoge", "piyo", "piyo"]
// 最初に出てきた順でユニークになってほしい -> ["hoge", "fuga", "piyo"]

print(Set(array)) // "["fuga", "piyo", "hoge"]\n"
print(Set(array)) // "["piyo", "hoge", "fuga"]\n"
print(Set(array)) // "["piyo", "hoge", "fuga"]\n"

毎回結果が変わってしまいました。これでは順序を保つという条件が保証できません:sob:

順序付きのSetってないの?

Swift用はSet一択のようで、存在しないようです。
ですが、Foundation FrameworkにはNSOrderedSetという昔ながらのImmutableな順序付きSetのクラスが現在もサポートされています。

NSOrderedSetを使って順序付きのユニークリストを作る

さっそく書いていきましょう。

import Foundation

extension Array where Element: Hashable {
    func unique() -> [Element] {
        return NSOrderedSet(array: self).array as! [Element]
    }
}

こんな感じに書けるかと思います。

どうでしょう、めちゃくちゃシンプルですよね:sparkles:
NSOrderedSetに入れて吐き出すだけです!

NSOrderedSetはObjective-C時代のものなので、インターフェースがNSArray時代のように[Any]となってしまいます。
そのため、入力時も[Any]として扱われ、出力ももちろん[Any]として返ってきてしまいます。

そこで、SwiftのArrayのextensionとしてメソッドを用意し、Elementの型を入出力で縛ってあげることによって、使用する側は型安全でSwiftらしく書けるようにしています。
as!のような汚い実装はこうしてextensionの中に隠蔽してしまいましょう:mask:

では、最後に結果を確認していきます。

let array: [String] = ["hoge", "fuga", "hoge", "piyo", "piyo"]

print(array.unique()) // "["hoge", "fuga", "piyo"]\n"
print(array.unique()) // "["hoge", "fuga", "piyo"]\n"
print(array.unique()) // "["hoge", "fuga", "piyo"]\n"

いかがでしょうか?
呼び出しもシンプルで今回の目的を達成することができました:clap:
こちらは順序をサポートしているので、何度実行しても結果は変わりません!

P.S.

  • Advent Calendarに登録しようと思っていたのにすっかり忘れてしまったので、普通の投稿です(笑)
9
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5