Swift: Identify the entered characters are in ascending/descending sequence - swift

I have a textfield where i can't set the text which is ascending/descending sequence. like "abcdefgh" or "1234567". how to identify the entered string is not in ascending/descending order in swift.

This should do the trick:
enum Order {
case ascending
case descending
case none
}
func getOrder(of text: String) -> Order {
switch text {
case String(text.sorted()):
return .ascending
case String(text.sorted().reversed()):
return .descending
default:
return .none
}
}

Others have proposed sorting the string and checking to see if the array changes with sorting. That's not quite right, since the string "abcxyz" is in sorted order, but is not a sequence because it's missing some characters. Sorting is also relatively expensive, at least on larger arrays. (Not an issue if you're dealing with a few hundred characters, since a good sort algorithm's O(n•log n) performance is pretty much O(n) performance for small data-sets.)
How about this extension on String I came up with:
extension String {
func isSequence() -> Bool
{
let charValues = Array(self.unicodeScalars).map { $0.value }
guard let first = charValues.first,
let last = charValues.last else { return false }
return charValues == Array(first...last)
}
}
It maps the string into an array of UTF32 values, and then checks to see if the result is the same as a range from first...last of the values.
You could use that function to validate a user's text input to see if it is a continuous sequence of characters like "abcdefg".
The above should have O(n) time complexity.

You could do it this way:
func isOrderedSequence(_ string: String) -> Bool {
return string == String(string.sorted())
}
Here are some test cases:
isOrderedSequence("1234567") //true
isOrderedSequence("7836") //false
isOrderedSequence("abcdefg") //true
isOrderedSequence("Hello") //false
isOrderedSequence("123abc") //true
It uses sorted()

Related

Count number of characters between two specific characters

Trying to make a func that will count characters in between two specified char like:
count char between "#" and "." or "#" and ".com"
If this is only solution could this code be written in a simple way with .count or something less confusing
func validateEmail(_ str: String) -> Bool {
let range = 0..<str.count
var numAt = Int()
numDot = Int()
if str.contains("#") && str.contains(".") && str.characters.first != "#" {
for num in range {
if str[str.index(str.startIndex, offsetBy: num)] == "#" {
numAt = num
print("The position of # is \(numAt)")
} else if
str[str.index(str.startIndex, offsetBy: num)] == "." {
numDot = num
print("The position of . is \(numDot)")
}
}
if (numDot - numAt) > 1 {
return true
}
}
return false
}
With help from #Βασίλης Δ. i made a direct if statement for func validateEmail that check if number of char in between are less than 1
if (str.split(separator: "#").last?.split(separator: ".").first!.count)! < 1{
return false
}
It could be usefull
There are many edge cases to what you're trying to do, and email validation is notoriously complicated. I recommend doing as little of it as possible. Many, many things are legal email addresses. So you will need to think carefully about what you want to test. That said, this addresses what you've asked for, which is the distance between the first # and the first . that follows it.
func lengthOfFirstComponentAfterAt(in string: String) -> Int? {
guard
// Find the first # in the string
let firstAt = string.firstIndex(of: "#"),
// Find the first "." after that
let firstDotAfterAt = string[firstAt...].firstIndex(of: ".")
else {
return nil
}
// Return the distance between them (not counting the dot itself)
return string.distance(from: firstAt, to: firstDotAfterAt) - 1
}
lengthOfFirstComponentAfterAt(in: "rob#example.org") // Optional(7)
There's a very important lesson about Collections in this code. Notice the expression:
string[firstAt...].firstIndex(of: ".")
When you subscript a Collection, each element of the resulting slice has the same index as in the original collection. The returned value from firstIndex can be used directly to subscript string without offsetting. This is very different than how indexes work in many other languages, and allows powerful algorithms, and also creates at lot of bugs when developers forget this.

Swift - How to check a string not included punctuations and numbers

I want to check a string to be able to understand that string is suitable for using as a display name in the app. Below block looks only for english characters. How can I cover all language letters? Also all punctuations and numbers won't be allowed.
func isSuitableForDisplayName(inputString: String) -> Bool {
let mergedString = inputString.stringByRemovingWhitespaces
let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
if mergedString.rangeOfCharacter(from: characterset.inverted) != nil {
return false
} else {
return true
}
}
You can use CharacterSet.letters, which contains all the characters in the Unicode categories L and M.
Category M includes combining marks. If you don't want those, use:
CharacterSet.letters.subtracting(.nonBaseCharacters)
Also, your way of checking whether a string contains only the characters in a character set is quite weird. I would do something like this:
return mergedString.trimmingCharacters(in: CharacterSet.letters) == ""

Fastest way to convert Character to uppercase or lowercase in Swift 4?

