How to check a string is symmetric or not in Swift - swift

return value == String(value.reversed())
This is what I tried. It works well but takes a little bit longer. Does anyone know a better way?

There is nothing available in the standard library for String, or for anything else, so a solution for only String probably isn't the best option.
You don't need to make a new instance of anything based on reversed; elementsEqual does the job.
public extension Sequence where Element: Equatable {
var isSymmetric: Bool { elementsEqual(reversed()) }
}
"🐈🐆🐅🐆🐈".isSymmetric // true
"🐈🐆🐅".isSymmetric // false

You could in c test each character one at a time from beginning and end until your two indices meet in the middle. But for Swift you don't have a guarantee that characters are the same length in a Swift String, they have to store strings that are made of unicode characters which can have one of 100,000's of characters at the moment, I think they currently do this by storying the String using UTF16. So you can not just use offset logic to quickly calculate the index for a character, you have to enumerate through each character, checking the length of each character to get to the next. Objective-C made this even more complicated with the potential of private subclasses of NSString, each could have different encoding methods.

A less expensive way than reversing the array is to walk simultaneously from the beginning and the end to the middle and compare each character - or whatever the Element of the type conforming to BidirectionalCollection is.
public extension BidirectionalCollection where Element: Equatable {
var isPalindrome: Bool {
if isEmpty { return true }
var 👉 = startIndex
var 👈 = index(before: endIndex)
while 👉 < 👈 {
if self[👉] != self[👈] { return false }
formIndex(after: &👉)
formIndex(before: &👈)
}
return true
}
}

Related

Sequence extension that works on Arrays, Sets and Dictionaries in Swift

I'm learning Swift with Paul Hudson's 100 days of Swift. In one of his extension lessons, I have found a notion that more advanced developers could write a Sequence extension that would service Arrays, Sets and Dictionaries:
https://www.hackingwithswift.com/quick-start/understanding-swift/when-are-protocol-extensions-useful-in-swift
I have given this a shot but:
I don't know how to create a variable that could change its type (wonder if that's even possible at all)
I don't know how to create a sequence that would service dictionaries too, since their syntax for allSatisfy is a bit different
Would you be so kind and give me a hand here? :)
The code:
extension Sequence {
var isAllEven:Bool {
numbers.allSatisfy { $0.isMultiple(of:2)}
}
}
let numbers = Set([4, 8, 15, 16])
print(numbers.isAllEven)
I can change numbers to be both Array and Set but as soon as I understood what Paul said, there is a possibility to create an extension that could service all 3 in one passage of code without having to change the variables content.
As isMultiple(of:) belongs to all integer types a generic version must be constrained to BinaryInteger
extension Sequence where Element : BinaryInteger {
var isAllEven : Bool {
allSatisfy {$0.isMultiple(of: 2)}
}
}
But this cannot cover Dictionary, because although Dictionary conforms to Sequence the Element type is different.
You could write a second extension of Sequence which matches the Dictionary tuple type
extension Sequence where Element == (key:String, value:Int) {
var isAllEven : Bool {
allSatisfy {$0.value.isMultiple(of:2)}
}
}
but this considers only String keys and Int values
A more generic way is to extend Dictionary directly
extension Dictionary where Value : BinaryInteger {
var isAllEven : Bool {
allSatisfy {$0.value.isMultiple(of: 2)}
}
}

Swift string vs [Character] and performance

