Mở rộng mảng để loại bỏ đối tượng theo giá trị


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

Tuy nhiên, tôi gặp lỗi var index = find(self, object)

'T' không thể chuyển đổi thành 'T'

Tôi cũng đã thử với chữ ký phương thức này: func removeObject(object: AnyObject)tuy nhiên, tôi cũng gặp lỗi tương tự:

'AnyObject' không thể chuyển đổi thành 'T'

Cách thích hợp để làm điều này là gì?


Hãy thử loại bỏ T wherekhỏi khai báo phương pháp của bạn. Vì vậy, chỉ cần func removeObject<T: Equatable>. Câu hỏi này có liên quan: stackoverflow.com/questions/24091046/ từ
ahruss 24/07/14

Câu trả lời:


165

Kể từ Swift 2 , điều này có thể đạt được bằng phương pháp mở rộng giao thức . removeObject()được định nghĩa là một phương thức trên tất cả các loại tuân thủ RangeReplaceableCollectionType(cụ thể là trên Array) nếu các phần tử của bộ sưu tập là 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)
        }
    }
}

Thí dụ:

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

Cập nhật cho Swift 2 / Xcode 7 beta 2: Như Airspeed Velocity nhận thấy trong các nhận xét, giờ đây thực sự có thể viết một phương thức trên một loại chung hạn chế hơn trên mẫu, vì vậy phương thức này thực sự có thể được định nghĩa là một phần mở rộng của Array:

extension Array where Element : Equatable {

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

Phần mở rộng giao thức vẫn có lợi thế là có thể áp dụng cho một tập hợp các loại lớn hơn.

Cập nhật cho 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
Hoàn hảo, bạn phải yêu Swift (2). Tôi thực sự thích làm thế nào với thời gian nhiều thứ có thể hơn và mọi thứ trở nên đơn giản
Kametrixom

1
Điểm hay, theo nhiều cách, thực tế là câu trả lời vẫn đúng về mặt kỹ thuật, không còn thành ngữ, thậm chí còn tệ hơn - mọi người sẽ đến, đọc câu trả lời, nghĩ rằng một chức năng miễn phí là cách giải quyết đúng đắn vì đó là câu trả lời được đánh giá cao . Kịch bản khá xấu xí. Sẽ đăng lên meta.
Vận tốc không khí

1
@AirspeedVelocity: Wow, tôi đã bỏ lỡ điều đó. Có phải nó được bao phủ trong các ghi chú phát hành?
Martin R

1
Nếu bạn muốn có chức năng tương tự như ObjC (nghĩa là loại bỏ tất cả các đối tượng phù hợp thay vì chỉ có đối tượng thứ nhất), bạn có thể thay đổi "nếu" thành "trong khi"
powertoold

2
Phiên bản Swift 3 rất tuyệt, nhưng tôi sẽ đổi tên một chút remove(object: Element)để tuân thủ các nguyên tắc thiết kế API Swift và tránh tính dài dòng. Tôi đã gửi một chỉnh sửa phản ánh điều này.
mã nhanh chóng

66

Bạn không thể viết một phương thức trên một loại chung hạn chế hơn trên mẫu.

LƯU Ý : như của Swift 2.0, bây giờ bạn có thể viết phương pháp mà hạn chế hơn trên mẫu. Nếu bạn đã nâng cấp mã của mình lên 2.0, hãy xem các câu trả lời khác để biết thêm các tùy chọn mới để thực hiện điều này bằng cách sử dụng các tiện ích mở rộng.

Lý do bạn gặp lỗi 'T' is not convertible to 'T'là vì bạn thực sự đang xác định một T mới trong phương thức của bạn hoàn toàn không liên quan đến T. ban đầu. Nếu bạn muốn sử dụng T trong phương thức của mình, bạn có thể làm như vậy mà không chỉ định nó trên phương thức của mình.

Lý do bạn nhận được lỗi thứ hai 'AnyObject' is not convertible to 'T'là tất cả các giá trị có thể có cho T không phải là tất cả các lớp. Đối với một thể hiện được chuyển đổi thành AnyObject, nó phải là một lớp (nó không thể là một cấu trúc, enum, v.v.).

Đặt cược tốt nhất của bạn là làm cho nó trở thành một hàm chấp nhận mảng làm đối số:

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

Hoặc thay vì sửa đổi mảng ban đầu, bạn có thể làm cho phương thức của mình nhiều luồng an toàn hơn và có thể sử dụng lại bằng cách trả về một bản sao:

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

Để thay thế mà tôi không khuyến nghị, bạn có thể âm thầm thất bại nếu loại được lưu trữ trong mảng có thể được chuyển đổi sang mẫu phương thức (có thể tương đương). (Để rõ ràng, tôi đang sử dụng U thay vì T cho mẫu của phương thức):

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]

Chỉnh sửa Để vượt qua thất bại thầm lặng, bạn có thể trả lại thành công dưới dạng bool:

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

Kiểm tra câu trả lời của tôi ở đây: stackoverflow.com/a/24939242/458960 Tại sao tôi có thể làm theo cách này và không sử dụng findphương pháp?
tuyết

Phương pháp của bạn dễ bị sự cố thời gian chạy. Với chức năng của tôi, trình biên dịch sẽ ngăn chặn điều đó xảy ra.
vẽ

