How to check for palindrome in Swift using recursive definition - swift

I like many of the features in Swift, but using manipulating strings are still a big pain in the ass.
func checkPalindrome(word: String) -> Bool {
print(word)
if word == "" {
return true
} else {
if word.characters.first == word.characters.last {
return checkPalindrome(word.substringWithRange(word.startIndex.successor() ..< word.endIndex.predecessor()))
} else {
return false
}
}
}
This code fails miserably whenever the string's length is an odd number. Of course I could make it so the first line of the block would be if word.characters.count < 2, but is there a way in Swift to get substrings and check easily?
Update
I like many of the suggestions, but I guess the original question could be misleading a little, since it's a question about String more than getting the right results for the function.
For instance, in Python, checkPalindrome(word[1:-1]) would work fine for the recursive definition, whereas Swift code is much less graceful since it needs other bells and whistles.

return word == String(word.reversed())

func isPalindrome(myString:String) -> Bool {
let reverseString = String(myString.characters.reversed())
if(myString != "" && myString == reverseString) {
return true
} else {
return false
}
}
print(isPalindrome("madam"))

I have used the below extension to find whether the number is Palindrome or Not.
extension String {
var isPalindrome: Bool {
return self == String(self.reversed())
}
}

Sometimes having a front end for a recursion can simplify life. I sometimes do this when the arguments which are most convenient to use are not what I want in the user interface.
Would the following meet your needs?
func checkPalindrome(str: String) -> Bool {
func recursiveTest(var charSet: String.CharacterView) -> Bool {
if charSet.count < 2 {
return true
} else {
if charSet.popFirst() != charSet.popLast() {
return false
} else {
return recursiveTest(charSet)
}
}
}
return recursiveTest(str.characters)
}

just add on more condition in if
func checkPalindrome(word: String) -> Bool {
print(word)
if (word == "" || word.characters.count == 1){
return true
}
else {
if word.characters.first == word.characters.last {
return checkPalindrome(word.substringWithRange(word.startIndex.successor() ..< word.endIndex.predecessor()))
} else {
return false
}
}
}

extension StringProtocol where Self: RangeReplaceableCollection {
var letters: Self { filter(\.isLetter) }
var isPalindrome: Bool {
let letters = self.letters
return String(letters.reversed()).caseInsensitiveCompare(letters) == .orderedSame
}
}
"Dammit I'm Mad".isPalindrome // true
"Socorram-me subi no onibus em marrocos".isPalindrome // true
You can also break your string into an array of characters and iterate through them until its half comparing one by one with its counterpart:
func checkPalindrome(_ word: String) -> Bool {
let chars = Array(word.letters.lowercased())
for index in 0..<chars.count/2 {
if chars[index] != chars[chars.count - 1 - index] {
return false
}
}
return true
}
And the recursive version fixing the range issue where can't form a range with endIndex < startIndex:
func checkPalindrome<T: StringProtocol>(_ word: T) -> Bool {
let word = word.lowercased()
.components(separatedBy: .punctuationCharacters).joined()
.components(separatedBy: .whitespacesAndNewlines).joined()
if word == "" || word.count == 1 {
return true
} else {
if word.first == word.last {
let start = word.index(word.startIndex,offsetBy: 1, limitedBy: word.endIndex) ?? word.startIndex
let end = word.index(word.endIndex,offsetBy: -1, limitedBy: word.startIndex) ?? word.endIndex
return checkPalindrome(word[start..<end])
} else {
return false
}
}
}
checkPalindrome("Dammit I'm Mad")

I think if you make an extension to String like this one then it will make your life easier:
extension String {
var length: Int { return characters.count }
subscript(index: Int) -> Character {
return self[startIndex.advancedBy(index)]
}
subscript(range: Range<Int>) -> String {
return self[Range<Index>(start: startIndex.advancedBy(range.startIndex), end: startIndex.advancedBy(range.endIndex))]
}
}
With it in place, you can change your function to this:
func checkPalindrome(word: String) -> Bool {
if word.length < 2 {
return true
}
if word.characters.first != word.characters.last {
return false
}
return checkPalindrome(word[1..<word.length - 1])
}
Quick test:
print(checkPalindrome("aba")) // Prints "true"
print(checkPalindrome("abc")) // Prints "false"

