값으로 객체를 제거하는 배열 확장


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()따르는 모든 종류의 방법으로 정의된다 RangeReplaceableCollectionType(특히에서의 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 ...
}

프로토콜 확장은 더 큰 유형의 세트에 적용 할 수 있다는 장점이 있습니다.

스위프트 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
완벽, 당신은 스위프트를 사랑해야합니다 (2). 나는 시간이 지남에 따라 더 많은 일이 가능 해지고 물건이 단순화되는 방법을 정말 좋아합니다.
Kametrixom

1
좋은 점은 여러 가지면에서 대답이 여전히 기술적으로 정확하고 더 이상 관용적이지 않다는 사실이 훨씬 더 나빠진다는 것입니다. 사람들이 와서 대답을 읽고, 무료 기능이 높은 평가를받은 답변이므로이를 해결하는 올바른 방법이라고 생각합니다. . 아주 못생긴 시나리오. 메타에 게시합니다.
속도 속도

1
@AirspeedVelocity : 와우, 나는 그것을 놓쳤다. 릴리스 정보에 포함되어 있습니까?
Martin R

1
당신이 ObjC와 동일한 기능을 원하는 경우에, 당신은 "만약" "동안"로 변경할 수 있습니다 (즉, 모든 일치하는 대신 단지 1 하나의 객체를 제거)
powertoold

2
스위프트 3 버전은 매우 중요하지만, 나는 약간 선언의 이름을 바꿀 것 remove(object: Element)을 준수하기 위해 스위프트 API 설계 지침 및 피할 상세. 이를 반영하여 수정 사항을 제출했습니다.
swiftcode

66

템플릿에서 더 제한적인 제네릭 형식에는 메서드를 작성할 수 없습니다.

참고 : 스위프트 2.0로, 당신은 지금 방법을 쓸 수 있는 템플릿에 대한 제한을. 코드를 2.0으로 업그레이드 한 경우 확장을 사용하여이를 구현할 수있는 새로운 옵션에 대해서는 다른 답변을 참조하십시오.

오류 'T' is not convertible to 'T'가 발생하는 이유 는 실제로 것을 정의하고 있기 때문입니다. 원래 T와 전혀 관련이없는 메소드에서 T를 있기 때문입니다. 메소드에서 T를 사용하려는 경우 메소드에서 지정하지 않고 그렇게 할 수 있습니다.

두 번째 오류 'AnyObject' is not convertible to 'T'가 발생하는 이유는 T에 가능한 모든 값이 모든 클래스가 아니기 때문입니다. 인스턴스를 AnyObject로 변환하려면 클래스 여야합니다 (struct, enum 등일 수 없음).

가장 좋은 방법은 배열을 인수로 허용하는 함수로 만드는 것입니다.

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방법을 사용할 수 있습니까?
눈사람

메소드가 런타임 충돌에 취약합니다. 내 기능을 사용하면 컴파일러에서 전혀 발생하지 않습니다.
drewag

1
@Isuru이 메소드는 Equatable프로토콜 을 구현하는 모든 객체에서 작동 합니다. UIView의는 UIViews 작동합니다 그래서 그래 않습니다
drewag

4
와우, for 루프를 작성하여 요소를 제거하고 90 년대로 거슬러 올라갑니다!
Zorayr

5
최신 스위프트에서. enumerate(self)수정해야self.enumerate()
TomSawyer

29

간단하고 간결하게 :

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

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 )

스위프트 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" )

스위프트 3.1 업데이트

이제 스위프트 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

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
나는군요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한 다음 if-evaluation을 바꾸고 예를 들어 equality(item)함수를 호출 할 수도 있습니다 array.removeFirst({ $0 == "Banana" }).


==함수 이므로 ==String, Int 등 을 구현하는 모든 유형에 대해 다음과 같이 호출 할 수도 있습니다 .array.removeFirst("Banana", equality:==)
Aviel Gross

@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

I는 제거 그럭저럭 [String:AnyObject]배열에서 [[String:AnyObject]]보낸 인덱스를 나타 내기위한 루프의 카운트 외부 구현 .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

스위프트 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.