Removing from Array During Enumeration in Swift

How do I remove a value from swift array of enums

Enums with associated values aren't implicitly marked Equatable. You have to request it. But that's all you have to do if the associated values are themselves Equatable (as of Swift 4.2 I believe).

Change:

enum Test

to:

enum Test: Equatable

(Note: enums are types and should always be capitalized)

Remove enum from array of enums regardless of its argument

This is currently impossible.

What you are trying to do is essentially passing an enumeration case pattern as an argument to a method, so that that method can match each value in the array against that pattern. However, the swift guide says that:

An enumeration case pattern matches a case of an existing enumeration type. Enumeration case patterns appear in switch statement case labels and in the case conditions of if, while, guard, and for-in statements.

This means that enumeration case patterns are not allowed as arguments to functions. :(

So the best you can do is this:

array.filter {
if case .enableNotifications = $0 { return false } else { return true }
}

Swift how to remove elements from an array of enums?

I think the best way to check the type of the last element of the array is to cast it using an if var ...

if var more = model.myArray.last as? More {

and then change it and replace the old value in the array

if var more = myArray.last as? More, let array = more.peopleIds {
more.peopleIds = Array(array.dropFirst(100))
myArray[myArray.endIndex - 1] = more
}

Safe and elegant way to delete element in custom array, Swift

if you want find a specific item index you can use firstIndex(of: )

like this:

let index = customArray.firstIndex(of: CustomItem)
customArray.remove(at: index)

EDIT:

You have to make your object Equatable with an extension:

enter codextension CustomItem: Equatable {
static func ==(lhs: CustomItem, rhs: CustomItem) -> Bool {
return lhs.firstItem == rhs.firstItem
} }

Now you can compare the objects with each other.
this is my code from playground:

enum Enumeration {
case opt1
case op2
}

struct CustomItem {
var firstItem: Enumeration
let data: Any
}

extension CustomItem: Equatable {
static func ==(lhs: CustomItem, rhs: CustomItem) -> Bool {
return lhs.firstItem == rhs.firstItem
}
}

class ViewController: UIViewController {
var customArray = [CustomItem]()

func searchAndDestory() {
let index = customArray.firstIndex(of: CustomItem.init(firstItem: .op2, data: 0)) ?? 0
customArray.remove(at: index)
}
}

Swift: Better way to remove a specific Object from an array?

Use firstIndex(where:) (previously called index(where:) in Swift 4.1 and earlier) to search the array for your object using the predicate { $0 === objectToRemove }, then call remove(at:) on the array to remove it:

if let idx = objectArray.firstIndex(where: { $0 === objectToRemove }) {
objectArray.remove(at: idx)
}

This allows you to search for your object whether it is Equatable or not.

Remove element from collection during iteration with forEach

This is indeed expected behaviour – and is due to the fact that an Array in Swift (as well as many other collections in the standard library) is a value type with copy-on-write semantics. This means that its underlying buffer (which is stored indirectly) will be copied upon being mutated (and, as an optimisation, only when it's not uniquely referenced).

When you come to iterate over a Sequence (such as an array), be it with forEach(_:) or a standard for in loop, an iterator is created from the sequence's makeIterator() method, and its next() method is repeatedly applied in order to sequentially generate elements.

You can think of iterating over a sequence as looking like this:

let sequence = [1, 2, 3, 4]
var iterator = sequence.makeIterator()

// `next()` will return the next element, or `nil` if
// it has reached the end sequence.
while let element = iterator.next() {
// do something with the element
}

In the case of Array, an IndexingIterator is used as its iterator – which will iterate through the elements of a given collection by simply storing that collection along with the current index of the iteration. Each time next() is called, the base collection is subscripted with the index, which is then incremented, until it reaches endIndex (you can see its exact implementation here).

Therefore, when you come to mutate your array in the loop, its underlying buffer is not uniquely referenced, as the iterator also has a view onto it. This forces a copy of the buffer – which myCollection then uses.

So, there are now two arrays – the one which is being iterated over, and the one you're mutating. Any further mutations in the loop won't trigger another copy, as long as myCollection's buffer remains uniquely referenced.

This therefore means that it's perfectly safe to mutate a collection with value semantics while enumerating over it. The enumeration will iterate over the full length of the collection – completely independant of any mutations you do, as they will be done on a copy.

How to remove item in Array?

You are removing item at the same time when you are enumerating same array. Use filter instead:

var array: [Int] = [0,1,2,3,4,5]
array = array.filter{$0 != 2}

or, for multiple values, use Set:

let unwantedValues: Set<Int> = [2, 4, 5]
array = array.filter{!unwantedValues.contains($0)}

Same in one line:

array = array.filter{!Set([2, 4, 5]).contains($0)}

Remove duplicates from array of Int in Swift (for-in-loop)

The problematic part is to iterate over an array and update that array at the same time. In this case, removing an element while iterating.

Removing an element decreases array length (count) and also changes indices. Therefore in

for j in i + 1 ..< array.count  {
if array[i] == array[j] {
newArray.remove(at: j)
}
}

After removing the first index, your other indices become invalid. Note that count is always read only once, before the actual iteration.

This is one of the reasons why removing elements during iteration is dangerous and complicated. You could fix it by maintaining the number of removed elements and update indices accordingly. Or you can iterate backwards:

var newArray = array

for i in (0 ..< newArray.count - 1).reversed() {
for j in (i + 1 ..< newArray.count).reversed() {
if newArray[i] == newArray[j] {
newArray.remove(at: j)
}
}
}
return newArray

You are still changing indices and count but since you are iterating backwards, you only change the indices that have been already used.

In general it's simple and safer to build a new array instead of updating the current one:

var newArray: [Int] = []

for value in array {
if !newArray.contains(value) {
newArray.append(value)
}
}

return newArray

which can be very reduced in complexity (performance) by using a Set to keep added elements:

var newArray: [Int] = []
var foundElements: Set<Int> = []

for value in array {
if foundElements.insert(value).inserted {
newArray.append(value)
}
}

return newArray

Which can be simplified using filter:

var foundElements: Set<Int> = []
return array.filter { foundElements.insert($0).inserted }


Related Topics



Leave a reply



Submit