web-dev-qa-db-ja.com

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

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」に変換できません

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

140
Snowman

Swift 2の時点で、これはプロトコル拡張メソッドで実現できます。コレクションの要素がRangeReplaceableCollectionTypeの場合、removeObject()は、Array(特にEquatableに準拠)に準拠するすべての型のメソッドとして定義されます。

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)
        }
    }
}
165
Martin R

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

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

エラー'T' is not convertible to 'T'が表示される理由は、元のTとはまったく関係のないメソッドでnew 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
65
drewag

簡潔かつ簡潔に:

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

上記のすべてを読んだ後、私の心に最高の答えは次のとおりです。

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"]
17
rosstulloch

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

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)
        }
    }
}

Swift

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

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

13

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)
        }
    }
}
7
ogantopkaya

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

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

}
4
valvoline

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

//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にすることで関数呼び出しを短縮し、if-evaluationをequality(item)に置き換えて、たとえばarray.removeFirst({ $0 == "Banana" })で関数を呼び出すこともできます。

2
borchero

indexOfまたはforの代わりにenumerateを使用する:

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
juanjo

たぶん私は質問を理解していなかった。

なぜこれが機能しないのですか?

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
1
Chris Marshall

[String:AnyObject]および[[String:AnyObject]].findと互換性がないため、インデックスを表すためにforループの外側にカウントを実装することで、配列.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
}
0

最終的に次のコードになりました。

extension Array where Element: Equatable {

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

}
0
Kaz Yoshikawa