Swiftの配列まわりのAPIをExtensionでパワーアップしてみる

ちょっと気になったので。。。

指定したオブジェクトを配列から除去したい

突然ですが、以下の配列の要素のうち「ありがとうさぎ」だけは要らない子なので取り除こうという話になりました。

var arr = ["こんにちわん", "ありがとうさぎ", "こんばんわに"]

Java とか C# とかに慣れていると、

arr.remove("ありがとうさぎ")  // ※ 動かない

って書けば配列からありがとうさぎが消えそうな予感がしませんか? しますよね。

ところが信じがたいことに、Swift の配列に標準装備されている remove 関連の関数はたったの3つだけ。

  • removeAll … 全消し
  • removeAtIndex … インデックスを指定して削除
  • removeLast … 最後の要素を削除

正攻法では、指定したオブジェクトを配列から取り除くのは大変そうです。

ただ、打つ手が無いわけではなく、以下のように filter を使ってデータをフィルタリングすれば、一応配列からありがとうさぎを消すことができます。

let except = "ありがとうさぎ"
arr = arr.filter({$0 != except})

できますが、できれば Array のメンバ関数っぽく書きたいところです。

arr.remove("ありがとうさぎ") // ←ほんとはこう書きたい

そこで、Swift の Extension と Generics を使って改造してみることにしました。

extension Array {
    mutating func remove<T : Equatable>(obj : T) -> Array {
        self = self.filter({$0 as? T != obj})
        return self;
    }
}

こうすると、以下の記法が有効になります。やったね!

arr.remove("ありがとうさぎ") 
実行結果
こんにちわん
こんばんわに
Program ended with exit code: 0

配列同士の差集合が欲しい

続きまして、以下の配列のうち「たのしい」と「なかま」は不吉な言葉なので取り除きたい場合を考えます。

var arr = ["こんにちわん",
           "ありがとうさぎ",
           "こんばんわに",
           "たのしい",
           "なかま"]

先程のコードに、contains と except 関数を追加してみます。

extension Array {
    mutating func remove<T : Equatable>(obj : T) -> Array {
        self = self.filter({$0 as? T != obj})
        return self;
    }
    
    func contains<T : Equatable>(obj : T) -> Bool {
        return self.filter({$0 as? T == obj}).count > 0
    }
    
    func except<T : Equatable>(obj: [T]) -> [T] {
        var ret = [T]()
        
        for x in self {
            if !obj.contains(x as T) {
                ret += x as T
            }
        }
        return ret
    }
}

すると、配列オブジェクトから except 関数が呼べるようになり、「たのしい」「なかま」がぽぽぽぽーんします(意味不明)。

ただし今回は元の配列に対して破壊的な操作を行わないようにしています。

var arr = ["こんにちわん",
           "ありがとうさぎ",
           "こんばんわに",
           "たのしい",
           "なかま"]

let ex = ["たのしい", "なかま"]

for x in arr.except(ex) {
    println(x)
}
実行結果
こんにちわん
ありがとうさぎ
こんばんわに
Program ended with exit code: 0

余談:破壊的操作でよければ

こうなる。

    mutating func except<T : Equatable>(obj : [T]) -> Array {
        for x in obj {
            self.remove(x)
        }
        return self
    }

配列同士の共通要素(積集合)を求める

2つの配列の共通要素だけを抜き出します。

extension Array {

    /* 略 */
    
    func intersect<T : Equatable>(obj : [T]) -> [T] {
        var ret = [T]()
        
        for x in self {
            if obj.contains(x as T) {
                ret += x as T
            }
        }
        return ret
    }
}


var arr1 = ["アンパンマン", "ばいきんまん", "カレーパンマン", "チーズ"]
var arr2 = ["ジャムおじさん", "チーズ", "バタ子さん", "ばいきんまん"]

for x in arr1.intersect(arr2) {
    println(x)
}
実行結果
ばいきんまん
チーズ
Program ended with exit code: 0

今日はもうつかれたのでこのへんで。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.