値によってオブジェクトを削除する配列拡張


140
extension Array {
    func removeObject<T where T : Equatable>(object: T) {
        var index = find(self, object)
        self.removeAtIndex(index)
    }
}

しかし、私はエラーになります var index = find(self, object)

「T」は「T」に変換できません

このメソッドシグネチャも試してみましたfunc removeObject(object: AnyObject)が、同じエラーが発生します。

「AnyObject」は「T」に変換できません

これを行う適切な方法は何ですか?


T whereメソッド宣言からを削除してみてください。だからちょうどfunc removeObject<T: Equatable>。この質問は関連していますstackoverflow.com/questions/24091046/...
ahruss

回答:


165

以下のようスイフト2、これを用いて達成することができるプロトコル拡張方法。 コレクションの要素が次の場合、(特に)にremoveObject()準拠するすべての型のメソッドとして定義されます。RangeReplaceableCollectionTypeArrayEquatable

extension RangeReplaceableCollectionType where Generator.Element : Equatable {

    // Remove first collection element that is equal to the given `object`:
    mutating func removeObject(object : Generator.Element) {
        if let index = self.indexOf(object) {
            self.removeAtIndex(index)
        }
    }
}

例:

var ar = [1, 2, 3, 2]
ar.removeObject(2)
print(ar) // [1, 3, 2]

Swift 2 / Xcode 7ベータ2のアップデート Airspeed Velocityがコメントで気づいたように、テンプレートでより制限的なジェネリック型にメソッドを書き込むことが可能になり、メソッドは実際に拡張として定義できるようになりましたのArray

extension Array where Element : Equatable {

    // ... same method as above ...
}

プロトコル拡張には、より大きなタイプのセットに適用できるという利点があります。

Swift 3の更新

extension Array where Element: Equatable {

    // Remove first collection element that is equal to the given `object`:
    mutating func remove(object: Element) {
        if let index = index(of: object) {
            remove(at: index)
        }
    }
}

1
Swift(2)が大好きです。時間が経つにつれ、より多くのことが可能になり、物事が簡素化される方法が本当に好きです
Kametrixom

1
良い点は、多くの点で、答えがまだ技術的に正しいが、もはや慣用的ではないという事実はさらに悪いことです-人々は来て、答えを読み、無料の関数は高い評価の答えであるため、それを解決する正しい方法だと思います。かなり醜いシナリオ。メタに投稿します。
対気速度

1
@AirspeedVelocity:うわー、私はそれを逃した。それはリリースノートでカバーされていますか?
マーティンR

1
ObjCと同じ機能が必要な場合(つまり、最初のものだけでなく、一致するすべてのオブジェクトを削除する場合)、「if」を「while」に変更できます
powertoold

2
Swift 3バージョンは素晴らしいですがremove(object: Element)Swift API設計ガイドラインに準拠し、冗長性を避けるために、宣言の名前を少し変更します。これを反映した編集を提出しました。
16:07のswiftcode

66

テンプレートでより制限的なジェネリック型にメソッドを記述することはできません。

:Swift 2.0以降、テンプレートに対してより制限的なメソッドを記述できるようなりました。コードを2.0にアップグレードした場合は、拡張機能を使用してこれを実装するための新しいオプションについて、さらに下の回答を参照してください。

エラー'T' is not convertible to 'T'が発生する理由は、元のTとはまったく関係のない新しい Tをメソッドで実際に定義しているためです。メソッドでTを使用したい場合は、メソッドで指定せずにTを使用できます。

2番目のエラー'AnyObject' is not convertible to 'T'が発生する理由は、Tのすべての可能な値がすべてのクラスではないためです。インスタンスをAnyObjectに変換するには、クラスである必要があります(構造体、列挙型などにすることはできません)。

あなたの最善の策は、配列を引数として受け入れる関数にすることです:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) {
}

または、元の配列を変更する代わりに、コピーを返すことで、メソッドをよりスレッドセーフで再利用可能にすることができます。

func arrayRemovingObject<T : Equatable>(object: T, fromArray array: [T]) -> [T] {
}

私がお勧めしない代替案として、配列に格納されている型をメソッドテンプレートに変換できない場合(つまり、等値である場合)に、メソッドを警告なしに失敗させることができます。(明確にするために、メソッドのテンプレートにはTではなくUを使用しています):

extension Array {
    mutating func removeObject<U: Equatable>(object: U) {
        var index: Int?
        for (idx, objectToCompare) in enumerate(self) {
            if let to = objectToCompare as? U {
                if object == to {
                    index = idx
                }
            }
        }

        if(index != nil) {
            self.removeAtIndex(index!)
        }
    }
}

var list = [1,2,3]
list.removeObject(2) // Successfully removes 2 because types matched
list.removeObject("3") // fails silently to remove anything because the types don't match
list // [1, 3]

編集サイレントな失敗を克服するには、成功をブール値として返すことができます。

