I currently have this Extension thanks to #Leo Dabus. It works perfectly for a single String, but how would I implement this same logic to an Set of Strings like var mySet = ["word", "notaword", "stillnotaword"]. In this example, I would want the function to only identify the first index as true (i.e. an English word).
extension String {
public mutating func isEnglishWord() -> Bool {
return UITextChecker().rangeOfMisspelledWord(in: self, range: NSRange(location: 0, length: utf16.count), startingAt: 0, wrap: false, language: "en_US").location == NSNotFound
}
}
var myString = "word"
myString.isEnglishWord()
let words = ["word", "notaword", "stillnotaword"]
let validWords = words.filter { word -> Bool in
return word.isEnglishWord()
}
let wordsArray : NSArray = NSArray(array: words)
let validWordsIndexes = wordsArray.indexesOfObjects { (word, index, _) -> Bool in
return (word as! String).isEnglishWord()
}
print(validWords)
print(validWordsIndexes)
extension String {
public func isEnglishWord() -> Bool {
return UITextChecker().rangeOfMisspelledWord(in: self, range: NSRange(location: 0, length: utf16.count), startingAt: 0, wrap: false, language: "en_US").location == NSNotFound
}
}
I have added the code to print valid words and indexes of valid words. You can choose which ever suits you. Thanks
Related
Basically, I want to make a program where if you start typing a name, the app will recognize it from the database and fill in the name for you. To do this, if a person types a comma after finishing a name, the app will start recording what the next name is. If it matches as a substring of one of the names in the database, it will fill it in for the user. The issue is that I need to get what part of the name has been filled out so far after the last occurrence of the comma character in the textField string, but I don't know how. For example:
User types: "Daniel, Joh"
And the app fills in John for you. Thanks.
If you really want the characters after the last comma, you could use a regular expression:
let string = "Hamilton, A"
let regex = try! NSRegularExpression(pattern: ",\\s*(\\S[^,]*)$")
if let match = regex.firstMatch(in: string, range: string.nsRange), let result = string[match.range(at: 1)] {
// use `result` here
}
Where, in Swift 4:
extension String {
/// An `NSRange` that represents the full range of the string.
var nsRange: NSRange {
return NSRange(startIndex ..< endIndex, in: self)
}
/// Substring from `NSRange`
///
/// - Parameter nsRange: `NSRange` within the string.
/// - Returns: `Substring` with the given `NSRange`, or `nil` if the range can't be converted.
subscript(nsRange: NSRange) -> Substring? {
return Range(nsRange, in: self)
.flatMap { self[$0] }
}
}
Many thanks to Rob. We can even extend his String extension by including the full answer to the initial question, with any Character:
extension String {
func substringAfterLastOccurenceOf(_ char: Character) -> String {
let regex = try! NSRegularExpression(pattern: "\(char)\\s*(\\S[^\(char)]*)$")
if let match = regex.firstMatch(in: self, range: self.nsRange), let result = self[match.range(at: 1)] {
return String(result)
}
return ""
}
// ... Rob's String extension
}
So we just need to call:
let subStringAfterLastComma = "Hamilton, A".substringAfterLastOccurenceOf(",")
Simple solution with range(of and options regularExpression and backwards.
It searches for a comma followed by an optional whitespace character.
extension String {
var subStringAfterLastComma : String {
guard let subrange = self.range(of: ",\\s?", options: [.regularExpression, .backwards]) else { return self }
return String(self[subrange.upperBound...])
}
}
let string1 = "Dan".subStringAfterLastComma // "Dan"
let string2 = "Daniel, Joh".subStringAfterLastComma // "Joh"
Thanks to Rob and Thierry G.
extension String {
var nsRange: NSRange {
return Foundation.NSRange(startIndex ..< endIndex, in: self)
}
subscript(nsRange: NSRange) -> Substring? {
return Range(nsRange, in: self)
.flatMap { self[$0] }
}
func substringAfterLastOccurenceOf(_ char: Character) -> String {
let regex = try! NSRegularExpression(pattern: "\(char)\\s*(\\S[^\(char)]*)$")
if let match = regex.firstMatch(in: self, range: self.nsRange), let result = self[match.range(at: 1)] {
return String(result)
}
return ""
}
}
we can use like this
let subStringAfterLastComma = "Hamilton, A".substringAfterLastOccurenceOf(",")
I have a huge text in String.
For example "... value=word. ...". How can I get the string "word" if I know that before I have "value=" and after "."?
for example:
for str in string {
if str == "value=" {
// then get the strings until .
}
}
Thanks!
You can extend String with a kind of sliceBetween method:
import Foundation
extension String {
func sliceFrom(start: String, to: String) -> String? {
guard let s = rangeOfString(start)?.endIndex else { return nil }
guard let e = rangeOfString(to, range: s..<endIndex)?.startIndex else { return nil }
return self[s..<e]
}
}
And you'd use it like this:
"... value=word. ...".sliceFrom("value=", to: ". ") // "word"
NSRegularExpression should solve your issue.
In order to use it, you will need to understand Regex first. In your case, you can use value=[\\w]+[^.]+ as your regex pattern.
The following code will give you a [String] object contains value=allCharacterBeforeFirstPeriod
let regex = try NSRegularExpression(pattern: "value=[\\w]+[^.]+", options: [])
let nsStr = str as NSString
let array = regex.matchesInString(str, options: [], range: NSMakeRange(0, nsStr.length))
let results = array.map({ nsStr.substringWithRange($0.range) })
And then if you only need the value after value=, you can use another map function to do it:
results.map({ $0.stringByReplacingOccurrencesOfString("value=", withString: "") })
I have tested the code with a 10,000 characters String. It finishes in ~0.3 sec
The most straight forward way to do this would be to use NSRegularExpression. Tutorial
Given an input String like this
let text = "key0=value0&key1=value1&key2=value2"
You can organireduce method
let dict = text.characters.split("&").reduce([String:String]()) { (var result, keyValue) -> [String:String] in
let chunks = keyValue.split("=")
guard let first = chunks.first, last = chunks.last else { return result }
let key = String(first)
let value = String(last)
result[key] = value
return result
}
Now everything is stored inside dict and you can easily access it
dict["key2"] // "value2"
I need to know if a string contains an Int to be sure that a name the user entered is a valid full name,
for that I need to either make the user type only chars, or valid that there are no ints in the string the user entered.
Thanks for all the help.
You can use Foundation methods with Swift strings, and that's what you should do here. NSString has built in methods that use NSCharacterSet to check if certain types of characters are present. This translates nicely to Swift:
var str = "Hello, playground1"
let decimalCharacters = CharacterSet.decimalDigits
let decimalRange = str.rangeOfCharacter(from: decimalCharacters)
if decimalRange != nil {
print("Numbers found")
}
If you're interested in restricting what can be typed, you should implement UITextFieldDelegate and the method textField(_:shouldChangeCharactersIn:replacementString:) to prevent people from typing those characters in the first place.
Simple Swift 4 version using rangeOfCharacter method from String class:
let numbersRange = stringValue.rangeOfCharacter(from: .decimalDigits)
let hasNumbers = (numbersRange != nil)
This method is what i use now for checking if a string contains a number
func doStringContainsNumber( _string : String) -> Bool{
let numberRegEx = ".*[0-9]+.*"
let testCase = NSPredicate(format:"SELF MATCHES %#", numberRegEx)
let containsNumber = testCase.evaluateWithObject(_string)
return containsNumber
}
If your string Contains a number it will return true else false. Hope it helps
//Swift 3.0 to check if String contains numbers (decimal digits):
let someString = "string 1"
let numberCharacters = NSCharacterSet.decimalDigits
if someString.rangeOfCharacter(from: numberCharacters) != nil
{ print("String contains numbers")}
else if someString.rangeOfCharacter(from: numberCharacters) == nil
{ print("String doesn't contains numbers")}
//A function that checks if a string has any numbers
func stringHasNumber(_ string:String) -> Bool {
for character in string{
if character.isNumber{
return true
}
}
return false
}
/// Check stringHasNumber function
stringHasNumber("mhhhldiddld")
stringHasNumber("kjkdjd99900")
if (ContainsNumbers(str).count > 0)
{
// Your string contains at least one number 0-9
}
func ContainsNumbers(s: String) -> [Character]
{
return s.characters.filter { ("0"..."9").contains($0)}
}
Swift 2.3. version working.
extension String
{
func containsNumbers() -> Bool
{
let numberRegEx = ".*[0-9]+.*"
let testCase = NSPredicate(format:"SELF MATCHES %#", numberRegEx)
return testCase.evaluateWithObject(self)
}
}
Usage:
//guard let firstname = textField.text else { return }
let testStr1 = "lalalala"
let testStr2 = "1lalalala"
let testStr3 = "lal2lsd2l"
print("Test 1 = \(testStr1.containsNumbers())\nTest 2 = \(testStr2.containsNumbers())\nTest 3 = \(testStr3.containsNumbers())\n")
You need to trick Swift into using Regex by wrapping up its nsRegularExpression
class Regex {
let internalExpression: NSRegularExpression
let pattern: String
init(_ pattern: String) {
self.pattern = pattern
var error: NSError?
self.internalExpression = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)
}
func test(input: String) -> Bool {
let matches = self.internalExpression.matchesInString(input, options: nil, range:NSMakeRange(0, countElements(input)))
return matches.count > 0
}
}
if Regex("\\d/").test("John 2 Smith") {
println("has a number in the name")
}
I got these from http://benscheirman.com/2014/06/regex-in-swift/
let numericCharSet = CharacterSet.init(charactersIn: "1234567890")
let newCharSet = CharacterSet.init(charactersIn: "~`##$%^&*(){}[]<>?")
let sentence = "Tes#ting4 #Charact2er1Seqt"
if sentence.rangeOfCharacter(from: numericCharSet) != nil {
print("Yes It,Have a Numeric")
let removedSpl = sentence.components(separatedBy: newCharSet).joined()
print(sentence.components(separatedBy: newCharSet).joined())
print(removedSpl.components(separatedBy: numericCharSet).joined())
}
else {
print("No")
}
let str = "tHIS is A test"
let swapped_case = "This IS a TEST"
Swift noob here, how to do the second statement programatically?
This function works with all upper/lowercase characters
defined in Unicode, even those from "foreign" languages such as Ä or ć:
func swapCases(_ str : String) -> String {
var result = ""
for c in str.characters { // Swift 1: for c in str {
let s = String(c)
let lo = s.lowercased() //Swift 1 & 2: s.lowercaseString
let up = s.uppercased() //Swift 1 & 2: s.uppercaseString
result += (s == lo) ? up : lo
}
return result
}
Example:
let str = "tHIS is a test ÄöÜ ĂćŒ Α" // The last character is a capital Greek Alpha
let swapped_case = swapCases(str)
print(swapped_case)
// This IS A TEST äÖü ăĆœ α
Use switch statement in-range checks to determine letter case, and use NSString-bridged methods to convert accordingly.
let str = "tHIS is A test"
let swapped_case = "This IS a TEST"
func swapCase(string: String) -> String {
var swappedCaseString: String = ""
for character in string {
switch character {
case "a"..."z":
let uppercaseCharacter = (String(character) as NSString).uppercaseString
swappedCaseString += uppercaseCharacter
case "A"..."Z":
let lowercaseCharacter = (String(character) as NSString).lowercaseString
swappedCaseString += lowercaseCharacter
default:
swappedCaseString += String(character)
}
}
return swappedCaseString
}
swapCase(str)
I'm a bit too late but this works too :-)
let str = "tHIS is A test"
var res = ""
for c in str {
if contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c) {
res += "\(c)".lowercaseString
} else {
res += "\(c)".uppercaseString
}
}
res
In Swift 5 I achieved it by creating a function which iterates through each character of the string, and using string methods to change each character I appended each character back into a new variable:
func reverseCase(string: String) -> String {
var newCase = ""
for char in string {
if char.isLowercase {
newCase.append(char.uppercased())
}
else if char.isUppercase {
newCase.append(char.lowercased())
}
else {
newCase.append(char)
}
}
return newCase
}
Then just pass your string through to the function when you call it in a print statement:
print(reverseCase(string: str))
You already have plenty of good succinct answers but here’s an over-elaborate one for fun.
Really this is a job for map – iterate over a collection (in this case String) and do a thing to each element (here, each Character). Except map takes any collection, but only gives you back an array, which you’d have to then turn into a String again.
But here’s a version of map that, given an extensible collection, gives you back that same kind of extensible collection.
(It does have the limitation of needing both collections to contain the same type, but that’s fine for strings. You could make it return a different type, but then you’d have to tell it which type you wanted i.e. map(s, transform) as String which would be annoying)
func map<C: ExtensibleCollectionType>(source: C, transform: (C.Generator.Element) -> C.Generator.Element) -> C {
var result = C()
for elem in source {
result.append(transform(elem))
}
return result
}
Then to write the transform function, first here’s an extension to character similar to the other answers. It does seem quite unsatisfying that you have to convert to a string just to uppercase a character, is there really no good (international characterset-friendly) way to do this?
extension Character {
var uppercaseCharacter: Character {
let s = String(self).uppercaseString
return s[s.startIndex]
}
var lowercaseCharacter: Character {
let s = String(self).lowercaseString
return s[s.startIndex]
}
}
And the function to flip the case. What I wonder is whether this pattern matching is international-friendly. It seems to be – "A"..."Z" ~= "Ä" returns true.
func flipCase(c: Character) -> Character {
switch c {
case "A"..."Z":
return c.lowercaseCharacter
case "a"..."z":
return c.uppercaseCharacter
default:
return c
}
}
Finally:
let s = map("Hello", flipCase)
// s is a String = "hELLO"
I hope this helps. inputString and resultString are the input and output respectively.
let inputString = "Example"
let outputString = inputString.characters.map { (character) -> Character in
let string = String(character)
let lower = string.lowercased()
let upper = string.uppercased()
return (string == lower) ? Character(upper) : Character(lower)
}
let resultString = String(outputString)
I am trying to validate a form to make sure the user has entered an integer number and not a string. I can check if the number is an integer as follows:
var possibleNumber = timeRetrieved.text
convertedNumber = possibleNumber.toInt()
// convertedNumber is inferred to be of type "Int?", or "optional Int"
if convertedNumber != nil {
println("It's a number!")
totalTime = convertedNumber!
}
My problem is I want to make sure the user has not entered any text, doubles etc. I only want integer numbers. The following code does not work because it evaluates true if the variable is an integer. What code should I use to evaluate if variable is not an integer?
if convertedNumber != nil {
let alertController = UIAlertController(title: "Validation Error", message: "You must enter an integer number!", preferredStyle: .Alert)
let alertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Destructive, handler: {(alert : UIAlertAction!) in
alertController.dismissViewControllerAnimated(true, completion: nil)
})
alertController.addAction(alertAction)
presentViewController(alertController, animated: true, completion: nil)
Swift 2 changes this: as both Int("abc") and Int("0") return 0, integer conversion can't be used. You could use this:
class Validation {
static func isStringNumerical(string : String) -> Bool {
// Only allow numbers. Look for anything not a number.
let range = string.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
return (range == nil)
}
}
It uses a decimalDigitCharacterSet, and can be changed to use whatever character set you want.
func testIsStringNumerical() {
XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical("123"), true)
XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical(""), true)
XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical("12AA"), false)
XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical("123.4"), false)
}
This is dramatically faster than the Regex answer. (2000 runs, 0.004s vs regex 0.233s)
If the number the user has entered is not an integer, convertedNumber will be nil. Just add an else clause in which you can show the alert.
Int initializer
This works in Swift 2.2 and above. It is based on Minhal Khan's answer which illustrates that Int has an initializer with this signature: init?(_ text: String, radix: Int = default). Since radix has a default value, it can be left out. *more info on this initializer is found here.
var totalTime: Int?
let possibleInt = timeRetrieved.text ?? ""
if let convertedNumber = Int(possibleInt) {
print("'\(possibleInt)' is an Int")
totalTime = convertedNumber
}
else {
print("'\(possibleInt)' is not an Int")
}
print("totalTime: '\(totalTime)'")
Note: I assumed timeRetrieved is a UITextField. The UITextField text property is an optional string (though programmatically not allowed to be nil). Therefore, the compiler requires it be unwrapped. I used the nil coalescing operator (??) to substitute a nil for empty string which does not yield an integer as desired. Here's a post that discusses the optionality of UITextfield.text.
What i had done was get the value and check if it could convert it, works for me
var enteredText = Int(textfield.text)
if enteredText == nil{
//String entered
}
else{
//Int entered
}
Based on #Graham Perks answer a Swift 3 Version as string extension:
extension String
{
var isNumeric: Bool
{
let range = self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted)
return (range == nil)
}
}
Usage:
"123".isNumeric // true
"abc".isNumeric // false
I really recommend using a REGEX, I was recently trying to validate 10 digit phone numbers using if let _ = Int(stringToTest)... and on 32 bit hardware, I faced range issues.
func validate(value: String) -> Bool {
let PHONE_REGEX = "\\d{10}"
let phoneTest = NSPredicate(format: "SELF MATCHES %#", PHONE_REGEX)
let result = phoneTest.evaluateWithObject(value)
if result == true {
log.info("'\(self.text!)' is a valid number.")
} else {
log.info("'\(self.text!)' is an invalid number.")
}
return result
}