Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I run through three separate arrays in the same for loop?

Tags:

arrays

swift

I have three arrays I am trying to run through and I want to use the values from all three arrays in one function. This might sound confusing but here is what I have:

    var Name = [Joe, Sarah, Chad]
    var Age = [18, 20, 22]
    var Gender = [Male, Female, Male]

    for name in Name {
        for age in Age {
            for gender in Gender {   
                makeUser(name, userAge: age, userGender: gender)
            }
        }
    } 

This runs but what I get is: (makeUser prints out the 3 values)

Joe, 18, Male
Joe, 20, Male
Joe, 22, Male

Joe, 18, Female
Joe, 20, Female
Joe, 22, Female ....

And so on.

All I want is

Joe, 18, Male
Sarah, 20, Female
Chad, 22, Male

Is this possible? Any help is appreciated.

Thanks!

like image 825
Andrew Carnige Avatar asked Feb 24 '15 01:02

Andrew Carnige


3 Answers

This is a very common requirement so the standard library caters to it with a function, zip:*

for (a,b) in zip(seq1, seq2) {
    // a and b will be matching pairs from the two sequences
}

Unfortunately, as of right now, zip only does pairs, even though in theory it could be overloaded to do triples. However, it’s not a big deal, you can just nest them:

var names = ["Joe", "Sarah", "Chad"]
var ages = [18, 20, 22]
var genders: [Gender] = [.Male, .Female, .Male]

for (name,(age,gender)) in zip(names,zip(ages,genders)) {
    makeUser(name, userAge: age, userGender: gender)
}

Note, it will only serve up to the shortest sequence, so if there are more names than ages or genders, you’ll only get the matching names.

This might seem like a down side compared to using an index, and this might also seem more complex, but the alternative’s simplicity is deceptive. Bear in mind what would happen if you used indices or enumerate alongside arrays that didn’t match – you’d get an array out of bounds assertion (or you’d have to put in checking logic).

zip avoids this problem. It also means you can use sequences instead of collections, as well as working with collections that don’t have integer indices (unlike enumerate) or collections that have different index types (e.g. a String and an Array).

*(in the current beta, anyway – zip returns a Zip2 object. In Swift 1.1, you need to create the Zip2 version directly as zip has only just been introduced)

like image 79
Airspeed Velocity Avatar answered Nov 20 '22 00:11

Airspeed Velocity


If you are always sure the arrays will be equal in length, then you are better to just loop through one of the arrays and use it's index to reference the others:

for (index, name) in enumerate(Name) {
    makeUser(name, userAge: Age[index], userGender: Gender[index])
}

However, I would recommend getting this data into a dictionary, but I assume this is just sample data to illustrate a point. :)

like image 29
theVinchi Avatar answered Nov 20 '22 01:11

theVinchi


You could use a custom zip3 function, which is not hard to write.

struct Zip3Sequence<E1, E2, E3>: Sequence, IteratorProtocol {
    private let _next: () -> (E1, E2, E3)?

    init<S1: Sequence, S2: Sequence, S3: Sequence>(_ s1: S1, _ s2: S2, _ s3: S3) where S1.Element == E1, S2.Element == E2, S3.Element == E3 {
        var it1 = s1.makeIterator()
        var it2 = s2.makeIterator()
        var it3 = s3.makeIterator()
        _next = {
            guard let e1 = it1.next(), let e2 = it2.next(), let e3 = it3.next() else { return nil }
            return (e1, e2, e3)
        }
    }

    mutating func next() -> (E1, E2, E3)? {
        return _next()
    }
}

func zip3<S1: Sequence, S2: Sequence, S3: Sequence>(_ s1: S1, _ s2: S2, _ s3: S3) -> Zip3Sequence<S1.Element, S2.Element, S3.Element> {
    return Zip3Sequence(s1, s2, s3)
}

let names = ["Joe", "Sarah", "Chad"]
let ages = [18, 20, 22]
let genders = ["Male", "Female", "Male"]

for (name, age, gender) in zip3(names, ages, genders) {
    print("Name: \(name), age: \(age), gender: \(gender)")
}

The above code prints:

Name: Joe, age: 18, gender: Male
Name: Sarah, age: 20, gender: Female
Name: Chad, age: 22, gender: Male
like image 4
Cristik Avatar answered Nov 20 '22 01:11

Cristik