Can I extend Tuples in Swift?
You cannot extend tuple types in Swift.
According to
Types, there are named types (which
can be extended) and compound types. Tuples and functions are compound
types.
See also (emphasis added):
Extensions
Extensions add new functionality to an existing
class, structure, or enumeration type.
Extending Array of tuples of equatables to find the first tuple matching
You can achieve that by constraining the method instead of the
extension:
extension Array {
func contains<E1, E2>(_ tuple: (E1, E2)) -> Bool where E1: Equatable, E2: Equatable, Element == (E1, E2) {
return contains { $0.0 == tuple.0 && $0.1 == tuple.1 }
}
}
Example:
let a = [(1, "a"), (2, "b")]
print(a.contains((1, "a")))
Swift - Joining two tuple arrays based on their values
let tuple1 = [("score1", "index1"), ("score2", "index2"), ("score3", "index3")]
let tuple2 = [("date1", "index1"), ("date2", "index2"), ("date3", "index4")]
let t2Dict = tuple2.reduce(into: [String:String]()) { (dict, args) in
let (date, index) = args
dict[index] = date
}
let tuple3 = tuple1.compactMap { args -> (String, String)? in
let (score, index) = args
guard let date = t2Dict[index] else { return nil }
return (score, date)
}
It's not as pretty as the others, but it's far more efficient to collapse one of the tuples into a dictionary first.
Why Swift Tuples can be compared only when the number of elements is less than or equal to 6?
Background: Tuples aren't Equatable
Swift's tuples aren't Equatable
, and they actually can't be (for now). It's impossible to write something like:
extension (T1, T2): Equatable { // Invalid
// ...
}
This is because Swift's tuples are structural types: Their identity is derived from their structure. Your (Int, String)
is the same as my (Int, String)
.
You can contrast this from nominal types, whose identity is solely based off their name (well, and the name of the module that defines them), and whose structure is irrelevant. An enum E1 { case a, b }
is different from an enum E2 { case a, b }
, despite being structurally equivalent.
In Swift, only nominal types can conform to protocols (like Equatble
), which precludes tuples from being able to participate.
...but ==
operators exist
Despite this, ==
operators for comparing tuples are provided by the standard library. (But remember, since there is still no conformance to Equatable
, you can't pass a tuple to a function where an Equatable type is expected, e.g. func f<T: Equatable>(input: T)
.)
One ==
operator has to be manually be defined for every tuple arity, like:
public func == <A: Equatable, B: Equatable, >(lhs: (A,B ), rhs: (A,B )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, >(lhs: (A,B,C ), rhs: (A,B,C )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, >(lhs: (A,B,C,D ), rhs: (A,B,C,D )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, >(lhs: (A,B,C,D,E ), rhs: (A,B,C,D,E )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool { ... }
Of course, this would be really tedious to write-out by hand. Instead, it's written using GYB ("Generate your Boilerplate"), a light-weight Python templating tool. It allows the library authors to implement ==
using just:
% for arity in range(2,7):
% typeParams = [chr(ord("A") + i) for i in range(arity)]
% tupleT = "({})".format(",".join(typeParams))
% equatableTypeParams = ", ".join(["{}: Equatable".format(c) for c in typeParams])
// ...
@inlinable // trivial-implementation
public func == <${equatableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
${", ".join("lhs.{}".format(i) for i in range(1, arity))}
) == (
${", ".join("rhs.{}".format(i) for i in range(1, arity))}
)
}
Which then gets expanded out by GYB to:
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable>(lhs: (A,B), rhs: (A,B)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1
) == (
rhs.1
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2
) == (
rhs.1, rhs.2
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable>(lhs: (A,B,C,D), rhs: (A,B,C,D)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3
) == (
rhs.1, rhs.2, rhs.3
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable>(lhs: (A,B,C,D,E), rhs: (A,B,C,D,E)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4
) == (
rhs.1, rhs.2, rhs.3, rhs.4
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4, lhs.5
) == (
rhs.1, rhs.2, rhs.3, rhs.4, rhs.5
)
}
Even though they automated this boilerplate and could theoretically change for arity in range(2,7):
to for arity in range(2,999):
, there is still a cost: All of these implementations have to be compiled and produce machine code that ends up bloating the standard library. Thus, there's still a need for a cutoff. The library authors chose 6, though I don't know how they settled on that number in particular.
Future
There's two ways this might improve in the future:
There is a Swift Evolution pitch (not yet implemented, so there's no official proposal yet) to introduce Variadic generics, which explicitly mentions this as one of the motivating examples:
Finally, tuples have always held a special place in the Swift language, but working with arbitrary tuples remains a challenge today. In particular, there is no way to extend tuples, and so clients like the Swift Standard Library must take a similarly boilerplate-heavy approach and define special overloads at each arity for the comparison operators. There, the Standard Library chooses to artificially limit its overload set to tuples of length between 2 and 7, with each additional overload placing ever more strain on the type checker. Of particular note: This proposal lays the ground work for non-nominal conformances, but syntax for such conformances are out of scope.
This proposed language feature would allow one to write:
public func == <T...>(lhs: T..., rhs: T...) where T: Equatable -> Bool {
for (l, r) in zip(lhs, rhs) {
guard l == r else { return false }
}
return true
}Which would be a general-purpose
==
operator that can handle tuples or any arity.There is also interest in potentially supporting non-nominal conformances, allowing structural types like Tuples to conform to protocols (like
Equatable
).That would allow one to something like:
extension<T...> (T...): Equatable where T: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
for (l, r) in zip(lhs, rhs) {
guard l == r else { return false }
}
return true
}
}
merging two arrays of tuples
You can use a single map
to achieve your calls. You call map
on weightCalculated
, which you know for sure will contain an entry for each day, then check if weightsMeasured
contains an entry for the same day or not. If there is a measurement, return the measurement, otherwise return the calculated value.
let mergedWeights = weightsCalculated.map({ todayCalculated->PointTuple in
if let todayMeasured = weightsMeasured.first(where: { $0.day == todayCalculated.day}) {
return todayMeasured
} else {
return todayCalculated
}
})
Or you can even write this as a one liner using the nil-coalescing operator to return the measured value if it is found and the calculated value otherwise:
let mergedWeights = weightsCalculated.map({ todayCalculated in return weightsMeasured.first(where: { $0.day == todayCalculated.day}) ?? todayCalculated })
When trying out the above code bear in mind that you had a typo in the variable name weightCalculated
, which I corrected in my code.
List of tuples with different size and type in swift
This looks like a job for a struct.
Since there are 5 different permutations of your data, you can create 5 structs, each of which to conform to a common protocol. You can then make your array hold a list of those structs by their more general protocol type.
Tuple-Array with variable content types & length?
A tuple is inappropriate because you cannot declare a tuple in a generic manner.
A possible solution is to declare data
as an array of CustomStringConvertible
instead of a tuple.
The benefit is you can pass any type which supports String Interpolation and the number of items in the array is variable.
func debugData(names: [String], data: [[CustomStringConvertible]] ) {
var debugLine = ""
for line in data {
for i in 0..<line.count {
debugLine += "\(names[i])=\(line[i]) "
}
print(debugLine)
debugLine = ""
}
}
debugData(names: ["Name", "Age", "SexM"], data: [["Alex", 5, true], ["Lisa", 7, false], ["Max", 9, true]])
Related Topics
How to Move Application's Window Between Virtual Desktops in Os X
Adding Animation to Tabviews in Swiftui When Switching Between Tabs
Using Uilexicon to Implement Autocorrect in iOS 8 Keyboard Extension
Command Failed Due to Signal: Segmentation Fault: 11 While Emitting Ir Sil Function
How to Import Googleanalytics Header into a Library Framework
Uitableviewautomaticdimension Not Working on iOS 8
Navigationview Bar Material Invisible on iOS 15
Swift: Generics and Type Constraints, Strange Behavior
Swift Generics Protocols: Can Only Be Used as a Generic Constraint Problem
Custom Uitabbar Unselected Item's Color
Solving System of Equations in Swift
Why Use an Extra Let Statement Here
How to Restrict an Enum to Certain Cases of Another Enum
How to Detect Text View Begin Editing and End Editing in Swift 3
How to Sort Dates in a Dictionary
Rotate a Text View and Its Frame in Swiftui
How to Customize the Title/Subtitle Font in Callout from Mkannotationview or Just Hide Them