extension Array {
  mutating func removeObject<U: Equatable>(object: U) -> Bool {
    for (idx, objectToCompare) in self.enumerate() {  //in old swift use enumerate(self) 
      if let to = objectToCompare as? U {
        if object == to {
          self.removeAtIndex(idx)
          return true
        }
      }
    }
    return false
  }
}
var list = [1,2,3,2]
list.removeObject(2)
list
list.removeObject(2)
list

ここで私の答えを確認してください:stackoverflow.com/a/24939242/458960なぜこのようにしてfindメソッドを使用しないのですか?
スノーマン2014

メソッドはランタイムクラッシュの影響を受けます。私の機能では、コンパイラーはそれがまったく起こらないようにします。
drewag 14

1
@Isuruこのメソッドは、Equatableプロトコルを実装するすべてのオブジェクトで機能します。UIViewはそうするので、UIViewsで動作します
drewag

4
わあ、要素を削除するためのforループを書いて、90年代に戻りましょう。
Zorayr、2015年

5
最新の迅速に。enumerate(self)修正する必要があるself.enumerate()
TomSawyer、2015年

28

簡潔かつ簡潔に:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) 
{
    var index = find(array, object)
    array.removeAtIndex(index!)
}

2
これはカッコいい。もちろんinout、なしでも実行できます。そのinoutままでも使用できるarray = array.filter() { $0 != object }と思います。
Dan Rosenstark、2015年

11
強制アンラップされたインデックスを使用することに注意してください。「if let ind = index {array.removeAtIndex(ind)}」に変更
HotJard

17

上記のすべてを読んだ後、私の心には最良の答えは:

func arrayRemovingObject<U: Equatable>(object: U, # fromArray:[U]) -> [U] {
  return fromArray.filter { return $0 != object }
}

サンプル:

var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = arrayRemovingObject("Cat", fromArray:myArray )

Swift 2(xcode 7b4)配列拡張:

extension Array where Element: Equatable {  
  func arrayRemovingObject(object: Element) -> [Element] {  
    return filter { $0 != object }  
  }  
}  

サンプル:

var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = myArray.arrayRemovingObject("Cat" )

Swift 3.1アップデート

Swift 3.1がリリースされた今、これに戻ってきました。以下は、網羅的で高速な変異と作成バリアントを提供する拡張機能です。

extension Array where Element:Equatable {
    public mutating func remove(_ item:Element ) {
        var index = 0
        while index < self.count {
            if self[index] == item {
                self.remove(at: index)
            } else {
                index += 1
            }
        }
    }

    public func array( removing item:Element ) -> [Element] {
        var result = self
        result.remove( item )
        return result
    }
}

サンプル:

// Mutation...
      var array1 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
      array1.remove("Cat")
      print(array1) //  ["Dog", "Turtle", "Socks"]

// Creation...
      let array2 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
      let array3 = array2.array(removing:"Cat")
      print(array3) // ["Dog", "Turtle", "Fish"]

これは配列の完全に新しいインスタンスを返しませんか?
pxpgraphics

はい。より機能的なスタイルです。YMMV。
12月

この場合、filter関数が既にその機能を処理している場合を除いて、私は関数のスタイル設定に同意する傾向があります。これは機能が重複しているようです。しかし、それでもよい答えは次のとおりです。]
pxpgraphics 2016年

13

プロトコル拡張を使用すると、これを行うことができます。

extension Array where Element: Equatable {
    mutating func remove(object: Element) {
        if let index = indexOf({ $0 == object }) {
            removeAtIndex(index)
        }
    }
}

クラスと同じ機能、

スウィフト2

extension Array where Element: AnyObject {
    mutating func remove(object: Element) {
        if let index = indexOf({ $0 === object }) {
            removeAtIndex(index)
        }
    }
}

スウィフト3

extension Array where Element: AnyObject {
    mutating func remove(object: Element) {
        if let index = index(where: { $0 === object }) {
             remove(at: index)
        }
    }
}

しかし、クラスがEquatableを実装している場合は、あいまいになり、コンパイラーがエラーをスローします。


1
私はaを手に入れましたBinary operator '===' cannot be applied to two elements of type '_' and 'Element'

6

Swift 2.0でプロトコル拡張を使用する

extension _ArrayType where Generator.Element : Equatable{
    mutating func removeObject(object : Self.Generator.Element) {
        while let index = self.indexOf(object){
            self.removeAtIndex(index)
        }
    }
}

4

フィルタリングを使用するにはどうすればよいですか?以下は[AnyObject]でも非常にうまく機能します。

import Foundation
extension Array {
    mutating func removeObject<T where T : Equatable>(obj: T) {
        self = self.filter({$0 as? T != obj})
    }

}

2

削除するオブジェクトのジェネリック型を配列の型と同じにすることはできないため、危険な使用をすることなく、配列から項目を削除する可能性があります。オプションは非常に遅いため、オプションを使用することも最適な方法ではありません。したがって、たとえば配列のソート時にすでに使用されているようなクロージャーを使用できます。

