How to check if a string only contains numbers in Swift 2? - string

Before Swift 2 I used this extension to check if a string only is made out of numbers:
func isNumbersOnly() -> Bool {
let regexNumbersOnly = NSRegularExpression(pattern: ".*[^0-9].*", options: nil, error: nil)!
return regexNumbersOnly.firstMatchInString(self, options: nil, range: NSMakeRange(0, self.length())) != nil
}
but now with Swift 2 I get the error
Cannot invoke initializer for type 'NSRegularExpression' with an
argument list of type '(pattern: String, options:
NilLiteralConvertible, error: NilLiteralConvertible)'
Is there a better known way now? Thnx!

In Swift 2 NSRegularExpression "throws" so you have to use it with try.
Also you can't pass nil for options anymore: if you don't want to specify options, pass an empty array (same for firstMatchInString).
And self.length() should become self.characters.count.
Last note: if the goal is to determine if a String contains only numbers, and since you're naming it "isNumbersOnly", the resulting Boolean should be true if there's only numbers: it's currently the inverse. I've fixed this in my example.
Ignoring errors:
let regexNumbersOnly = try! NSRegularExpression(pattern: ".*[^0-9].*", options: [])
return regexNumbersOnly.firstMatchInString(self, options: [], range: NSMakeRange(0, self.characters.count)) == nil
With proper error handling:
do {
let regexNumbersOnly = try NSRegularExpression(pattern: ".*[^0-9].*", options: [])
return regexNumbersOnly.firstMatchInString(self, options: [], range: NSMakeRange(0, self.characters.count)) == nil
} catch let error as NSError {
print(error.description)
}

Instead of using regular expressions, you can use CharacterSets to check for the existence (or absence) of certain characters. To check if the string is only digits you can use the following:
extension String {
var isDigits: Bool {
if isEmpty { return false }
// The inverted set of .decimalDigits is every character minus digits
let nonDigits = CharacterSet.decimalDigits.inverted
return rangeOfCharacter(from: nonDigits) == nil
}
}
This method can be applied to any type of CharacterSet and, in my opinion, is a lot cleaner than using regex strings.

Swift 2 has adjusted the error-handling process; you should now try the call, not specifying an error and be prepared to catch an exception.
E.g.
do {
let regexNumbersOnly = try NSRegularExpression(pattern: ..., options: nil)
... etc ...
} catch _ {}
... given that you're electing not to handle error states.

Related

Is there a way to a prevent a String from being interpolated in Swift?

I'm experimenting around the idea of a simple logger that would look like this:
log(constant: String, _ variable: [String: AnyObject]? = nil)
Which would be used like this:
log("Something happened", ["error": error])
However I want to prevent misuse of the constant/variable pattern like the following:
log("Something happened: \(error)") // `error` should be passed in the `variable` argument
Is there a way to make sure that constant wasn't constructed with a string interpolation?
You could use StaticString instead of String:
func log(constant: StaticString, _ variable: [String: AnyObject]? = nil) {
// You can retrieve `String` from `StaticString`
let msg = constant.stringValue
}
let foo = 1
log("test \(foo)") // -> error: cannot invoke 'log' with an argument list of type '(String)'

How do you inspect an Optional-String's length?

class PersonEntry: NSObject {
var firstName: String?
var lastName: String?
}
//This errors
if (self.person.firstName?.isEmpty) {
println("Empty")
}
//Compiler auto-correction is this
if ((self.model.firstName?.isEmpty) != nil) {
println("Empty")
}
I understand that optional chaining returns an optional type. So I suppose my question is, how do you unwrap an optional string, to inspect it's length, without risking a crash ?
I presume that if the property is nil then you want to consider it empty - in that case you can use the nil coalescing operator in combination with the first version of the if statement:
if self.person.firstName?.isEmpty ?? true {
println("Empty")
}
If firstName is nil, the expression evaluates to the right side of the coalescing operator, true - otherwise it evaluates to the value of the isEmpty property.
References:
Nil Coalescing Operator
Optional Chaining
Another trick.
var str: String?
str = "Hello, playground"
println(count(str ?? ""))

How can i replace a specific word in a string in swift?