From the very beginning Swift strings were tricky since they work properly with UTF and there is a standard example from Apple:
let cafe1 = "Cafe\u{301}"
let cafe2 = "Café"
print(cafe1 == cafe2)
// Prints "true"
It means that comparison has some implicit logic and it's not a simple comparison of two memory areas are the same. I used to see recommendations to flat out strings into [Character] since when you do this all unicode-related conversions take place once and then all operations are faster. Additionally strings are not necessarily use continuous memory area which makes it more expensive to compare them than character arrays.
Long story short, I solved this problem on leetcode: https://leetcode.com/problems/implement-strstr/ and tried different approaches: KMP, character arrays and strings. To my surprise strings are the fastest.
How is it so? KMP has some prework and it is less efficient in general but why strings are faster than [Character]? Is it new for some recent Swift version or do I miss something conceptually?
Code that I used for reference:
[Character], 8ms, 15mb memory
func strStr(_ haystack: String, _ needle: String) -> Int {
guard !needle.isEmpty else { return 0 }
guard haystack.count >= needle.count else { return -1 }
var result: Int = -1
let str = Array(haystack)
let pattern = Array(needle)
for i in 0...(str.count - pattern.count) {
if str[i] == pattern[0] && Array(str[i...(i + pattern.count - 1)]) == pattern {
result = i
break
}
}
return result
}
Strings, 4ms(!!!), 14.5mb memory
func strStr(_ haystack: String, _ needle: String) -> Int {
guard !needle.isEmpty else { return 0 }
guard haystack.count >= needle.count else { return -1 }
var result: Int = -1
for i in 0...(haystack.count - needle.count) {
var hIdx = haystack.index(haystack.startIndex, offsetBy: i)
if haystack[hIdx] == needle[needle.startIndex] {
var hEndIdx = haystack.index(hIdx, offsetBy: needle.count - 1)
if haystack[hIdx...hEndIdx] == needle {
result = i
break
}
}
}
return result
}
First, I think there may be some misunderstandings on your part:
flat out strings into [Character] since when you do this all unicode-related conversions take place once and then all operations are faster
This doesn't make a lot of sense. Character has exactly the same issues as String. It still may be made of composed or decomposed UnicodeScalars that need special handling for equality.
Additionally strings are not necessarily use continuous memory area
This is equally true of Array. Nothing in Array promises that memory is contiguous. That's why ContiguousArray exists.
As to why String is faster than hand-coded abstractions, that should be obvious. If you could easily out-perform String with no major tradeoffs, then stdlib would implement String to do that.
To the mechanics of it, String does not promise any particular internal representation, so it heavily depends on how you're creating your strings. Small strings, for example, can be reduced all the way to a tagged pointer that requires zero memory (it can live in a register). Strings can be stored in UTF-8, but they can also be stored in UTF-16 (which is extremely fast to work with).
When Strings are compared with other Strings that know they have the same internal representations, then they can apply various optimizations. And this really points to one part of your problem:
Array(str[i...(i + pattern.count - 1)])
This is forcing a memory allocation and copy to create a new Array out of str. You would probably do much better if you used Slice for this work rather than making full Array copies. You'd almost certainly find in that case that you're exactly matching String's implementations (using SubStr).
But the real lesson here is that you're unlikely to beat String at its own game in the general case. If you happen to have very specialized knowledge about your Strings, then I can see where you'd be able to beat the general-purpose String algorithms. But if you think you're beating stdlib for arbitary strings, why would stdlib not just implement what you're doing (and beat you using knowledge of the internal details of String)?

Swift: String starts(with:) vs hasPrefix

String.hasPrefix (or [NSString hasPrefix]) was always part of Foundation. However, I just noticed that now we also have starts(with:).
This method comes from Sequence but it also works for String.
My question is, which one should I prefer? Are there any performance considerations? I'm used to hasPrefix from Objective-C days, but starts(with:) is more intuitive and works for other sequences.
String.hasPrefix() is implemented in StringLegacy.swift as
extension String {
public func hasPrefix(_ prefix: String) -> Bool {
if _fastPath(self._guts.isNFCFastUTF8 && prefix._guts.isNFCFastUTF8) {
guard prefix._guts.count <= self._guts.count else { return false }
return prefix._guts.withFastUTF8 { nfcPrefix in
let prefixEnd = nfcPrefix.count
return self._guts.withFastUTF8(range: 0..<prefixEnd) { nfcSlicedSelf in
return _binaryCompare(nfcSlicedSelf, nfcPrefix) == 0
}
}
}
return starts(with: prefix)
}
}
which means (if I understand it correctly): If both the string and the prefix candidate use a UTF-8 based storage then the UTF-8 bytes are compared directly. Otherwise it falls back to starts(with:) and does a Character based comparison.
So there is no difference in the result, but hasPrefix() is optimized for native Swift strings.
Note: This is the from the master (Swift 5) branch, the situation might be different in earlier versions.