//removes the first item that is equal to the specified element
mutating func removeFirst(element: Element, equality: (Element, Element) -> Bool) -> Bool {
    for (index, item) in enumerate(self) {
        if equality(item, element) {
            self.removeAtIndex(index)
            return true
        }
    }
    return false
}

Arrayこの関数を使用してクラスを拡張する場合、次のようにして要素を削除できます。

var array = ["Apple", "Banana", "Strawberry"]
array.removeFirst("Banana") { $0 == $1 } //Banana is now removed

ただし、要素が同じメモリアドレスを持つ場合にのみ要素を削除することもできます(AnyObjectもちろん、プロトコルに準拠したクラスの場合のみ)。

let date1 = NSDate()
let date2 = NSDate()
var array = [date1, date2]
array.removeFirst(NSDate()) { $0 === $1 } //won't do anything
array.removeFirst(date1) { $0 === $1 } //array now contains only 'date2'

良い点は、比較するパラメータを指定できることです。たとえば、配列の配列がある場合、等値クロージャーをとして指定でき、{ $0.count == $1.count }削除する配列と同じサイズの最初の配列が配列から削除されます。

あなたも、としての機能を有することにより、関数呼び出しを短縮することができmutating func removeFirst(equality: (Element) -> Bool) -> Bool、その後であれば評価を置き換え、equality(item)そしてによって関数を呼び出すarray.removeFirst({ $0 == "Banana" })例。


以来==関数であり、あなたも実装していること、あらゆるタイプのために、このようにそれを呼び出すことができます==:(などの文字列、のIntのような)array.removeFirst("Banana", equality:==)
Avielグロス

@AvielGrossこれはSwift 2の新機能だと思います-必要に応じて自由に回答を編集してください
borchero

2

拡張する必要はありません:

var ra = [7, 2, 5, 5, 4, 5, 3, 4, 2]

print(ra)                           // [7, 2, 5, 5, 4, 5, 3, 4, 2]

ra.removeAll(where: { $0 == 5 })

print(ra)                           // [7, 2, 4, 3, 4, 2]

if let i = ra.firstIndex(of: 4) {
    ra.remove(at: i)
}

print(ra)                           // [7, 2, 3, 4, 2]

if let j = ra.lastIndex(of: 2) {
    ra.remove(at: j)
}

print(ra)                           // [7, 2, 3, 4]

1

またはのindexOf代わりに使用:forenumerate

extension Array where Element: Equatable {

   mutating func removeElement(element: Element) -> Element? {
      if let index = indexOf(element) {
         return removeAtIndex(index)
      }
      return nil
   }

   mutating func removeAllOccurrencesOfElement(element: Element) -> Int {
       var occurrences = 0
       while true {
          if let index = indexOf(element) {
             removeAtIndex(index)
             occurrences++
          } else {
             return occurrences
          }
       }
   }   
}

1

多分私は質問を理解しませんでした。

なぜこれはうまくいかないのですか?

import Foundation
extension Array where Element: Equatable {
    mutating func removeObject(object: Element) {
        if let index = self.firstIndex(of: object) {
            self.remove(at: index)
        }
    }
}

var testArray = [1,2,3,4,5,6,7,8,9,0]
testArray.removeObject(object: 6)
let newArray = testArray

var testArray2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
testArray2.removeObject(object: "6")
let newArray2 = testArray2

0

私は最終的に次のコードで終わった。

extension Array where Element: Equatable {

    mutating func remove<Element: Equatable>(item: Element) -> Array {
        self = self.filter { $0 as? Element != item }
        return self
    }

}

0

私は削除する管理[String:AnyObject]アレイの[[String:AnyObject]]ためのインデックスを表すためにforループのカウント外側を実装することによって、.findおよび.filter互換性がありません[String:AnyObject]

let additionValue = productHarvestChoices[trueIndex]["name"] as! String
var count = 0
for productHarvestChoice in productHarvestChoices {
  if productHarvestChoice["name"] as! String == additionValue {
    productHarvestChoices.removeAtIndex(count)
  }
  count = count + 1
}

-1

Swift 2での実装:

extension Array {
  mutating func removeObject<T: Equatable>(object: T) -> Bool {
    var index: Int?
    for (idx, objectToCompare) in self.enumerate() {
      if let toCompare = objectToCompare as? T {
        if toCompare == object {
          index = idx
          break
        }
      }
    }
    if(index != nil) {
      self.removeAtIndex(index!)
      return true
    } else {
      return false
    }
  }
}

-4

私はそれを動作させることができました:

extension Array {
    mutating func removeObject<T: Equatable>(object: T) {
        var index: Int?
        for (idx, objectToCompare) in enumerate(self) {
            let to = objectToCompare as T
            if object == to {
                index = idx
            }
        }

        if(index) {
            self.removeAtIndex(index!)
        }
    }
}

比較if(index)が無効です
juanjo 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.