I am looking for a way to replace a word inside a string in swift. Can anyone help?
this is what I have so far, I can find the specific word, but i do not know how to replace it...
var str = "helo, playgound"
var findWords = ["helo","playgound"]
var replaceWords = ["hello","playground"]
extension String {
var wordList:[String] {
return "".join(componentsSeparatedByCharactersInSet(NSCharacterSet.punctuationCharacterSet())).componentsSeparatedByString(" ")
}
}
func stringToArray() -> Array<String> {
var arr = str.wordList
return arr
}
func correction(var _arr:Array<String>) -> String{
for var i = 0; i < _arr.count; i++ {
if str.lowercaseString.rangeOfString(findWords[i]) != nil {
println("exists")
}
}
return str
}
It depends what your definition of a "word" is. If you're looking for an intelligent built-in notion of a "word", the easiest solution is probably to use NSRegularExpression, which knows where "word" boundaries are:
var s = NSMutableString(string:"hello world, go to hell")
let r = NSRegularExpression(
pattern: "\\bhell\\b",
options: .CaseInsensitive, error: nil)!
r.replaceMatchesInString(
s, options: nil, range: NSMakeRange(0,s.length),
withTemplate: "heaven")
After that, s is "hello world, go to heaven", which is the right answer; we replaced the "hell" that is a word, but not the "hell" in "hello". Notice that we are also matching case-insensitively, which seems to be one of your desiderata.
That example shows how do just one pair ("hell" and "heaven") but it is easy to abstract it into a method so that you can do it again and again for further pairs:
var str = "helo, playgound"
var findWords = ["helo", "playgound"]
var replaceWords = ["hello", "playground"]
func correct(str:String, orig:String, repl:String) -> String {
var s = NSMutableString(string:str)
let r = NSRegularExpression(
pattern: "\\b\(orig)\\b",
options: .CaseInsensitive, error: nil)!
r.replaceMatchesInString(
s, options: nil, range: NSMakeRange(0,s.length),
withTemplate: repl)
return s
}
for pair in Zip2(findWords,replaceWords) {
str = correct(str, pair.0, pair.1)
}
str // hello, playground
The easiest is probably this:
let statement = "Swift is hard."
let swiftRange = statement.startIndex..<advance(statement.startIndex, 5)
let newStatement = statement.stringByReplacingCharactersInRange(swiftRange, withString: "Objective-C")
// now newStatement = "Objective-C is hard."
Following a longer commenting tour: The above is under the assumption of the OP "I can find the specific word, but i do not know how to replace it...", so it's not about finding a "word" which to define is another discussion. It's just about replacing an already found word.
Another word on stringByReplacingCharactersInRange: #matt states that this is Cocoa cross-over. In that case Apple is telling a plain lie:
I fostered the web but there's no Apple source telling anything. Only the Foundation method for NSString. Their Swift book is silent too (in many respects). Well, I don't trust Apple anyway any longer since Yosemite-fail.

How to use if let with another statement in swift?

If want to both assign a string and check that its not empty in Swift.
if let alternative3Text = attributes.stringForKey("choiceThree") && alternative3Text != "" {
// do stuff with alternative3Text
}
Is this possible in Swift, or do i have to do a nested if-statement?
Update: As of Swift 3 (Xcode 8), additional clauses are
separated by a comma, not by where:
if let alternative3Text = attributes.string(forKey: "choiceThree"),
alternative3Text != "" {
// do stuff with alternative3Text
}
Update: As of Swift 1.2 (Xcode 6.3 beta), you can combine
optional binding with additional conditions:
if let alternative3Text = attributes.stringForKey("choiceThree") where alternative3Text != "" {
// do stuff with alternative3Text
}
Using switch-case still works but is not necessary anymore for this purpose.
Old answer:
It is not possible with an if statement, but with switch.
A switch case can use a where clause to check for additional conditions
(documentation).
Assuming (from your question) that attributes.stringForKey("choiceThree") returns
String?, the following would work:
switch (attributes.stringForKey("choiceThree")) {
case .Some(let alternative3Text) where alternative3Text != "":
// alternative3Text is the unwrapped String here
default:
break
}
No, you can't require additional expressions to be true in an if let statement. You will need to add additional code to do this in either the form of a nested if statement as you've already mentioned, or in some other way. If your only requirement is to keep this statement looking clean and wouldn't mind moving some of the logic elsewhere, you could always make an extension to what ever type your attributes variable is to add this functionality.
Here's an example if attributes was an instance of NSUserDefaults. (just because it already contains a stringForKey() instance method.)
extension NSUserDefaults {
func nonEmptyStringForKey(key: String) -> String? {
let full = self.stringForKey(key)
return full != "" ? full : nil
}
}
And then use it like this
if let alternative3Text = attributes.nonEmptyStringForKey("choiceThree") {
// stuff
}

Compare enum without considering its arguments

Let me make this clear, I have this enum:
enum Token {
Number(v:Float);
Identifier(v:String);
TString(v:String);
Var;
Assign;
Division;
// and so on
}
I want to check if the value of a variable is an Identifier, but this doesn't work:
if(tk == Token.Identifier) {
It only allows me to compare the values if I pass arguments:
if(tk == Token.Identifier('test')) {
But this will only match if the identifier is 'test', but I want to match any identifier.
Type.enumConstructor(tk) == "Identifier"
Read the Type doc for more methods on enum.
Update (2019-02-04):
At the time of writing this answer it was still Haxe 2.06. Much have changed since then.
At this moment, for Haxe 3 (or 4), I would recommend pattern matching, specifically using single pattern check instead:
if (tk.match(Identifier(_)) ...
which is a short hand for
if (switch tk { case Identifier(_): true; case _: false; }) ...
_ is the wildcard that matches anything.
alternatively:
static function isIdentifier(token : Token) return switch(token) { case Token.Identifier(_): true; default: false; }
Using "using" you should also be able to do:
if(tk.isIdentifier()) {
Or even:
tk.match(Token.Identifier(_));

Resources