6

Is there a function in the Swift Standard library that acts on a collection, takes a predicate and returns the value removed from that collection?

Currently, I have to implement it in 2 steps:

guard let idx = allAnnotations.index(where: {$0 is MKUserLocation}) else {return}
let userLocation = allAnnotations.remove(at: idx) as! MKUserLocation

But I guess, a similar function exists.

The goal

I have the following array:

[Type1, Type1, Type1, Type1, Type1, Type1, Type2]

Type2 may or may not be present in the array. There are no other types except these two.

I need to split it onto two elements:

[Type1, Type1, Type1, Type1, Type1, Type1]

and

Type2?

That's the function I'm looking for.

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
Richard Topchii
  • 7,075
  • 8
  • 48
  • 115

5 Answers5

4

Swift 5 has removeAll(where)

@inlinable public mutating func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows

You can use it like this -

var array = [1,2,3]
array.removeAll(where: { $0 == 2})
print(array) // [1,3]

Apple Docs

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
shannoga
  • 19,649
  • 20
  • 104
  • 169
  • Thank you for your answer, I'll mark it as accepted since Swift 5 is the mainstream now. My question was written prior to Swift 5. – Richard Topchii Jun 19 '19 at 12:15
2

Here is an extension that returns an array of dropped elements:

extension Array {
    mutating func dropWhere(_ isIncluded: (Element) throws -> Bool) -> [Element] {
        do {
            let reverseArray = try filter { try isIncluded($0) }
            self = try filter { try !isIncluded($0) }

            return reverseArray
        } catch {
            return []
        }
    }
}

Just call it like you would call filter.

var array = [1, 2, 3]
let array2 = array.dropWhere { $0 > 2 }
print(array) //[1, 2]
print(array2) //[3]
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
2

Code I've finished with:

extension Array {
  mutating func popFirstElementWith(_ predicate:(Element)->(Bool)) -> Element? {
    for (idx, element) in enumerated() {
      if predicate(element) {
        remove(at: idx)
        return element
      }
    }
    return nil
  }
}
Richard Topchii
  • 7,075
  • 8
  • 48
  • 115
1

you can use Split on condition will slice your array

    var typeList =  [Type1, Type1, Type1, Type1, Type1, Type1, Type2]

    var slicedArray:[ArraySlice] = typeList.split { (value) -> Bool in
                return value == YourCondition
            }
print(slicedArray)
Abdelahad Darwish
  • 5,969
  • 1
  • 17
  • 35
1

This extension takes a predicate and returns the element if it matches the predicate. Also, removes the element from the array.

extension Array {
    mutating func popFirstElementWith(_ predicate:(Element)->(Bool)) -> Element? {
        var firstElement:Element?
        self = self.compactMap({ (element:Element) -> Element? in
            guard predicate(element) == true else {
                return element
            }
            if firstElement == nil {
                firstElement = element
            }
            return nil
        })
        return firstElement
    }
}

TEST:

var array:[Any] = ["a", "b", 1, "c"]
let element = array.popFirstElementWith { (element) -> (Bool) in
    return element is Int
}
print(element!)
print(array)

Output:

1
["a", "b", "c"]
Puneet Sharma
  • 9,369
  • 1
  • 27
  • 33