A set is not equal but an array from this set is equal? - swift

I work with unit-tests and encounter this problem:
I have some classes and each have their own isEqual() method. At some point I came to a situation where an unit-test sometimes fails and sometimes succeeds.
I check the equality of two objects that contain a set of objects. Here the the problem arises. Sometimes the test "obj1.mySet == obj2.mySet" fails - sometimes not. I test this with only one object in each set (mySet). The test for the equality of this objects (in mySet) itself succeeds.
I tried some hours to find a mistake in my code, but couldn't find any. Now I have a workaround that helps to pass the test, but I do not understand, what's going on. I have a method within the test-objects, that returns the objects of the set as an (ordered) array. When I test the equality of this arrays, the test always succeeds.
Do someone know, what’s going on?
Update:
In my BaseClass
func hash(into hasher: inout Hasher) { hasher.combine(firebaseID) }
static func == (lhs: FirebaseObject, rhs: FirebaseObject) -> Bool { return lhs.isEqual(to: rhs) }
func isEqual(to object: Any?) -> Bool {
guard object != nil && object is FirebaseObject else { return false }
let value = object as! FirebaseObject
return firebaseID == value.firebaseID && name == value.name
}
In the SubClass
override func isEqual(to object: Any?) -> Bool {
guard object != nil && object! is MealPlanned else { return false }
let obj = object as! MealPlanned
var result = ""
if !super.isEqual(to:obj) { result.append("fbObject ") }
if portions != obj.portions { result.append("portions ") }
if imgID != obj.imgID { result.append("imgID ") }
if meal != obj.meal { result.append("meal ") }
if date != obj.date { result.append("date ") }
if portionsInBaseMeal != obj.portionsInBaseMeal {result.append("portionsInBaseMeal ") }
if getIngrediencesInMeals() != obj.getIngrediencesInMeals() { result.append("ingrediencesInMeals ") }
if result.count > 0 {
if (showsDifference) { print("difference in MealPlanned <\(obj.name ?? "Fehler")>: \(result)") }
return false
}
return true
}
I did it this way, to find and print the problem.
This version succeeds.
if getIngrediencesInMeals() != obj.getIngrediencesInMeals() { result.append("ingrediencesInMeals ")
getIngrediencesInMeals() returns the set as an ordered array.
In this way the test sometimes succeeds sometimes fails:
if ingrediences != ingrediences { result.append("ingrediencesInMeals ")
This returns the ordered array:
func getIngrediencesInMeals() -> [IngredienceInMeals] { return ingrediences.sorted{ $0.position < $1.position } }
in IngredienceInMeals
override func isEqual(to object: Any?) -> Bool {
guard object != nil && object! is IngredienceInMeals else { return false }
let obj = object as! IngredienceInMeals
var result = ""
if !super.isEqual(to:obj) { result.append("fbObject ")}
if unit != obj.unit { result.append("unit ")}
if quantity != obj.quantity { result.append("quantity ")}
if ingredience != obj.ingredience { result.append("ingredience ")}
if position != obj.position { result.append("position ")}
if result.count > 0 {
if (showsDifference) { print("difference in IngredienceInMeal <\(obj.name ?? "Fehler")>: \(result)") }
return false
}
return true
}

if you want to compare two objects use Equatable protocol method in your object class
example of compare two objects
class ItemModel : Equatable {
var TypeOfOffer : String?
var TypeOfSelling : String?
var Address : String?
var NumberOfRoom : String?
var Price : String?
var Image : String?
var ID : String?
var itemId : String?
init(TypeOfOffer : String? , TypeOfSelling : String?, Address : String?, NumberOfRoom : String? , Price : String?, Image : String?, ID : String?, itemId : String? )
{
self.TypeOfOffer = TypeOfOffer
self.TypeOfSelling = TypeOfSelling
self.Address = Address
self.NumberOfRoom = NumberOfRoom
self.Price = Price
self.Image = Image
self.ID = ID
self.itemId = itemId
}
static func == (lhs: ItemModel, rhs: ItemModel) -> Bool {
var isIt = true
isIt = (lhs.TypeOfOffer == "" || lhs.TypeOfOffer == rhs.TypeOfOffer)
&& (lhs.TypeOfSelling == "" || lhs.TypeOfSelling == rhs.TypeOfSelling)
&& (lhs.Address == "" || lhs.Address == rhs.Address)
&& (lhs.NumberOfRoom == "" || lhs.NumberOfRoom == rhs.NumberOfRoom)
&& (lhs.Price == "" || lhs.Price == rhs.Price)
return isIt
}
}
Compare two instances of an object in Swift!

Related

Efficient sort array by ParentId swift

Given an array of dictionaries some of which have ParentId I need to sort them in ancestry order.
I have a working algorithm, but I am not sure it is actually efficient.
How can this be improved?
Sample data:
var dicts = [["ParentId": "eee82", "Id":"a0dq1"],
["ParentId": "pqrst", "Id":"eee82"],
["ParentId": "aaa1", "Id":"pqrst"]]
Sample output
["pqrst", "eee82", "a0dq1"]
I ran below in playground
import Foundation
// GIVEN this source...
var dicts = [["ParentId": "eee82", "Id":"a0dq1"],
["ParentId": "pqrst", "Id":"eee82"],
["ParentId": "aaa1", "Id":"pqrst"]]
func findParents(source: [[String:String]], this: [String:String]) -> [[String:String]] {
var results = [[String:String]]()
if let parentId = this["ParentId"],
let parent = source.first(where: { $0["Id"] == parentId }) {
results.append(parent)
results.append(contentsOf: findParents(source: source, this: parent))
}
return results
}
var this = dicts.first!
var accounts = (findParents(source: dicts, this: this) + [this])
var sorted = [[String:String]]()
var hasParentMap = [String: Bool]()
for account in accounts {
let parentId = account["ParentId"]
let hasParent = accounts.first(where: { $0["Id"] == parentId }) != nil
hasParentMap[account["Id"]!] = !(parentId == nil || !hasParent)
}
while sorted.count != accounts.count {
for account in accounts {
if sorted.first(where: { $0["Id"] == account["Id"] }) != nil {
continue
}
if hasParentMap[account["Id"]!] == false {
sorted.insert(account, at: 0)
continue
} else if let parentId = account["ParentId"] {
let parentIndex = sorted.firstIndex(where: { $0["Id"] == parentId })
if parentIndex == nil {
continue
}
sorted.insert(account, at: parentIndex! + 1)
}
}
}
dump (accounts.map({ $0["Id"]! })) // ["eee82", "pqrst", "a0dq1"]
// ...we want to get this output
dump (sorted.map({ $0["Id"]! })) // ["pqrst", "eee82", "a0dq1"]
Update removed the numerical ids to avoid confusion
Here's the visual illustration of what I am trying to achieve
To make things easier I created a Person type:
struct Person: Comparable, CustomStringConvertible {
let id: String
let parentID: String
var description: String {
return "[\(id), \(parentID)]"
}
static func < (lhs: Self, rhs: Self) -> Bool {
return lhs.id < rhs.id
}
init?(dict: [String: String]) {
guard let id = dict["Id"] else { return nil }
guard let parentID = dict["ParentId"] else { return nil }
self.id = id
self.parentID = parentID
}
func toDictionary() -> [String: String] {
return ["Id": id, "ParentId": parentID]
}
}
Here is our data:
var dicts = [
["ParentId": "2", "Id":"3"],
["ParentId": "1", "Id":"2"],
["ParentId": "42", "Id":"1"],
["ParentId": "100", "Id":"88"],
["ParentId": "88", "Id":"77"],
["ParentId": "77", "Id":"66"],
["ParentId": "77", "Id":"55"],
]
Here are our people converted to structs:
var people = dicts.compactMap { Person(dict: $0) }
Here are a few methods to operate on our array of people:
extension Array where Element == Person {
func tree(root: Person) -> [Person] {
[root] + children(of: root)
.flatMap { tree(root: $0) }
}
func topLevelParents() -> [Person] {
return filter { parent(of: $0) == nil }
}
func children(of parent: Person) -> [Person] {
return filter { $0.parentID == parent.id }.sorted()
}
func parent(of child: Person) -> Person? {
return first { child.parentID == $0.id }
}
}
Get all people who don't have parents:
let topLevelParents = people.topLevelParents().sorted()
print("topLevelParents: \(topLevelParents)")
Construct the tree of descendants for each parent and flatten into an array:
let results = topLevelParents.flatMap({ people.tree(root: $0) })
print("results: \(results)")
Convert back to a dictionary:
let dictionaryResults = results.map { $0.toDictionary() }
print("dictionaryResults: \(dictionaryResults)")

Overloading == operator calls parent function for !=

I overloaded the == operator on a class. Then a child class also overloads the == operator. My understanding is that both == and != are supposed to call the same == function. However, when running tests, == calls the function on the child class, yet != calls the function on the parent class. This is bizarre. I would expect the same behavior for both == and !=.
class Description: Equatable {
var id : Int = IdFactory.id()
static func == (lhs: Description, rhs: Description) -> Bool {
assertionFailure("Child Description must override == method.")
return false
}
}
class LineDescription : Description {
var track = 0
static func == (lhs: LineDescription, rhs: LineDescription) -> Bool {
if lhs.track != rhs.track { return false }
return true
}
}
import XCTest
class LineDescriptionTest: XCTestCase {
func testEquality(){
let ld = LineDescription()
ld.track = 9
let ld2 = LineDescription()
ld2.track = 9
XCTAssert(ld == ld2) //<--Calls child == function
ld2.track = 1
XCTAssert(ld != ld2) //<--Calls parent != function
}
}

How to filter objects by string comparison in Swift

I'm having an array of models:
struct Contact {
var givenName: String!
var familyName: String!
var organizationName: String!
}
I want to filter those contacts using ONE UITextField. My current problem is to define between words and filter only contacts matching all words.
For example:
var contacts: [Contact] = [Contact(givenName: "David", familyName: "Seek", organizationName: "Aldi"),
Contact(givenName: "Johne", familyName: "Doe", organizationName: "Kaisers"),
Contact(givenName: "Jane", familyName: "Doe", organizationName: "Tengelmann"),
Contact(givenName: "Marco", familyName: "Seidl", organizationName: "Rewe"),
Contact(givenName: "Filip", familyName: "Halbig", organizationName: "Aldi")]
I want to enter: David Aldi and only find David who works at Aldi. I don't want to see Filip who also works at Aldi.
Also if I enter David Do, I don't want to see any contacts, because none should match.
func getSearchResults(_ filterKey: String) {
self.presentActivityIndicator()
var processed: Int = 0
self.filteredContacts.removeAll()
DispatchQueue.global(qos: .background).async {
for contact in self.unfilteredContacts {
processed += 1
let lowercasedGivenName = contact.givenName.replacingOccurrences(of: " ", with: "").lowercased()
let lowercasedFamilyName = contact.familyName.replacingOccurrences(of: " ", with: "").lowercased()
let lowercasedOrganizationName = contact.organizationName.replacingOccurrences(of: " ", with: "").lowercased()
let name = lowercasedGivenName.appending(lowercasedFamilyName)
if name.range(of: filterKey.replacingOccurrences(of: " ", with: "")) != nil {
if !self.filteredContacts.contains(contact) {
self.filteredContacts.append(contact)
}
}
for word in filterKey.components(separatedBy: " ") {
if lowercasedOrganizationName.range(of: word.lowercased()) != nil {
if !self.filteredContacts.contains(contact) {
self.filteredContacts.append(contact)
}
}
}
if processed == self.unfilteredContacts.count {
self.reloadTableViewInMainThread()
}
}
}
}
This is one of the several approaches I've tried today. But with every try, I've been ending up, filtering out David and by entering the second name, I have found other contacts not matching the first name David, but match for example part of the family name or company name.
What am I missing and would be the best approach for this? Help is very appreciated.
First separate the filterKey into space separated components
let components = filterKey.components(separatedBy: " ")
then use the filter function with the closure syntax
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if contact.givenName != string && contact.familyName != string && contact.organizationName != string {
return false
}
}
return true
}
The closure returns true if the contact matches all components.
Wrapped in the function it's
func getSearchResults(_ filterKey: String) {
let components = filterKey.components(separatedBy: " ")
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if contact.givenName != string && contact.familyName != string && contact.organizationName != string {
return false
}
}
return true
}
}
Note:
Please, please never declare properties / members as implicit unwrapped optionals which are initialized with an init method. It perfectly legal (and recommended) to declare the members without the exclamation marks. However if they are supposed to be optional declare them as real optional (question mark).
And if the values won't change declare the members as constants (let).
struct Contact {
let givenName: String
let familyName: String
let organizationName: String
}
Edit:
To filter the contacts whose properties contain filterKey write
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if !contact.givenName.lowercased().contains(string) &&
!contact.familyName.lowercased().contains(string) &&
!contact.organizationName.lowercased().contains(string) {
return false
}
}
return true
}
To filter the contacts whose properties begin with filterKey write
self.filteredContacts = contacts.filter { contact -> Bool in
for string in components {
if !contact.givenName.lowercased().hasPrefix(string) &&
!contact.familyName.lowercased().hasPrefix(string) &&
!contact.organizationName.lowercased().hasPrefix(string) {
return false
}
}
return true
}
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ")
var contactsFiltered = contacts
keys.forEach { key in
contactsFiltered = contactsFiltered.filter {
$0.givenName == key || $0.familyName == key || $0.organizationName == key
}
}
return contactsFiltered
}
I splitted the filterKeys by blank spaces. Then, for each key, I check if the value exists in contact attribute.
If do you want a pure functional solution, you can use Set and intersection:
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ")
return contacts.filter {
Set([$0.givenName, $0.familyName, $0.organizationName]).intersection(keys).count >= keys.count
}
}
And, if do you want a crazy solution with Mirror, for when adding a new attribute in Contact, you do not need to update getSearchResults:
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ")
return contacts.filter {
let stringAttr = Mirror(reflecting: $0).children.filter { ($0.value as? String) != nil }
let contactValues = stringAttr.map { $0.value as! String }
return Set(contactValues).intersection(keys).count >= keys.count
}
}
Use with caution my last code (or never use it)
Edit
For match part of string in key.
func getSearchResults(contacts: [Contact], filterKeys: String) -> [Contact] {
let keys = filterKeys.components(separatedBy: " ").map { $0.lowercased() }
var contactsFiltered = contacts
keys.forEach { key in
contactsFiltered = contactsFiltered.filter {
$0.givenName.lowercased().range(of: key) != nil ||
$0.familyName.lowercased().range(of: key) != nil ||
$0.organizationName.lowercased().range(of: key) != nil
}
}
return contactsFiltered
}