extension String {
func trimmingFirstAndLastCharacters() -> String {
guard let startIndex = index(self.startIndex, offsetBy: 1, limitedBy: self.endIndex) else {
return self
}
guard let endIndex = index(self.endIndex, offsetBy: -1, limitedBy: self.startIndex) else {
return self
}
guard endIndex >= startIndex else {
return self
}
return String(self[startIndex..<endIndex])
}
var isPalindrome: Bool {
guard count > 1 else {
return true
}
return first == last && trimmingFirstAndLastCharacters().isPalindrome
}
}
We first declare a function that removes first and last characters from a string.
Next we declare a computer property which will contain the actual recursive code that checks if a string is palindrome.
If string's size is less than or equal 1 we immediately return true (strings composed by one character like "a" or the empty string "" are considered palindrome), otherwise we check if first and last characters of the string are the same and we recursively call isPalindrome on the current string deprived of the first and last characters.

Convert the string into an Array. When the loop is executed get the first index and compare with the last index.
func palindrome(string: String)-> Bool{
let char = Array(string)
for i in 0..<char.count / 2 {
if char[i] != char[char.count - 1 - i] {
return false
}
}
return true
}

This solution is not recursive, but it is a O(n) pure index based solution without filtering anything and without creating new objects. Non-letter characters are ignored as well.
It uses two indexes and walks outside in from both sides.
I admit that the extension type and property name is stolen from Leo, I apologize. 😉
extension StringProtocol where Self: RangeReplaceableCollection {
var isPalindrome : Bool {
if isEmpty { return false }
if index(after: startIndex) == endIndex { return true }
var forward = startIndex
var backward = endIndex
while forward < backward {
repeat { formIndex(before: &backward) } while !self[backward].isLetter
if self[forward].lowercased() != self[backward].lowercased() { return false }
repeat { formIndex(after: &forward) } while !self[forward].isLetter
}
return true
}
}

Wasn't really thinking of this, but I think I came up with a pretty cool extension, and thought I'd share.
extension String {
var subString: (Int?) -> (Int?) -> String {
return { (start) in
{ (end) in
let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)
return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
}
}
}
}
let test = ["Eye", "Pop", "Noon", "Level", "Radar", "Kayak", "Rotator", "Redivider", "Detartrated", "Tattarrattat", "Aibohphobia", "Eve", "Bob", "Otto", "Anna", "Hannah", "Evil olive", "Mirror rim", "Stack cats", "Doom mood", "Rise to vote sir", "Step on no pets", "Never odd or even", "A nut for a jar of tuna", "No lemon, no melon", "Some men interpret nine memos", "Gateman sees name, garageman sees nametag"]
func checkPalindrome(word: String) -> Bool {
if word.isEmpty { return true }
else {
if word.subString(nil)(1) == word.subString(-1)(nil) {
return checkPalindrome(word.subString(1)(-1))
} else {
return false
}
}
}
for item in test.map({ $0.lowercaseString.stringByReplacingOccurrencesOfString(",", withString: "").stringByReplacingOccurrencesOfString(" ", withString: "") }) {
if !checkPalindrome(item) {
print(item)
}
}

A simple solution in Swift:
func isPalindrome(word: String) -> Bool {
// If no string found, return false
if word.count == 0 { return false }
var index = 0
var characters = Array(word) // make array of characters
while index < characters.count / 2 { // repeat loop only for half length of given string
if characters[index] != characters[(characters.count - 1) - index] {
return false
}
index += 1
}
return true
}

func checkPalindrome(_ inputString: String) -> Bool {
if inputString.count % 2 == 0 {
return false
} else if inputString.count == 1 {
return true
} else {
var stringCount = inputString.count
while stringCount != 1 {
if inputString.first == inputString.last {
stringCount -= 2
} else {
continue
}
}
if stringCount == 1 {
return true
} else {
return false
}
}
}