I understand the reasons why the Character class doesn't support toUpper() and toLower() but my use case is not for language purposes. Furthermore, I do not wish to revert to NSString.
So what's the fastest way to convert a character to upper case or lower case using Swift 4?
// Is there something better than this?
extension Character {
func toLower() -> Character {
return String(self).lowercased().first!
}
}
Use the uppercase2() below if you only need to uppercase the first char. It’s a 5x speed up over uppercasing the entire string.
import Foundation
// too slow, maybe with some bitwise operations could get faster 🤷‍♀️
func uppercase(_ string: String) -> Character? {
let key: Int8 = string.utf8CString[0]
guard key>0, key<127, let c = Unicode.Scalar(Int(key >= 97 ? key - Int8(32) : key)) else { return nil }
return Character(c)
}
// winner but using internal _core stuff
func uppercase2(_ string: String) -> Character? {
guard let key = string._core.asciiBuffer?[0] else { return nil }
return Character(Unicode.Scalar(key >= 97 ? key - 32 : key)) // use < + to lowercase
}
func measure(times: Int, task: ()->()){
let start1 = CFAbsoluteTimeGetCurrent()
for _ in 1..<times {
task()
}
print(CFAbsoluteTimeGetCurrent() - start1)
}
print("😀".uppercased().first as Any) // Optional("😀")
print(uppercase("😀") as Any) // nil
print(uppercase2("😀") as Any) // nil
measure(times: 10_000_000) { _ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".uppercased().first } // 4.17883902788162
measure(times: 10_000_000) { _ = uppercase("ABCDEFGHIJKLMNOPQRSTUVWXYZ") } // 4.91275697946548
measure(times: 10_000_000) { _ = uppercase2("ABCDEFGHIJKLMNOPQRSTUVWXYZ") } // 0.720575034618378
In a 10 million run, Apple’s uppercased ran 148x times faster than the code at the bottom of this post, even with force-unwrap. I’ll leave it for comedic purposes.
Their approach is of course, way lower level. See lowercased(). They check for an internal asciiBuffer and then use an _asciiUpperCaseTable.
My understanding is that if the original String is already a Swift String, it will be represented by a StringCore class which is already optimized to deal with ASCII characters at a low level. Thus, you won’t be able to beat the uppercase function of Swift.
So, kind of an answer: the fastest way is to use the regular uppercase() function.
I'm assuming that “my use-case is not for language purposes” means I’m only using ASCII. The advantage that this provides is that UTF-8 and ASCII share the same scalar code, so upper/lowercasing implies subtracting or adding a fixed number.
import Foundation
print("a".unicodeScalars.first!.value) // 97
print("A".unicodeScalars.first!.value) // 65
let uppercase = String("abcde".flatMap {
guard let char = $0.unicodeScalars.first,
let uppercased = Unicode.Scalar(char.value - UInt32(97 - 65))
else {
return nil
}
return Character(uppercased)
})
print(uppercase) // ABCDE

rindex in Swift 3

I think, there is no builtin function rindex (like in perl) to get the position of the last occurrence of a character in a string.
I tried in a naive way like this
func rindex (_ s:String, _ needle:Character) -> Int?
{ var pos = s.characters.count
for char in s.characters.reversed()
{
if (char == needle)
{ return pos;
}
pos -= 1
}
return nil
}
Is there a more elegant way for this?
First of all, I would recommend making this an extension of String (or even String.CharacterView) rather than a free-floating function. Also, given that strings are indexed by String.Index rather than Int, I would advise returning that instead.
You could then implement it like so:
extension String {
func lastIndex(of character: Character) -> Index? {
return characters.indices.reversed().first(where: {self[$0] == character})
}
}
indices gives you the indices of the string's characters.
reversed() gives you a reversed view onto these indices.
first(where:) iterates through the these reversed indices until it finds the index where the element at that index is the character you're looking for.
You can then use it like so:
let string = "foobarbaz"
if let index = string.lastIndex(of: "a") {
print(index) // Index(_base: Swift.String.UnicodeScalarView.Index(_position: 7), _countUTF16: 1)
print(string[index]) // "a"
}

Positions of a Character in a String with Swift 2

I'm making a string extension for finding multiple positions a character can occur in a string. This is my code:
let letter: Character = "l"
extension String {
func checkLetter(letter: Character) {
if let index = self.rangeOfString(AString: String(letter), range: Range<Int>(start: 2, end: 5) ) {
print(index)
}
}
}
I'm just completely lost on how to fill in that range part. There keep getting errors. I want to write a while loop which checks for the index of a character in a string. When found it will update a variabele which I can insert in range so it skips the part next time in the whole loop that contained the position of the character found before. Hope this makes it a bit clear. Here's some pseaduo code:
extension func
let range = 0
while loop: checks if character is in string
update range to index
append index to NSarray list
return nsarray list and if none found return nil
This is your extension written following the Functional Programming approach.
extension String {
func indexesOfChar(c: Character) -> [Int] {
return characters
.enumerate()
.filter { $0.element == c }
.map { $0.index }
}
}
Test
"Luminetic Land".indexesOfChar("i") // [3, 7]
Just for reference yourString.characters.indexOf("a") will give you the index of the first appearance of "a" in yourString. You could use this in a while loop to find "a" in the range from the previous index of "a" plus one and then add the indexes to an array until the output is negative one.