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

前端 未结 6 1296
遇见更好的自我
遇见更好的自我 2020-12-06 13:38

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:

<
相关标签:
6条回答
  • 2020-12-06 14:13

    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. :)

    0 讨论(0)
  • 2020-12-06 14:20

    Here is a solution using zip with 3 arrays (test they are indeed the same length):

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

    But maybe the cleanest of them all is just old fashioned C-style:

    for i in 0..<names.count {
        let name = names[i]
        let age = ages[i]
        let gender = genders[i]
        makeUser(name, userAge: age, userGender: gender)
    }
    
    0 讨论(0)
  • 2020-12-06 14:26

    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
    
    0 讨论(0)
  • 2020-12-06 14:31

    See below. However, your code will crash if any of these arrays differs in size from the others.

    var Name = ["a", "b", "c"]
    var Age = [1, 2, 3]
    var Gender = ["m", "f", "m"]
    
    for (var i = 0; i<Name.count; i++) {
        var name = Name[i]
        var age = Age[i]
        var gender = Gender[i]
        makeUser(name, userAge: age, userGender: gender)
    }
    
    0 讨论(0)
  • 2020-12-06 14:33

    You can cast the enumerator as an array and use functional methods to map the result to what you want.

    var Name = ["a", "b", "c"]
    var Age = [1, 2, 3]
    var Gender = ["m", "f", "m"]
    
    let results = Array(Name.enumerated())
        .map {($0.element, Age[$0.index], Gender[$0.index])}
    
    0 讨论(0)
  • 2020-12-06 14:40

    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)

    0 讨论(0)
提交回复
热议问题