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

问题描述 投票:4回答:6

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

    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

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

谢谢!

arrays swift
6个回答
4
投票

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

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

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


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不同)的集合或具有不同索引类型的集合(例如StringArray)。

*(在当前的测试版中,无论如何 - zip返回一个Zip2对象。在Swift 1.1中,你需要直接创建Zip2版本,因为zip刚刚被引入)


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

1
投票

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

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

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

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])}
© www.soinside.com 2019 - 2024. All rights reserved.