如何在同一个for循环中运行三个单独的数组?

时间:2015-02-24 01:13:05

标签: arrays swift

我有三个我试图运行的数组,我想在一个函数中使用所有三个数组的值。这可能听起来令人困惑,但这就是我所拥有的:

    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)
            }
        }
    } 

这会运行,但我得到的是:( makeUser打印出3个值)

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

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

等等。

我想要的只是

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

这可能吗?任何帮助表示赞赏。

谢谢!

6 个答案:

答案 0 :(得分:20)

这是一个非常常见的要求,因此标准库通过函数zip迎合它:*

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

不幸的是,截至目前,zip只进行配对,即使理论上它可能会超载三倍。但是,这不是什么大问题,你可以嵌套它们:

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)
}

请注意,它只会提供最短的序列,因此如果名称多于年龄或性别,则只能获得匹配的名称。

与使用索引相比,这似乎有点不利,这看起来似乎也更复杂,但替代方案的简单性具有欺骗性。请记住,如果您使用indicesenumerate以及不匹配的数组会发生什么情况 - 您将获得一个超出边界断言的数组(或者您必须放入检查逻辑) 。

zip避免了这个问题。它还意味着您可以使用序列而不是集合,以及使用不具有整数索引(不同于enumerate)的集合或具有不同索引类型的集合(例如String和{ {1}})。

*(在当前的测试版中,无论如何 - Array会返回zip个对象。在Swift 1.1中,您需要直接创建Zip2版本,因为Zip2只有刚刚介绍过)

答案 1 :(得分:4)

如果你总是确定数组的长度是相等的,那么你最好循环遍历其中一个数组并使用它的索引来引用其他数组:

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

但是,我建议将这些数据放入字典中,但我认为这只是示例数据来说明一点。 :)

答案 2 :(得分:2)

您可以使用自定义zip3功能,这并不难写。

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)")
}

以上代码打印:

Name: Joe, age: 18, gender: Male
Name: Sarah, age: 20, gender: Female
Name: Chad, age: 22, gender: Male

答案 3 :(得分:1)

这是一个使用zip和3个数组的解决方案(测试它们的长度确实相同):

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

但也许最干净的只是老式的C风格:

for i in 0..<names.count {
    let name = names[i]
    let age = ages[i]
    let gender = genders[i]
    makeUser(name, userAge: age, userGender: gender)
}

答案 4 :(得分:0)

见下文。但是,如果这些数组中的任何数组与其他数组的大小不同,则代码将崩溃。

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)
}

答案 5 :(得分:0)

您可以将枚举数转换为数组,并使用函数方法将结果映射到您想要的结果。

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])}