Any way to iterate a tuple in swift?

Swift

Swift Problem Overview


I am curious how to do a for loop with a tuple in swift.

I know that to access each member you can use dot notation using the index number

var tupleList = ("A",2.9,3,8,5,6,7,8,9)

for each in tupleList {
    println(each)
}

//Error: Type does not conform to protocol sequence

Swift Solutions


Solution 1 - Swift

Yes, you can!

func iterate<C,R>(t:C, block:(String,Any)->R) {
    let mirror = reflect(t)
    for i in 0..<mirror.count {
        block(mirror[i].0, mirror[i].1.value)
    }
}

And voila!

let tuple = ((false, true), 42, 42.195, "42.195km")
iterate(tuple) { println("\($0) => \($1)") }
iterate(tuple.0){ println("\($0) => \($1)")}
iterate(tuple.0.0) { println("\($0) => \($1)")} // no-op

Note the last one is not a tuple so nothing happens (though it is a 1-tuple or "Single" which content can be accessed .0, reflect(it).count is 0).

What's interesting is that iterate() can iterate even other types of collection.

iterate([0,1])              { println("\($0) => \($1)") }
iterate(["zero":0,"one":1]) { println("\($0) => \($1)") }

And that collection includes class and struct!

struct Point { var x = 0.0, y = 0.0 }
class  Rect  { var tl = Point(), br = Point() }
iterate(Point()) { println("\($0) => \($1)") }
iterate(Rect())  { println("\($0) => \($1)") }

Caveat: the value passed as the 2nd argument of the block is type Any. You have to cast it back to the values with original type.

Solution 2 - Swift

You can using reflection Swift 5

Try this in a Playground:

let tuple = (1, 2, "3")
let tupleMirror = Mirror(reflecting: tuple)
let tupleElements = tupleMirror.children.map({ $0.value })
tupleElements

Output:

enter image description here

Solution 3 - Swift

Swift does not currently support iterating over tuples.

The biggest reasons are:

  1. There is no way at runtime to determine the number of elements in a tuple
  2. There is no way to access an element at a specific index except for the compile time accessors like tupleList.0. You would really want a subscript tupleList[0] but that is not provided to us

Frankly, I can't see a reason that you would use a tuple instead of an Array if you want to iterate over it.

It doesn't make sense to iterate over a tuple because:

  1. Tuples always have a fixed length and each element has a fixed type
  2. You can name each tuple member with a name you can use to access it later

Arrays are well made to iterate over:

  1. Arbitrary length
  2. Can store multiple types using a common superclass or AnyObject
  3. Can be declared as a literal in a similar fashion to tuples: var list = ["A",2.9,3,8,5,6,7,8,9]

Solution 4 - Swift

@dankogai's excellent solution, updated for Swift 3.0:

func iterate<Tuple>(_ tuple:Tuple, body:(_ label:String?,_ value:Any)->Void) {
	for child in Mirror(reflecting: tuple).children {
		body(child.label, child.value)
	}
}

Usage remains identical to @dankogai's examples (beyond Swift 2's println()print() rename).

Note that the label is now of type String? when it was formerly String, to match the type change from Swift 1's MirrorType.subscript(…).0 to Swift 3's Mirror.Child.label.  However, for labelless tuples the label arg comes back as ".0", ".1", ".2", etc.— it's only nil for some other types.

Also, I took the liberty of renaming types & args to better match Swift 3's solidified naming standards, and changing the closure return type to Void.

Sidenote: I noticed somebody downvoted me here— I can't imagine why, other than the (fair) argument that building app functionality around reflection in Swift is hacking the type system, and is likely to lead to crappy code throughout (Swift's tuples shouldn't be considered an abstract data type, but rather a small collection of variables, akin to method args).  As a counter-argument, I originally ended up porting this to Swift 3 in project because I needed it— for better descriptions and debugDescriptions.  Because sane debug output will saves you hours and hours of frustration. ;-)  Additionally, this could be really useful for unit tests… because tests are ultimately most interested in “did the result of this operation match what we expect?”

Solution 5 - Swift

Details

  • Xcode 11.2.1 (11B500), Swift 5.1

Base Solution

struct Tuple<T> {
    let original: T
    private let array: [Mirror.Child]
    init(_ value: T) {
        self.original = value
        array = Array(Mirror(reflecting: original).children)
    }
    func forEach(closure: (Mirror.Child) -> Void) { array.forEach { closure($0) } }
    func getOnlyValues<T: Any>() -> [T] { array.compactMap { $0.value as? T } }
    func getAllValues() -> [Any] { array.compactMap { $0.value } }
}

Usage on base solution

let num: Int? = 3
let str: String? = nil
let x = (1, "stew", str, 5.4, 2, num)

let tuple = Tuple(x)
tuple.forEach { print("\($0)") }

print("\(tuple.getAllValues())")            // [1, "stew", nil, 5.4, 2, Optional(3)]
print("\(tuple.getOnlyValues() as [Int])")  // [1, 2, 3]

More sugar

func valuesFrom<V>(tuple: V) -> [Any] { return Tuple(tuple).getAllValues() }
func onlyValuesFrom<T,V>(tuple: V) -> [T] { return Tuple(tuple).getOnlyValues() as [T] }

print(valuesFrom(tuple: x))                 // [1, "stew", nil, 5.4, 2, Optional(3)]
print(onlyValuesFrom(tuple: x) as [Int])    // [1, 2, 3]

Solution 6 - Swift

No, you can't. The reason is that tuple items are not all required to have the same type, so you would not be able to know what type each should have.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCh&#233;yoView Question on Stackoverflow
Solution 1 - SwiftdankogaiView Answer on Stackoverflow
Solution 2 - SwiftMennoView Answer on Stackoverflow
Solution 3 - SwiftdrewagView Answer on Stackoverflow
Solution 4 - SwiftSlipp D. ThompsonView Answer on Stackoverflow
Solution 5 - SwiftVasily BodnarchukView Answer on Stackoverflow
Solution 6 - SwiftAlex GaynorView Answer on Stackoverflow