0

When I want to pass through and remove an item or items from an array (when certain conditions are met), I typically iterate backward in the C-style for-loop and remove the item by index, avoiding the problem of index numbers being changed of the next item to be processed, or the changing size of the list affecting how many times the loop is passed through. But the C for-loop has been removed in Swift 3.

Here is my Swift 2.3 code for the initialization of the loop:

for (var i = allowedItems.count - 1;  i > -1;  i -= 1)

Here is the monstrosity created by the Swift 3 converter:

for (i in ((-1 + 1)...allowedItems.count - 1).reversed())

This version does not compile however. ("Expected ',' separator" at the "in" operator).

I simplify the "-1 + 1" bit to zero:

for (i in (0...allowedItems.count - 1).reversed())

Now the error is "Expected Sequence expression for for-each loop".

What is the safe and hopefully reasonably elegant way of iterating backward in Swift 3, in which an index or counter variable is made available for use in specifying which item should be removed? This type of logic appears a number of places in my code so I want to make sure to find the best solution.

Thanks.

Casey Perkins
  • 1,853
  • 2
  • 23
  • 41

3 Answers3

3

What is the safe and hopefully reasonably elegant way of iterating backward in Swift 3

The built-in way is:

for i in (0 ..< allowedItems.count).reversed()

The elegant way is:

for i in allowedItems.count >>> 0

(where >>> is the custom operator that I define here).

Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141
2

Use stride:

for i in stride(from: allowedItems.count - 1, through: 0, by: -1) {

}
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • The answers from Code Different, matt, and the comment from dasblinkenlight all appear to viable ways of accomplishing this. I upvoted them all, but marked this as the answer because it satisfies (to me at least) the criteria of being elegant and easy to understand also. – Casey Perkins Nov 22 '16 at 17:14
2

What is the safe and hopefully reasonably elegant way of iterating backward in Swift 3, in which an index or counter variable is made available for use in specifying which item should be removed?

This doesn't answer the technical question, but possibly the underlying XY problem: have you considered simply filtering your array based on the criteria "when certain conditions are met"?

func certainConditionsForKeepingAreMet(_ element: YourElementType) -> Bool { /* ... */ }
allowedItems = allowedItems.filter(certainConditionsForKeepingAreMet)

E.g.

var allowedItems = [1, 3 ,6, 2]
func certainConditionsForKeepingAreMet(_ element: Int) -> Bool { return element < 3 }
allowedItems = allowedItems.filter(certainConditionsForKeepingAreMet)
print(allowedItems) // [1, 2]

If you'd like to remove and use the removed elements (on-the-fly), you could simply pipe the elements that are to be removed to some "use this element" function, in the course of checking the conditions for the elements.

func doSomethingWith(_ element: Int) { print("Removed", element) }

func certainConditionsForKeepingAreMet(_ element: Int) -> Bool {
    if element >= 3 {
        doSomethingWith(element)
        return false
    }
    return true
}

var allowedItems = [1, 3 ,6, 2]
allowedItems = allowedItems.filter(certainConditionsForKeepingAreMet)
/* Removed 3
   Removed 6 */
print(allowedItems) // [1, 2]
dfrib
  • 70,367
  • 12
  • 127
  • 192