Does anybody know of a shortcut to test whether three numbers are the same? I know this works:
if number1 == number2 && number2 == number3 {
}
But I would like something cleaner, such as;
if number1 == number2 == number3 {
}
It's quite important as I'm comparing a lot of different values.
You could use a set
if Set([number1, number2, number3]).count == 1 {
...
though I'd argue it isn't as transparent as multiple if clauses
You can use the power of tuples and the Transitive Property of Equality.
if (number1, number2) == (number2, number3) {
}
The clause of this IF is true only when number1 is equals to number2 AND number2 is equals to number3. It means that the 3 values must be equals.
You can add them in an array and use sets:
var someSet = NSSet(array: [2,2,2])
if someSet.count == 1 {
print("Same values")
}
Don't know of anything other than a Set, I'd suggest wrapping it in a function to make your intent clear. Something along these lines:
func allItemsEqual<T>(items:[T]) -> Bool {
guard items.count > 1 else { fatalError("Must have at least two objects to check for equality") }
return Set(items).count == 1
}
func allItemsEqual(items:T...) -> Bool {
return equal(items)
}
if allItemsEqual(2,3,2) {
// false
}
if allItemsEqual(2, 2, 2) {
// true
}
Beyond that, maybe you could get fancy with operator overloading?
Try this:
func areEqual<T: NumericType>(numbers: T...) -> Bool {
let num = numbers[0]
for number in numbers {
if number != num {
return false
}
}
return true
}
Where NumericType is defined in this post: What protocol should be adopted by a Type for a generic function to take any number type as an argument in Swift?
This will allow you to use the function for all number types
You just pass any number of numbers like:
//returns true
if areEqual(1, 1, 1) {
print("equal")
}
Related
I want to test my function that takes a string, a returns all the pairs of characters as an array s.t.
func pairsOfChars(_ s: String) -> [(Character,Character)] {
let strArray = Array(s)
var outputArray = [(Character,Character)]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append( (strArray[i], strArray[j]) )
}
}
return outputArray
}
So I want to create a suite of tests using XCTestCase. I usually use XCTestCase and XCTAssertEqual but these are only appropriate for C scalar types. This means that the following test case returns an error:
class pairsTests: XCTestCase {
func testNaive() {
measure {
XCTAssertEqual( pairsOfChars("abc") , [(Character("a"),Character("b")),(Character("a"),Character("c")),(Character("b"),Character("c")) ] )
}
}
}
I could convert to a string, but I'm thinking there is a better solution.
How can I test an output of an array of pairs of characters [(Character,Character)]
Your notion of a nonscalar is a total red herring. The problem is one of equatability.
How can I test an output of an array of pairs of characters [(Character,Character)]
You can't, because there is no default notion of what it would mean to equate two such arrays. This is the old "tuples of Equatable are not Equatable" problem (https://bugs.swift.org/browse/SR-1222) which still rears its head with arrays. The == operator works on tuples by a kind of magic, but they are still not formally Equatable.
You could define equatability of arrays of character pairs yourself:
typealias CharPair = (Character,Character)
func ==(lhs:[CharPair], rhs:[CharPair]) -> Bool {
if lhs.count != rhs.count {
return false
}
let zipped = zip(lhs,rhs)
return zipped.allSatisfy{$0 == $1}
}
Alternatively, have your pairsOfChars return something that is more easily made equatable, such as an array of a struct for which Equatable is defined.
For example:
struct CharacterPair : Equatable {
let c1:Character
let c2:Character
// in Swift 4.2 this next bit is not needed
static func ==(lhs:CharacterPair, rhs:CharacterPair) -> Bool {
return lhs.c1 == rhs.c1 && lhs.c2 == rhs.c2
}
}
func pairsOfChars(_ s: String) -> [CharacterPair] {
let strArray = Array(s)
var outputArray = [CharacterPair]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append(CharacterPair(c1:strArray[i],c2:strArray[j]))
}
}
return outputArray
}
You would then rewrite the test to match:
XCTAssertEqual(
pairsOfChars("abc"),
[CharacterPair(c1:Character("a"),c2:Character("b")),
CharacterPair(c1:Character("a"),c2:Character("c")),
CharacterPair(c1:Character("b"),c2:Character("c"))]
)
We already know multiple optional bindings can be used in a single if/guard statement by separating them with commas, but not with && e.g.
// Works as expected
if let a = someOpt, b = someOtherOpt {
}
// Crashes
if let a = someOpt && b = someOtherOpt {
}
Playing around with playgrounds, the comma-style format also seems to work for boolean conditions though I can't find this mentioned anywhere. e.g.
if 1 == 1, 2 == 2 {
}
// Seems to be the same as
if 1 == 1 && 2 == 2 {
}
Is this an accepted method for evaluating multiple boolean conditions, and is the behaviour of , identical to that of && or are they technically different?
Actually the result is not the same. Say that you have 2 statements in an if and && between them. If in the first one you create a let using optional binding, you won't be able to see it in the second statement. Instead, using a comma, you will.
Comma example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)), cell.isSelected {
//Everything ok
}
&& Example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)) && cell.isSelected {
//ERROR: Use of unresolved identifier 'cell'
}
Hope this helps.
In Swift 3, the where keyword in condition clauses were replaced by a comma instead.
So a statement like if 1 == 1, 2 == 2 {} is saying "if 1 equals 1 where 2 equals 2..."
It's probably easiest to read a conditional statement with an && instead of a ,, but the results are the same.
You can read more about the details of the change in Swift 3 in the Swift Evolution proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md
When it comes to evaluating boolean comma-separated conditions, the easies way to think of a comma is a pair or brackets.
So, if you have
if true, false || true {}
It gets transformed into
if true && (false || true) {}
Here is a case where they are sufficiently different as to require the ,. The following code will yield
'self' captured by a closure before all members were initialized
class User: NSObject, NSCoding {
public var profiles: [Profile] = []
private var selectedProfileIndex : Int = 0
public required init?(coder aDecoder: NSCoder) {
// self.profiles initialized here
let selectedIndex : Int = 100
if 0 <= selectedIndex && selectedIndex < self.profiles.count { <--- error here
self.selectedProfileIndex = selectedIndex
}
super.init()
}
...
}
This is due to the definition of && on Bool:
static func && (lhs: Bool, rhs: #autoclosure () throws -> Bool) rethrows -> Bool
The selectedIndex < self.profiles.count on the RHS is caught in a closure.
Changing the && to , will get rid of the error. Unfortunately, I'm not sure how , is defined, but I thought that this was interesting.
https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_condition-list
The Swift grammar says that the if statement condition-list can be composed by multiple condition separated by commas ,
A simple condition can be a boolean expression, a optional-binding-condition or other things:
So, using the comma to separate multiple expressions is perfectly fine.
When pattern matching a associated value in a switch, it matters when using a , or &&. One compiles, the other won't (Swift 5.1). This compiles (&&):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10 && integer < 10:
print("hi")
default:
fatalError()
}
}
This won't (,):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10, integer < 10:
print("hi")
default:
fatalError()
}
}
Please consider the following:
func example(array: [Int]) {
guard array.count > 0 else { return }
// Do something
}
It would be nice to announce the restriction via the function signature. Is it possible to constrain the parameter? Something equivalent to:
func example(array: [Int] where array.count > 0) {
// Do something
}
Going one step further: let's make the array optional:
func example(array: [Int]?) {
guard array == nil || array!.count > 0 else { return }
// Do something
}
func example(array: [Int]? where array.count > 0) {
// Do something
}
No, you can't have a conditional parameter. The best you can do is what you have in the second option with an optional and before you call the function (or posibly in the function) check if it is nil.
Please consider the following:
func example(array: [Int]) {
guard array.count > 0 else { return }
// Do something
}
It would be nice to announce the restriction via the function signature. Is it possible to constrain the parameter? Something equivalent to:
func example(array: [Int] where array.count > 0) {
// Do something
}
Going one step further: let's make the array optional:
func example(array: [Int]?) {
guard array == nil || array!.count > 0 else { return }
// Do something
}
func example(array: [Int]? where array.count > 0) {
// Do something
}
No, you can't have a conditional parameter. The best you can do is what you have in the second option with an optional and before you call the function (or posibly in the function) check if it is nil.
Let's say that you have the code
if stringValue == "ab" || stringValue == "bc" || stringValue == "cd" {
// do something
}
Is there a way to shorten this condition or beautify it (preferably without using the switch statement)? I know that this code does NOT work:
if stringValue == ("ab" || "bc" || "cd") {
// do something
}
I've seen some complex solutions on other languages, but they seem language specific and not applicable to Swift. Any solutions would be appreciated.
let valuesArray = ["ab","bc","cd"]
valuesArray.contains(str) // -> Bool
You can create an extension like this:
extension Equatable {
func oneOf(other: Self...) -> Bool {
return other.contains(self)
}
}
and use it like this:
if stringValue.oneOf("ab", "bc", "cd") { ... }
Credit for the impl which saved me typing it: https://gist.github.com/daehn/73b6a08b062c81d8c74467c131f78b55/
Not that i am aware, you can do something like this though,
let validStrings = Set<String>(arrayLiteral:"ab", "bc", "cd")
if validStrings.contains(str) {
//do something
}
Use a Switch Statement.
switch stringValue {
case "ab", "bc", "cd":
print("Yay!")
default:
break
}
The construction ["some", "array"].contains("value") works, but is somewhat annoying:
It inverts the left-to-right order you may want to write.
Items in the array are not declared using Swift's type inference, often forcing you to include unnecessary information to please the compiler.
You can instead use Set(["value"]).isSubset(of: ["some", "array"]).
The benefit is especially apparent when working with enums:
enum SomeReallyReallyLongTypeName {
case one, two
}
struct Thing {
let value: SomeReallyReallyLongTypeName
}
let thing = Thing(value: .one)
if Set([thing.value]).isSubset(of: [.one, .two]){
// :)
// Left-to-right order
// You get nice type inference
}
if [SomeReallyReallyLongTypeName.one, .two].contains(thing.value) {
// :(
// Annoying to have "SomeReallyReallyLongTypeName" in the code
}
if someArray.contains(object) {
// contains
} else {
// does not contains
}
The above function returns bool value, then you write logic accordingly.
Just for fun, how about overloading functions over String:
if a.isOneOf("ab", "bc", "cd") {
print("yes")
}
extension String {
#inlinable
func isOneOf(_ first: String, _ second: String) -> Bool {
self == first || self == second
}
#inlinable
func isOneOf(_ first: String, _ second: String, _ third: String) -> Bool {
self == first || isOneOf(second, third)
}
#inlinable
func isOneOf(_ first: String, _ second: String, _ third: String, _ fourth: String) -> Bool {
self == first || isOneOf(second, third, fourth)
}
}
This gives you full performance benefits, as the compiler will be able to inline and tail call as much as it wants, at the cost of having to write as many overloads as you need in your code, and also not being able to pass arrays - but other answers deal with this too.
let a = 1
let b = 1
let c = 1
let d = 1
if a == b,a==c,a==d {
print("all of them are equal")
}
else {
print("not equal")
}