1
@Isuru Phương thức này hoạt động với bất kỳ đối tượng nào thực hiện Equatablegiao thức. UIView làm như vậy, vâng, nó sẽ hoạt động với UIViews
drewag

4
Wow, viết một vòng lặp for để loại bỏ một yếu tố, trở lại những năm 90!
Zorayr

5
Trong swift mới nhất. enumerate(self)phải sửa thànhself.enumerate()
TomSawyer

28

ngắn gọn và súc tích:

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

2
Điều này thật tuyệt Tất nhiên nó có thể được thực hiện mà không có inout, quá. Ngay cả với inoutnguyên vẹn, người ta có thể sử dụng array = array.filter() { $0 != object }, tôi nghĩ.
Dan Rosenstark

11
Hãy nhận biết về việc sử dụng lực lượng chỉ số không được bao bọc, có thể là con số không. Thay đổi thành "if let ind = index {Array.removeAtIndex (ind)}"
HotJard

17

Sau khi đọc tất cả những điều trên, tôi nghĩ câu trả lời hay nhất là:

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

Mẫu vật:

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

Phần mở rộng mảng Swift 2 (xcode 7b4):

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

Mẫu vật:

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

Cập nhật Swift 3.1

Đã trở lại với điều này khi Swift 3.1 ra mắt. Dưới đây là một phần mở rộng cung cấp các biến thể toàn diện, nhanh chóng, đột biến và tạo ra.

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

Mẫu:

// 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"]

Điều này không trả về một thể hiện hoàn toàn mới của mảng?
pxpgraphics 18/03/2016

Đúng. Đó là một phong cách chức năng hơn. YMMV.
tháng 12

Tôi có xu hướng đồng ý với kiểu dáng chức năng, ngoại trừ, trong trường hợp này, khi filterchức năng đã xử lý chức năng đó cho bạn. Điều này dường như trùng lặp chức năng. Nhưng dù sao cũng là một câu trả lời hay:]
pxpgraphics 20/03/2016

13

Với các phần mở rộng giao thức, bạn có thể làm điều này,

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

Chức năng tương tự cho các lớp,

Swift 2

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

Swift 3

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

Nhưng nếu một lớp thực hiện Equitable thì nó trở nên mơ hồ và trình biên dịch sẽ đưa ra một lỗi.


1
Tôi đang nhận được một Binary operator '===' cannot be applied to two elements of type '_' and 'Element'
chiếc giày

6

Với việc sử dụng các phần mở rộng giao thức trong 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

Còn việc sử dụng lọc thì sao? sau đây hoạt động khá tốt ngay cả với [AnyObject].

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

}

2

Có một khả năng khác để loại bỏ một mục khỏi một mảng mà không có khả năng sử dụng không an toàn, vì loại chung của đối tượng cần loại bỏ không thể giống như loại của mảng. Sử dụng tùy chọn cũng không phải là cách hoàn hảo để đi vì chúng rất chậm. Do đó, bạn có thể sử dụng một bao đóng giống như nó đã được sử dụng khi sắp xếp một mảng chẳng hạn.

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

Khi bạn mở rộng Arraylớp với chức năng này, bạn có thể loại bỏ các phần tử bằng cách thực hiện như sau:

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

Tuy nhiên, bạn thậm chí có thể xóa một phần tử chỉ khi nó có cùng địa chỉ bộ nhớ (tất nhiên chỉ dành cho các lớp tuân thủ AnyObjectgiao thức):

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'

Điều tốt là, bạn có thể chỉ định tham số để so sánh. Ví dụ: khi bạn có một mảng các mảng, bạn có thể chỉ định bao đóng đẳng thức { $0.count == $1.count }và mảng đầu tiên có cùng kích thước với mảng cần loại bỏ sẽ bị xóa khỏi mảng.

Bạn thậm chí có thể rút ngắn cuộc gọi hàm bằng cách có chức năng như mutating func removeFirst(equality: (Element) -> Bool) -> Bool, sau đó thay thế if-Assessment bằng equality(item)và gọi hàm bằng array.removeFirst({ $0 == "Banana" })ví dụ.


==là một hàm, bạn cũng có thể gọi nó như thế này cho bất kỳ loại nào thực hiện ==(như String, Int, v.v.):array.removeFirst("Banana", equality:==)
Aviel Gross

@AvielGross điều này là mới trong Swift 2 Tôi nghĩ - hãy chỉnh sửa câu trả lời cho phù hợp nếu bạn muốn
borchero

2

Không cần gia hạn:

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

Sử dụng indexOfthay vì forhoặc 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

Có lẽ tôi đã không hiểu câu hỏi.

Tại sao điều này sẽ không làm việc?

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

Cuối cùng tôi đã kết thúc với mã sau đây.

extension Array where Element: Equatable {

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

}

0

Tôi đã quản lý để loại bỏ một [String:AnyObject]mảng [[String:AnyObject]]bằng cách thực hiện một số đếm bên ngoài vòng lặp for để thể hiện chỉ mục kể từ đó .find.filterkhông tương thích với [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

Triển khai trong 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

Tôi đã có thể làm cho nó hoạt động với:

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

Sự so sánh if(index)không hợp lệ
juanjo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.