Related

CodingBat string_bits problem solved using swit for loop

Question:
Given a string, return a new string made of every other char starting with the first, so "Hello" yields "Hlo".
string_bits('Hello') → 'Hlo'
string_bits('Hi') → 'H'
string_bits('Heeololeo') → 'Hello'
Solution:
func string_bits(userString: String) ->String{
var myString = ""
for(i, v) in userString.enumerated(){
if i % 2 == 0{
myString.append(v)
}
}
return myString
}
Output: Hello
Now my question:
Is there any I can iterate my index any way in swift like object-c, c, or other programming languages does. For instance:
result = ""
# On each iteration, add the substring of the chars 0..i
for i in range(len(str)):
result = result + str[:i+1]
return result
str[:i+1]
Here, I am adding +1 with the current index and getting the index value. How can I do this in swift.
extension Collection {
func everyNthIndex(n: Int) -> UnfoldSequence<Index,Index> {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { index = self.index(index, offsetBy: n, limitedBy: endIndex) ?? endIndex }
return index
}
}
}
let alphabet = "abcdefghijklmnopqrstuvwxyz"
for evenIndex in alphabet.everyNthIndex(n: 2) {
print("evenIndex", evenIndex, "char:", alphabet[evenIndex])
}
for oddIndex in alphabet.dropFirst().everyNthIndex(n: 2) {
print("oddIndex", oddIndex, "char:", alphabet[oddIndex])
}
regular approach using while loop:
var index = alphabet.startIndex
while index < alphabet.endIndex {
defer { index = alphabet.index(index, offsetBy: 1) }
print(alphabet[index])
print(index)
}
or enumerating the string indices:
func string_bits(userString: String) -> String {
var myString = ""
for (offset,index) in userString.indices.enumerated() {
if offset.isMultiple(of: 2) {
myString.append(userString[index])
}
}
return myString
}

Palindrome Swift CCC J3

I'm trying to figure out how to find a Palindrome in a specific word given. ie - banana the Palindrome would be anana. I have a function that allows me to check if the whole word is one but I don't know how to check in the word if any part of it is a Palindrome. Any ideas on what could fix this?
var word = "anaana"
func isPali(word:String) -> Bool {
let characters = Array(word)
var currentIndex = 0
while currentIndex < characters.count / 2 {
if characters[currentIndex] != characters[characters.count - currentIndex - 1] {
return false
}
currentIndex += 1
}
return true
}
if isPali(word: word) {
print(word.count)
}else{
print("Not a Pailidrome")
}
You can try the below functions.
func isPalindrome(_ word: String) -> Bool {
return word == String(word.reversed())
}
func findPalindrome(in word: String) -> String? {
var subString: String = word
for i in 0...word.count {
guard isPalindrome(subString) == false else { return subString }
subString = word
subString.remove(at: word.index(word.startIndex, offsetBy: i))
}
return nil
}
The isPalindrome will simply check if a word is palindrome. The findPalindrome function will check for all the subStrings in the given word if there is any palindrome and if it exist, that string will be returned.

How to create increment number string?