Set's contains method returns different value at different time

I was thinking about how Swift ensures uniqueness for Set because I have turned one of my obj from Equatable to Hashable for free and so I came up with this simple Playground
struct SimpleStruct: Hashable {
let string: String
let number: Int
static func == (lhs: SimpleStruct, rhs: SimpleStruct) -> Bool {
let areEqual = lhs.string == rhs.string
print(lhs, rhs, areEqual)
return areEqual
}
}
var set = Set<SimpleStruct>()
let first = SimpleStruct(string: "a", number: 2)
set.insert(first)
So my first question was:
Will the static func == method be called anytime I insert a new obj inside the set?
My question comes from this thought:
For Equatable obj, in order to make this decision, the only way to ensure two obj are the same is to ask the result of static func ==.
For Hashable obj, a faster way is to compare hashValues... but, like in my case, the default implementation will use both string and number, in contrast with == logic.
So, in order to test how Set behaves, I have just added a print statement.
I have figured out that sometimes I got the print statement, sometimes no. Like sometimes hashValue isn't enough in order to make this decision ... So the method hasn't been called every time.
Weird...
So I've tried to add two objects that are equal and wondering what will be the result of set.contains
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // returns true
set.contains(second)
And wonders of wonders, launching a couple of times the playground, I got different results and this might cause unpredictable results ...
Adding
var hashValue: Int {
return string.hashValue
}
it gets rid of any unexpected results but my doubt is:
Why, without the custom hashValue implementation, == sometimes gets called and sometimes it doesn't?
Should Apple avoid this kind of unexpected behaviours?
The synthesized implementation of the Hashable requirement uses all stored
properties of a struct, in your case string and number. Your implementation
of == is only based on the string:
let first = SimpleStruct(string: "a", number: 2)
let second = SimpleStruct(string: "a", number: 3)
print(first == second) // true
print(first.hashValue == second.hashValue) // false
This is a violation of a requirement of the Hashable protocol:
Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.
and causes the undefined behavior. (And since hash values are randomized
since Swift 4.2, the behavior can be different in each program run.)
What probably happens in your test is that the hash value of second is used to determine the “bucket” of the set in which the value
would be stored. That may or may not be the same bucket in which first is stored. – But that is an implementation detail: Undefined behavior is undefined behavior, it can cause unexpected results or even
runtime errors.
Implementing
var hashValue: Int {
return string.hashValue
}
or alternatively (starting with Swift 4.2)
func hash(into hasher: inout Hasher) {
hasher.combine(string)
}
fixes the rule violation, and therefore makes your code behave as expected.

Swift: Reduce optional array of models to single Bool by optional property

I just learned about the wonderful world of map, flatMap and reduce in swift and I already use it where ever it makes sense and helps improve my code.
Now I encountered a very special problem and I wonder if there is a solution to this using map, flatMap and/or reduce.
In my model class, I have a optional array of other models. These models have a optional Bool property. I now want to know if the whole array of models contains at least one with a true property. This is what I'm currently doing:
class ModelA{
var bModels: [ModelB]?
}
class ModelB{
var aBool: Bool?
}
func hasATrue(aModel: ModelA) {
guard let bModels = aModel.bModels else { return false }
for bModel in bModels {
if bModel.aBool == true {
return true
}
}
return false
}
You can do this all in a single expression with optional chaining, Array.contains(where:) and nil coalescence.
func hasATrue(aModel: ModelA) {
return aModel.bModels?.contains(where: { $0.aBool == true }) ?? false
}
As per the comments on my question, this is what will do the trick:
return aModel.bModels?.contains(where: { $0.aBool == true }) ?? false
or alternatively
return aModel.bModels?.contains(where: { $0.aBool == true }) == true
Is one better than the other? I don't know. Maybe a Swift expert with deep knowledge about how the language is implemented can tell which one is faster/more efficient but for me, that method is called rarely and processing time is therefore not an issue but that is another topic.
Thanks to all the commenters. I was so fascinated by map, flatMap and reduce that I did not see the obvious (nor did I know that you can pass a closure to a contains function in swift). From day to day I like the language more and more.
I'm almost crying every time I have to write the Java code for Android 😅