For-In Loops multiple conditions
Asked Answered
Q

5

9

With the new update to Xcode 7.3, a lot of issues appeared related with the new version of Swift 3. One of them says "C-style for statement is deprecated and will be removed in a future version of Swift" (this appears in traditional for statements).

One of this loops has more than one condition:

for i = 0; i < 5 && i < products.count; i += 1 {

}

My question is, is there any elegant way (not use break) to include this double condition in a for-in loop of Swift:

for i in 0 ..< 5 {

}
Queenhood answered 24/3, 2016 at 16:35 Comment(0)
S
17

It would be just as you're saying if you described it out loud:

for i in 0 ..< min(5, products.count) { ... }

That said, I suspect you really mean:

for product in products.prefix(5) { ... }

which is less error-prone than anything that requires subscripting.

It's possible you actually need an integer index (though this is rare), in which case you mean:

for (index, product) in products.enumerate().prefix(5) { ... }

Or you could even get a real index if you wanted with:

for (index, product) in zip(products.indices, products).prefix(5) { ... }
Shamblin answered 24/3, 2016 at 16:36 Comment(3)
Your answer demonstrates perfectly why Apple is removing the C-style for statement: because there are other, way more Swifty ways to accomplish the same thing, and often people only use the old style for statement to iterate an array anyway.Situated
Very complete answer! Thanks! with the two first was enough for me. About the two last answer (which both have compile errors): in the third answer, enumerate is a method and in the fourth answer "Value of type Range<DctionaryIndex<String, String>> has no member zip", so this method seems that doesn't exist for this type. Thanks you very much!Queenhood
Fixed the syntax. Thanks for noting it.Shamblin
M
23

You can use && operator with where condition like

let arr = [1,2,3,4,5,6,7,8,9]

for i in 1...arr.count where i < 5  {
    print(i)
}
//output:- 1 2 3 4

for i in 1...100 where i > 40 && i < 50 && (i % 2 == 0) {
     print(i)
}
//output:- 42 44 46 48
Mcnalley answered 22/6, 2016 at 10:31 Comment(2)
Is it possible to add optional binding after the where?Soccer
@Soccer I don't think so we can use let statement here, however, we can use != nil here or If you are trying to check class type then you can use is as well like object is UIButtonMcnalley
S
17

It would be just as you're saying if you described it out loud:

for i in 0 ..< min(5, products.count) { ... }

That said, I suspect you really mean:

for product in products.prefix(5) { ... }

which is less error-prone than anything that requires subscripting.

It's possible you actually need an integer index (though this is rare), in which case you mean:

for (index, product) in products.enumerate().prefix(5) { ... }

Or you could even get a real index if you wanted with:

for (index, product) in zip(products.indices, products).prefix(5) { ... }
Shamblin answered 24/3, 2016 at 16:36 Comment(3)
Your answer demonstrates perfectly why Apple is removing the C-style for statement: because there are other, way more Swifty ways to accomplish the same thing, and often people only use the old style for statement to iterate an array anyway.Situated
Very complete answer! Thanks! with the two first was enough for me. About the two last answer (which both have compile errors): in the third answer, enumerate is a method and in the fourth answer "Value of type Range<DctionaryIndex<String, String>> has no member zip", so this method seems that doesn't exist for this type. Thanks you very much!Queenhood
Fixed the syntax. Thanks for noting it.Shamblin
D
6

Another way to do so would be like this

for i in 0 ..< 5 where i < products.count {
}
Dorina answered 22/6, 2016 at 10:11 Comment(0)
V
1

One more example. Loop through all UILabel in subviews:

for label in view.subviews where label is UILabel {
    print(label.text)
}

UPDATE

Now the subview inside the loop is UIView and needs to additionally case it to UILabel

for subview in view.subviews where subview is UILabel {
    let label = subview as? UILabel
    print("\(label?.text ?? "")")
}
Vitrics answered 25/9, 2018 at 18:38 Comment(2)
In the current implementation of Swift this doesn't work, because label isn't cast to UILabel, even though it is known to be a UILabel. It would be a good feature but it isn't implemented yet.Wearproof
@Wearproof thank you to point it out! I've updated my answer.Vitrics
D
0

Here’s a simple solution:

var x = 0
while (x < foo.length && x < bar.length) {

  // Loop body goes here

  x += 1
}
Dade answered 6/4, 2017 at 16:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.