I use Swift 2 and Xcode 7.
I would like to know the difference between
if condition { ... } else { ... }
and
guard ... else ...
The really big difference is when you are doing an optional binding:
if let x = xOptional {
if let y = yOptional {
// ... and now x and y are in scope, _nested_
}
}
Contrast this:
guard let x = xOptional else {return}
guard let y = yOptional else {return}
// ... and now x and y are in scope _at top level_
For this reason, I often have a succession of multiple guard statements before I get to the meat of the routine.
Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.
Reference and code example here.
To add to Matt's answer, you can include several conditions in a single guard statement:
guard let x = xOptional, y = yOptional else { return }
// ... and now x and y are in scope _at top level_
In addition to optional binding, a guard condition can test boolean results:
guard x > 0 else { return }
In short, the benefit of the guard statement is to make the early exit apparent at the start of the scope, instead of the condition being buried further down in a nested else statement.
Related
Please consider following:
let isHaveSn = model.positions.contains {$0.assortment?.isSerialTrackable == true}
guard isHaveSn else {
return
}
isHaveSn product Bool, and so i check that Bool in guard and everything work. But i want to simplify this like that:
guard model.positions.contains {$0.assortment?.isSerialTrackable == true} else {
return
}
It produce several errors, some of them: expected expression or Consecutive statement should be separated by ;
But i just copy right side of:
let isHaveSn = model.positions.contains {$0.assortment?.isSerialTrackable == true}
Why my code not compile?
The correct syntax for that would be:
guard model.positions.contains(where: {$0.assortment?.isSerialTrackable == true}) else {
return
}
Actually there was a proposal in swift-evolution to enable trailing closures in guard statements that was rejected. You can read more about it here
This question already has answers here:
Refactored Solution In Swift
(2 answers)
Closed 6 years ago.
I'm attempting to solve HackerRank's Hash Table Ransom Note challenge. There are 19 test cases and I'm passing all but two of time due to timeout on larger data sets (10,000-30,000 entries).
I'm given:
1) an array of words contained in a magazine and
2) an array of words for a ransom note. My objective is to determine if the words in the magazine can be used to construct a ransom note.
I need to have enough unique elements in the magazineWords to satisfy the quantity needed by noteWords.
I'm using this code to make that determination...and it takes FOREVER...
for word in noteWordsSet {
// check if there are enough unique words in magazineWords to put in the note
if magazineWords.filter({$0==word}).count < noteWords.filter({$0==word}).count {
return "No"
}
}
What is a faster way to accomplish this task?
Below is my complete code for the challenge:
import Foundation
var magazineWords = // Array of 1 to 30,000 strings
var noteWords = // Array of 1 to 30,000 strings
enum RegexString: String {
// Letters a to z, A to Z, 1 to 5 characters long
case wordCanBeUsed = "([a-zA-Z]{1,5})"
}
func matches(for regexString: String, in text: String) -> [String] {
// Hat tip MartinR for this
do {
let regex = try NSRegularExpression(pattern: regexString)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
func canCreateRansomNote(from magazineWords: [String], for noteWords: [String]) -> String {
// figure out what's unique
let magazineWordsSet = Set(magazineWords)
let noteWordsSet = Set(noteWords)
let intersectingValuesSet = magazineWordsSet.intersection(noteWordsSet)
// constraints specified in challenge
guard magazineWords.count >= 1, noteWords.count >= 1 else { return "No" }
guard magazineWords.count <= 30000, noteWords.count <= 30000 else { return "No" }
// make sure there are enough individual words to work with
guard magazineWordsSet.count >= noteWordsSet.count else { return "No" }
guard intersectingValuesSet.count == noteWordsSet.count else { return "No" }
// check if all the words can be used. assume the regex method works perfectly
guard noteWords.count == matches(for: RegexString.wordCanBeUsed.rawValue, in: noteWords.joined(separator: " ")).count else { return "No" }
// FIXME: this is a processor hog. I'm timing out when I get to this point
// need to make sure there are enough magazine words to write the note
// compare quantity of word in magazine with quantity of word in note
for word in noteWordsSet {
// check if there are enough unique words in magazineWords to put in the note
if magazineWords.filter({$0==word}).count < noteWords.filter({$0==word}).count {
return "No"
}
}
return "Yes"
}
print(canCreateRansomNote(from: magazineWords, for: noteWords))
I don't know how to read from the test case on the contest website or what frameworks you are allowed. If Foundation is allowed, you can use NSCountedSet
import Foundation
let fileContent = try! String(contentsOf: URL(fileURLWithPath: "/path/to/file.txt"))
let scanner = Scanner(string: fileContent)
var m = 0
var n = 0
scanner.scanInt(&m)
scanner.scanInt(&n)
var magazineWords = NSCountedSet(capacity: m)
var ransomWords = NSCountedSet(capacity: n)
for i in 0..<(m+n) {
var word: NSString? = nil
scanner.scanUpToCharacters(from: .whitespacesAndNewlines, into: &word)
if i < m {
magazineWords.add(word!)
} else {
ransomWords.add(word!)
}
}
var canCreate = true
for w in ransomWords {
if ransomWords.count(for: w) > magazineWords.count(for: w) {
canCreate = false
break
}
}
print(canCreate ? "Yes" : "No")
It works by going through the input file one word at a time, counting how many times that word appears in the magazine and in the ransom note. Then if any word appear more frequently in the ransom note than in the magazine, it fails the test immediately. Run the 30,000 words test case in less than 1 second on my iMac 2012.
The Swift documentation at page 61 of the Swift manual hints to the possibility of using where to join an optional binding with a regular condition. Yet when I do it I have a warning suggesting me to substitute the where with a comma like in the following piece of code:
if let geocodingError = error as? NSError where geocodingError.code == 2
In Swift 3 this syntax has changed.
What was
if let x = y, a = b where a == x {
Is now
if let x = y, let a = b, a == x {
The justification is that each sub-clause of the if ... { is now an independent boolean test.
See the Xcode Release notes & the Swift Evolution proposal for more info about this change.
Example with two conditions
if let x = y, let a = b, a == x && !x.isEmpty {
In xcode 9
if let str = textField.text as String!, !str.isEmpty
{
params[key] = str
TextFieldHelper.setup(textField: textField)
}
else
{
TextFieldHelper.error(textField: textField)
}
Is there a way to make this statement shorter?
let number = 1
if number == 0 || number == 1 {
print("ok")
}
like
let number = 1
if number == (0 || 1) {
print("ok")
}
This one doesn't work but I was wondering if there is a shorter way that I am not aware of.
EDIT:
I am aware of the switch case that might be useful when many case appear, but in my case I was more looking for something to use with ternary operators:
let oldNumber = 1
let newNumber = (oldNumber = 0 || 1) ? 4 : 13
Boolean equation expressions are evaluated always providing left (number) and right (0) side.
Not really shorter but you could write
let number = 1
if (0...1).contains(number) {
print("ok")
}
just for variety:
let c = { (number:Int, args:Int...) -> Bool in return args.contains(number) }
let number = 1;
if c(number, 0,1,9,4) {
print("ok")
}
only shorter if you are going to reuse the closure, has the advantage over the 0...1 range because you can specify a disjoint set of numbers. Can be used for inline if statements etc quite neatly
Little poor but nevertheless.
let number = 1
print((0...1).contains(number) ? "ok" : "bad")
Or:
let a = 1
let b = (0...1).contains(a) ? 4 : 5
When checking for several possible cases the switch statement might be more concise.
let number = 1
switch (number) {
case 0, 1:
print("ok")
default:
print("something else")
}
I'm trying to teach myself Swift via the Stanford iTunes U course (currently working on the calculator app), but I just ran into a super weird issue that I've never seen before and can't figure out how to solve: one of my return statements (and only the return statement itself) is being skipped during runtime.
func evaluateOps(ops: [Op]) -> (result: Double?, remainingOps: [Op]) {
if !ops.isEmpty {
var remainingOps = ops
let op = remainingOps.removeLast()
switch op {
case ...
case ...
case .BinaryOperation(_, let operation):
// check that there are 2 operands before it
if remainingOps.count == 2 {
let op1Evaluation = evaluateOps(remainingOps)
if let operand1 = op1Evaluation.result {
let op2Evaluation = evaluateOps(op1Evaluation.remainingOps)
if let operand2 = op2Evaluation.result {
// PROBLEM AREA...
let x = (operation(operand1, operand2), op2Evaluation.remainingOps)
println("results: \(x.0) remainder: \(x.1)")
return (x.0, x.1) // skipped during runtime...
}
}
}
else { ... }
}
}
println("returning nil")
return (nil, ops)
}
// troublesome method is called here...
let (result, remainder) = evaluateOps(opStack)
println("\(opStrings) = \(result) with \(remainderStrings) leftover")
Everything works so that, if I tried to calculate 5*3 for example, the console would read:
results: 15.0 remainder: []
returning nil
[5.0, ×, 3.0] = nil with [5.0, ×, 3.0] leftover
I think the problem might have something to do with the fact that, in the above code, if I tried to simply return x, I get a compile error that reads Cannot express tuple conversion '(Double, [CalculatorModel.Op])' to '(result: Double?, remainingOps: [CalculatorModel.Op])'. I also have no idea what to do with this.
In my researching the problem, I've discovered the downcasting keyword as (see altered code below), which removed the compile error when returning x rather than (x.0, x.1) but results in the same console result (except that now it says results: Optional(15.0) remainder: []):
let x = (operation(operand1, operand2) as Double?, op2Evaluation.remainingOps as [Op])
println("results: \(x.0) remainder: \(x.1)")
return x // no error but same result as return (x.0, x.1)
I've also tried sticking a println(getClassName(x.0!)) just before the return statement to make sure I even had a double, and it spit out __NSCFNumber... which I also researched, and found to be highly underdocumented (or at least not documented well enough to figure out how that's affecting me or how I can fix it!)
Although as you can see, it should be a double...
enum Op {
case Operand(Double)
case UnaryOperation(String, Double -> Double)
case BinaryOperation(String, (Double, Double) -> Double)
var description: String {
get {
switch self {
case .Operand(let operand):
return "\(operand)"
case .UnaryOperation(let symbol, _):
return "\(symbol)"
case .BinaryOperation(let symbol, _):
return "\(symbol)"
}
}
}
}
As you can see, everything's working perfectly fine as it's performing the calculation and everything...until it gets to that little troublesome area. I've searched StackOverflow and did a bit of Googling, but this seems to be a rather uncommon problem... Any help is appreciated! Let me know if you need any further information. Thanks!
EDIT:
getClassName(_:) is defined below (found it online somewhere...):
func getClassName(obj : AnyObject) -> String
{
let objectClass : AnyClass! = object_getClass(obj)
let className = objectClass.description()
return className
}