Any Way to Iterate a Tuple in Swift

Any way to iterate a tuple in 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.

How to iterate over single tuple item in array of tuples

This is the solution I found:

If you have an array of types which are collection types on their own, and you want to look only at a certain property of each member of the outer array, use the .map method of swift's collection type:

var testArray = [(4,"Bee",2.5),(5,"dog",1.0),(8,"dog",43.13)]
var onlyFirstProperty = testArray.map({$0.0}) // [4,5,8]

This way you'll get a new array which consists only of the first elements of each tuple. $0.0 is shorthand syntax for firstMember.firstProperty. In my code I can call my function like this:

findInsertPoint(forElement: 7 in Array: testArray.map({$0.0}))

How to iterate through a tuple array with for loop in Swift

The type of points2D is not Array, but rather Array<(Int,Int)>,
so let Swift infer the type:

 var points2D = [(1,1),(2,3),(4,3),(9,5),(3,3),(7,6),(5,6)]

or set the correct type explicitly:

var points2D:Array<(Int,Int)> = [(1,1),(2,3),(4,3),(9,5),(3,3),(7,6),(5,6)]

Assigning values to tuple in for loop

There's no official API for doing this, but IIRC, tuples of homogeneous element types are guaranteed to have a contiguous memory layout. You take advantage of this by using UnsafeBufferPointer to read/write to the tuple.

Usually this requires you to manually hard-code the tuple's element count, but I wrote some helper functions that can do this for you. There's two variants, a mutable one, which lets you obtain an UnsafeBufferPointer, which you can read (e.g. to create an Array), and a mutable one, which gives you a UnsafeMutableBufferPointer, through which you can assign elements.

enum Tuple {
static func withUnsafeBufferPointer<Tuple, TupleElement, Result>(
to value: Tuple,
element: TupleElement.Type,
_ body: (UnsafeBufferPointer<TupleElement>) throws -> Result
) rethrows -> Result {
try withUnsafePointer(to: value) { tuplePtr in
let count = MemoryLayout<Tuple>.size / MemoryLayout<TupleElement>.size

return try tuplePtr.withMemoryRebound(
to: TupleElement.self,
capacity: count
) { elementPtr in
try body(UnsafeBufferPointer(start: elementPtr, count: count))
}

}
}

static func withUnsafeMutableBufferPointer<Tuple, TupleElement, Result>(
to value: inout Tuple,
element: TupleElement.Type,
_ body: (UnsafeMutableBufferPointer<TupleElement>) throws -> Result
) rethrows -> Result {
try withUnsafeMutablePointer(to: &value) { tuplePtr in
let count = MemoryLayout<Tuple>.size / MemoryLayout<TupleElement>.size

return try tuplePtr.withMemoryRebound(
to: TupleElement.self,
capacity: count
) { elementPtr in
try body(UnsafeMutableBufferPointer(start: elementPtr, count: count))
}

}
}
}

var destinationTouple: (Int, Int, Int, Int) = (0, 0, 0, 0) // => (0, 0, 0, 0)
var sourceArray = Array(1...4)

print("before:", destinationTouple)

Tuple.withUnsafeMutableBufferPointer(to: &destinationTouple, element: Int.self) { (destBuffer: UnsafeMutableBufferPointer<Int>) -> Void in
sourceArray.withUnsafeMutableBufferPointer { sourceBuffer in
// buffer[...] = 1...
destBuffer[destBuffer.indices] = sourceBuffer[destBuffer.indices]
return ()
}
}

print("after:", destinationTouple) // => (1, 2, 3, 4)

Can I retrieve an item from a tuple in the same way as indexing an item from an array in a for loop?

Check out this link for the inspiration to this answer. First add this function somewhere accessible in your code:

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

Then use this updated version of your code:

if objectType === UIButton.self {
if xOriginCgFloat.2 != nil || yOriginCgFloat.2 != nil || width.2 != nil || height.2 != nil {
var buttonOneData:[CGFloat] = [CGFloat]()
var buttonTwoData:[CGFloat] = [CGFloat]()
var buttonThreeData:[CGFloat] = [CGFloat]()
var buttonsData:[[CGFloat]] = [buttonOneData,buttonTwoData,buttonThreeData]
var tuples = [xOriginCgFloat,yOriginCgFloat,width,height]

for tuple in tuples {
iterate(tuple) {
var indexStr = $0! //index = .0,.1,.2, etc
indexStr.remove(at:indexStr.startIndex) //remove the . from .0,.1,.2,etc.
let index = Int(indexStr)
buttonsData[index].append(CGFloat($1 as! Int)) //$1 = the value of tuple.0, tuple.1, tuple.2, etc.
}
}

for (index,buttonData) in buttonsData.enumerated() {
var button = UIButton(frame: CGRect.init(origin: CGPoint.init(x: buttonData[index], y: buttonData[index]), size: CGSize.init(width: buttonData[index], height: buttonData[index])))
buttonArray.append(button)
}

} else {
fatalError("Each tuple for a button object must have exactly three values.")
}
}

It is very convoluted and I'm honestly not a big fan of it, but if you need to use tuples, then this will (probably) work for you. I basically used the iterate function from the list at the top of this answer to go through your tuples and add them to arrays that separated the data from the tuples into an array of button data, which I then made an array so you can iterate through that and make the actual buttons.

Iterating through an array of Tuples

The problem is with this line:

key = unsortedTupleArray.[i]

You have a . between the array and the subscript. It should look like this:

key = unsortedTupleArray[i]

but that only gets you the tuple, if you want to pull out the Int, you need to access the second element in the tuple

key = unsortedTupleArray[i].1

Now, if you want to preserve the names of the tuples, you should keep them in the function declaration. Then you can access the value using .value:

var keyValueArray = [(name: "One", value: 1), (name: "Four", value: 4), (name: "Two", value: 2) ]

func tupleArrayInsertionSort(var unsortedTupleArray: [(name: String, value: Int)]){
var key, y : Int
for i in 0..<unsortedTupleArray.count {
key = unsortedTupleArray[i].value
}
}

Swift: Get an element from a tuple

According to the documentation (scroll down to Tuples), there are three ways to do it.

Given

var answer: (number: Int, good: Bool) = (100, true)

Method 1

Put the element variable name within a tuple.

let (firstElement, _) = answer
let (_, secondElement) = answer

or

let (firstElement, secondElement) = answer

Method 2

Use the index.

let firstElement = answer.0
let secondElement = answer.1

Method 3

Use the names. This only works, of course, if the elements were named in the Tuple declaration.

let firstElement = answer.number
let secondElement = answer.good

How do i init a tuple property from a class in swift?

That is not the way to assign a value to a tuple. You need to assign the tuple as a whole, you cannot assign its properties 1-by-1.

var service: (String, Int, Bool)

init() {
service = ("Fix PC", 400, false)
}

Once the tuple is initialised, you can update its members 1-by-1, however your syntax was wrong, tuple member access is done using ., not ().

service.0 = "Fix Mac"

Also, you should only be using tuples as temporary storage, for your example, you should be using a custom type with 3 separate properties.

struct Service {
let name: String
let value: Int
let bool: Bool
}

let service = Service(name: "Fix PC", value: 400, bool: false)

Is there any way to append an element/value to a tuple?

No. Each tuple with a different number of elements or a different type of elements represents a different type in the Swift type system. Hence, once you create a tuple, you cannot append elements to it, because that would change the type of the tuple.

Some really basic examples of tuples and their types:

let tupleWithTwoInts = (1,2) //has type (Int,Int)
let tupleWithThreeInts = (1,2,3) //has type (Int,Int,Int)
let tupleWithTwoStrings = ("a","b") //has type (String,String)
let tupleWithIntAndString = (1,"a") //has type (Int,String)
let tupleWithStringAndInt = ("a",1) //has type (String,Int)

Even the order of the elements make a difference in the type of a tuple.

type(of: tupleWithIntAndString) == type(of: tupleWithStringAndInt) //false


Related Topics



Leave a reply



Submit