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
Is Swiftui Backwards-Compatible With iOS 12.X and Older
Retrieve Only 5 Users At a Time :Firebase [Like Instagram]
App Tracking Transparency Dialog Does Not Appear on iOS
Multiple Type Constraints in Swift
How to Determine the Type of a Variable in Swift
Swift Semantics Regarding Dictionary Access
How Does One Trap Arithmetic Overflow Errors in Swift
How to Cycle Through the Entire Alphabet with Swift While Assigning Values
Cannot Resolve Swift Packages After 15Th March 2022 in Xcode
Swift: Setting an Optional Property of a Protocol
Function with Datatask Returning a Value
@Noescape Attribute in Swift 1.2
Delete Data from Coredata Swift
How to Draw Text in PDF Context in Swift
Arkit: Placing an Scntext at a Particular Point in Front of the Camera