Hashing problems using a wrapper class around NSUUID as the key

** REWRITE **
OK, it turns out I'm really asking a different question. I understand about hashValue and ==, so that's not relevant.
I would like my wrapper class BUUID to "do the right thing" and act just like NSUUID's act in a Dictionary.
See below, where they don't.
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
Results. Note that I can look up using NSUUID nB and get back what I put under nA. Not so with my BUUID.
a: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
b: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
a === b: false
a == b: true
Optional("Hi") nil
nA: <__NSConcreteUUID 0x7fa193c39500> BB9F9851-93CF-4263-B98A-5015810E4286
nB: <__NSConcreteUUID 0x7fa193c37dd0> BB9F9851-93CF-4263-B98A-5015810E4286
nA === nB: false
nA == nB: true
Optional("Hi") Optional("Hi")
Inheriting from NSObject also assumes isEqual(object: AnyObject?) -> Bool method overloading:
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override func isEqual(object: AnyObject?) -> Bool {
guard let buuid = object as? BUUID else {
return false
}
return buuid._realUUID == _realUUID
}
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
So the answer is to not make BUUID inherit from NSObject, which undercuts the Swiftiness of overriding ==.
So:
extension BUUID: Hashable {}
class BUUID: CustomStringConvertible {
// take away all 'override' keywords, nothing to override
// otherwise same as above
}
Interesting!
This answer is relevant to initially asked question: Why that's possible to get two key-value pairs with identical key's hashes in a dictionary
This example illustrates that keys in Dictionary can have identical hashes, but equality operation should return false for different keys:
func ==(lhs: FooKey, rhs: FooKey) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
class FooKey: Hashable, Equatable {
var hashValue: Int {
get {
return 123
}
}
}
var d = Dictionary<FooKey, String>()
let key1 = FooKey()
let key2 = FooKey()
d[key1] = "value1"
d[key2] = "value2"
Output
[FooKey: "value1", FooKey: "value2"]
That's definitely not good to have all keys with the same hash. In this case we are getting that worst case when search element complexity fells down to O(n) (exhaustive search). But it will work.

How to check for palindrome in Swift using recursive definition

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
}
}
}