Like my question title.
If I get string 00001, I want to +1 and return 00002.
If I get string 00009, I want to +1 and return 00010.
....
If I get string 89999, I want to +1 and return 90000.
How to make this function looks better and easier? Thanks.
func createFloatNumberString(str: String){
var idArray: [String] = [] //[0,0,0,0,1] [0,0,0,1,0] [8,9,9,9,9]
str.map { (char) in
idArray.append(char.description)
}
idArray[4] = String(Int(idArray[4])!+1)
if idArray[4] == "10" {
idArray[4] = "0"
idArray[3] = String(Int(idArray[3])!+1)
if idArray[3] == "10" {
idArray[3] = "0"
idArray[2] = String(Int(idArray[2])!+1)
if idArray[2] == "10" {
idArray[2] = "0"
idArray[1] = String(Int(idArray[1])!+1)
if idArray[1] == "10" {
idArray[1] = "0"
idArray[0] = String(Int(idArray[0])!+1)
}
}
}
}
}
You need to convert the String to an Int, increment it, then use a formatted String as the return value.
func incrementFormattedNumericString(numericString:String,increment by:Int=1) -> String? {
guard let numericValue = Int(numericString) else { return nil }
return String(format: "%.5ld", numericValue+by)
}
If you are sure that the numericString passed into the function will always be a valid number, you can force unwrap the conversion to Int and return a non-Optional value.
Very simple solution:
As counter use an Int and this extension
extension Int {
var fiveDigitStringRepresentation : String {
return String(format: "%05ld", self)
}
}
123.fiveDigitStringRepresentation // "00123"
Or – a bit more sophisticated – use a struct
struct Count {
var counter = 0
mutating func increment() { counter += 1 }
var fiveDigitStringRepresentation : String {
return String(format: "%05ld", counter)
}
}
var count = Count()
count.increment()
count.increment()
print(count.fiveDigitStringRepresentation) // "00002"

How to remove duplicate characters from a string in Swift

ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr

How to check is a string or number?

I have an array ["abc", "94761178","790"]
I want to iterate each and check is a String or an Int?
How to check it?
How to convert "123" to integer 123?
Here is a small Swift version using String extension :
Swift 3/Swift 4 :
extension String {
var isNumber: Bool {
return !isEmpty && rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
}
}
Swift 2 :
extension String {
var isNumber : Bool {
get{
return !self.isEmpty && self.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet) == nil
}
}
}
Edit Swift 2.2:
In swift 2.2 use Int(yourArray[1])
var yourArray = ["abc", "94761178","790"]
var num = Int(yourArray[1])
if num != nil {
println("Valid Integer")
}
else {
println("Not Valid Integer")
}
It will show you that string is valid integer and num contains valid Int.You can do your calculation with num.
From docs:
If the string represents an integer that fits into an Int, returns the
corresponding integer.This accepts strings that match the regular
expression "[-+]?[0-9]+" only.
Be aware that checking a string/number using the Int initializer has limits. Specifically, a max value of 2^32-1 or 4294967295. This can lead to problems, as a phone number of 8005551234 will fail the Int(8005551234) check despite being a valid number.
A much safer approach is to use NSCharacterSet to check for any characters matching the decimal set in the range of the string.
let number = "8005551234"
let numberCharacters = NSCharacterSet.decimalDigitCharacterSet().invertedSet
if !number.isEmpty && number.rangeOfCharacterFromSet(numberCharacters) == nil {
// string is a valid number
} else {
// string contained non-digit characters
}
Additionally, it could be useful to add this to a String extension.
public extension String {
func isNumber() -> Bool {
let numberCharacters = NSCharacterSet.decimalDigitCharacterSet().invertedSet
return !self.isEmpty && self.rangeOfCharacterFromSet(numberCharacters) == nil
}
}
I think the nicest solution is:
extension String {
var isNumeric : Bool {
return Double(self) != nil
}
}
Starting from Swift 2, String.toInt() was removed.
A new Int Initializer was being introduced: Int(str: String)
for target in ["abc", "94761178","790"]
{
if let number = Int(target)
{
print("value: \(target) is a valid number. add one to get :\(number+1)!")
}
else
{
print("value: \(target) is not a valid number.")
}
}
Swift 3, 4
extension String {
var isNumber: Bool {
let characters = CharacterSet.decimalDigits.inverted
return !self.isEmpty && rangeOfCharacter(from: characters) == nil
}
}
Simple solution like this:
extension String {
public var isNumber: Bool {
return !isEmpty && rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
}
}
I think using NumberFormatter is an easy way:
(Swift 5)
import Foundation
extension String {
private static let numberFormatter = NumberFormatter()
var isNumeric : Bool {
Self.numberFormatter.number(from: self) != nil
}
}
The correct way is to use the toInt() method of String, and an optional binding to determine whether the conversion succeeded or not. So your loop would look like:
let myArray = ["abc", "94761178","790"]
for val in myArray {
if let intValue = val.toInt() {
// It's an int
println(intValue)
} else {
// It's not an int
println(val)
}
}
The toInt() method returns an Int?, so an optional Int, which is nil if the string cannot be converted ton an integer, or an Int value (wrapped in the optional) if the conversion succeeds.
The method documentation (shown using CMD+click on toInt in Xcode) says:
If the string represents an integer that fits into an Int, returns the corresponding integer. This accepts strings that match the regular expression "[-+]?[0-9]+" only.
This way works also with strings with mixed numbers:
public extension String {
func isNumber() -> Bool {
return !self.isEmpty && self.rangeOfCharacter(from: CharacterSet.decimalDigits) != nil && self.rangeOfCharacter(from: CharacterSet.letters) == nil
}}
So u get something like this:
Swift 3.0 version
func isNumber(stringToTest : String) -> Bool {
let numberCharacters = CharacterSet.decimalDigits.inverted
return !s.isEmpty && s.rangeOfCharacter(from:numberCharacters) == nil
}
If you want to accept a more fine-grained approach (i.e. accept a number like 4.5 or 3e10), you proceed like this:
func isNumber(val: String) -> Bool
{
var result: Bool = false
let parseDotComNumberCharacterSet = NSMutableCharacterSet.decimalDigitCharacterSet()
parseDotComNumberCharacterSet.formUnionWithCharacterSet(NSCharacterSet(charactersInString: ".e"))
let noNumberCharacters = parseDotComNumberCharacterSet.invertedSet
if let v = val
{
result = !v.isEmpty && v.rangeOfCharacterFromSet(noNumberCharacters) == nil
}
return result
}
For even better resolution, you might draw on regular expression..
Xcode 8 and Swift 3.0
We can also check :
//MARK: - NUMERIC DIGITS
class func isString10Digits(ten_digits: String) -> Bool{
if !ten_digits.isEmpty {
let numberCharacters = NSCharacterSet.decimalDigits.inverted
return !ten_digits.isEmpty && ten_digits.rangeOfCharacter(from: numberCharacters) == nil
}
return false
}
This code works for me for Swift 3/4
func isNumber(textField: UITextField) -> Bool {
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: textField.text!)
return allowedCharacters.isSuperset(of: characterSet)
// return true
}
You can use this for integers of any length.
func getIntegerStrings(from givenStrings: [String]) -> [String]
{
var integerStrings = [String]()
for string in givenStrings
{
let isValidInteger = isInteger(givenString: string)
if isValidInteger { integerStrings.append(string) }
}
return integerStrings
}
func isInteger(givenString: String) -> Bool
{
var answer = true
givenString.forEach { answer = ("0"..."9").contains($0) && answer }
return answer
}
func getIntegers(from integerStrings: [String]) -> [Int]
{
let integers = integerStrings.compactMap { Int($0) }
return integers
}
let strings = ["abc", "94761178", "790", "18446744073709551615000000"]
let integerStrings = getIntegerStrings(from: strings)
let integers = getIntegers(from: integerStrings)
print(integerStrings) // ["94761178", "790", "18446744073709551615000000"]
print(integers) // [94761178, 790]
However, as pointed out by #Can, you can get the integer value for the number only up to 2^31 - 1 (signed integer limit on 32-bit arch). For the larger value, however, you will still get the string representation.
This code will return an array of converted integers:
["abc", "94761178","790"].map(Int.init) // returns [ nil, 94761178, 790 ]
OR
["abc", "94761178","790"].map { Int($0) ?? 0 } // returns [ 0, 94761178, 790 ]
Get the following isInteger() function from the below stackoverflow post posted by corsiKa:
Determine if a String is an Integer in Java
And I think this is what you want to do (where nameOfArray is the array you want to pass)
void convertStrArrayToIntArray( int[] integerArray ) {
for (int i = 0; i < nameOfArray.length(); i++) {
if (!isInteger(nameOfArray[i])) {
integerArray[i] = nameOfArray[i].toString();
}
}
}