How to compare two Strings to check if they have same Characters Swift 4?
If “multiplicity” counts (i.e. "aab" has the same characters as "aba",
but not the same characters as "abb"), then
s1.sorted() == s2.sorted()
does the trick. If you don't care about the multiplicity, then just
Set(s1) == Set(s2)
Example:
let firstArray = ["sumit", "ambuj", "abhi", "aba"]
let secondArray = ["mitsu", "jumba", "hibb", "abb"]
for (s1, s2) in zip(firstArray, secondArray) {
print(s1.sorted() == s2.sorted())
}
// true, true, false, false
for (s1, s2) in zip(firstArray, secondArray) {
print(Set(s1) == Set(s2))
}
// true, true, false, true
For longer strings it might be more efficient to maintain a
dictionary with the number of occurrences of each character in
a string (similar to a NSCountedSet
):
func characterCounts(_ s: String) -> [Character: Int] {
return s.reduce(into: [:], { $0[$1, default: 0] += 1 })
}
and then compare the dictionaries:
characterCounts(s1) == characterCounts(s2)
Swift get the different characters in two strings
UPDATE for Swift 4.0 or greater
Because of the change of String to also be a collection, this answer can be shortened to
let difference = zip(x, y).filter{ $0 != $1 }
For Swift version 3.*
let difference = zip(x.characters, y.characters).filter{$0 != $1}
How to compare two strings in swift?
You can try
if !isText.contains("Schreibe etwas...") {
shareButton.isEnabled = true
abortButton.isEnabled = true
}
How to compare string equality which contains unicode characters in swift?
Doplňky k Apple TV
string looks like that it comes from the Apple website. When I checked it on their website, this string, it contains NO-BREAK SPACE (U+00A0) between Apple
& TV
. It's a white space character, but it doesn't equal to a normal SPACE (U+0020).
"Doplňky k Apple\u{00a0}TV" == "Doplňky k Apple TV" // false
First thing to specify - does it matter? Should we treat it as equal or not?
Then you have Apple TVtilbehør
& *Apple*TV*Tilbehør*
strings. Is it intentional typo? Or Apple TVtilbehør
should be Apple TV Tilbehør
? Let's assume it's intentional typo to test your comparison.
Next, these *
(at the beginning/end) in the *Apple*TV*Tilbehør*
string are ...? Second thing to specify - should we ignore them? Do they represent a whitespace?
Next thing is the Unicode equivalence. How would you like to compare these two strings? Swift helps you here (source):
Comparing strings for equality using the equal-to operator (
==
) or a relational operator (like<
or>=
) is always performed using Unicode canonical representation. As a result, different representations of a string compare as being equal.
"Cafe\u{301}" == "Café" // true
What about other countries? Like Germany where Straße
equals to Strasse
? Third thing to specify - how we should treat these strings?
As you can see, there's a lot of things one should think about. Do you have a specification? Follow it. No specification? Your algorithm will stop working sooner or later.
Playground
I took the liberty to specify all these things by myself:
- All whitespaces do equal
*
at the beginning/end doesn't matter (ignored)Straße
does not equal toStrasse
Sample code:
import Foundation
let json = [
// U+00A0 is NO-BREAK SPACE which looks like a normal space (U+0020)
"cz": "Doplňky k Apple\u{00a0}TV",
"dk": "Apple TV Tilbehør",
"en": "Hello",
"de": "Straße",
"fr": "Expos\u{00E9}" // Exposé
]
let plist = [
"cz": "Doplňky*k*Apple*TV",
"dk": "*Apple*TV*Tilbehør*",
"es": "Hola",
"de": "Strasse",
"fr": "Expose\u{0301}" // Exposé
]
let jsonKeys = Set(json.keys)
let plistKeys = Set(plist.keys)
let commonKeys = jsonKeys.intersection(plistKeys)
let keysMissingInJson = plistKeys.subtracting(jsonKeys)
let keysMissingInPlist = jsonKeys.subtracting(plistKeys)
print("Languages missing in JSON: \(keysMissingInJson.count)")
keysMissingInJson.forEach { key in
print(" - \(key)")
}
print("Languages missing in PLIST: \(keysMissingInPlist.count)")
keysMissingInPlist.forEach { key in
print(" - \(key)")
}
let differentValueKeys: [String] = commonKeys.compactMap { key in
guard let initialJsonValue = json[key], let initialPlistValue = plist[key] else {
fatalError("Fix commonKeys")
}
// Replace all whitespace characters with a normal space
let jsonValue = String(
initialJsonValue.map { $0.isWhitespace ? " " : $0 }
)
let plistValue = initialPlistValue
// Replace all * with a normal whitespace
.replacingOccurrences(of: "*", with: " ")
// Trim all whitespace characters from the beginning/end
.trimmingCharacters(in: .whitespaces)
return jsonValue == plistValue ? nil : key
}
print("Different values: \(differentValueKeys.count)")
differentValueKeys.forEach { key in
print(" - \(key): JSON(\(json[key]!)) PLIST(\(plist[key]!))")
}
Output:
Languages missing in JSON: 1
- es
Languages missing in PLIST: 1
- en
Different values: 1
- de: JSON(Straße) PLIST(Strasse)
How to compare characters in Swift efficiently
Unless you chose a fixed length character model for your strings, methods and properties such as .count and .characters will have a complexity of O(n) or at best O(n/2) (where n is the string length). If you were to store your data in an array of character (e.g. [Character] ), your functions would perform much better.
You can also combine the whole calculation in a single pass using the zip() function
let hammingDistance = zip(word1.characters,word2.characters)
.filter{$0 != $1}.count
but that still requires going through all characters of every word pair.
...
Given that you're only looking for Hamming distances of 1, there is a faster way to get to all the unique pairs of words:
The strategy is to group words by the 4 (or 5) patterns that correspond to one "missing" letter. Each of these pattern groups defines a smaller scope for word pairs because words in different groups would be at a distance other than 1.
Each word will belong to as many groups as its character count.
For example :
"hear" will be part of the pattern groups:
"*ear", "h*ar", "he*r" and "hea*".
Any other word that would correspond to one of these 4 pattern groups would be at a Hamming distance of 1 from "hear".
Here is how this can be implemented:
// Test data 8500 words of 4-5 characters ...
var seenWords = Set<String>()
var allWords = try! String(contentsOfFile: "/usr/share/dict/words")
.lowercased()
.components(separatedBy:"\n")
.filter{$0.characters.count == 4 || $0.characters.count == 5}
.filter{seenWords.insert($0).inserted}
.enumerated().filter{$0.0 < 8500}.map{$1}
// Compute patterns for a Hamming distance of 1
// Replace each letter position with "*" to create patterns of
// one "non-matching" letter
public func wordH1Patterns(_ aWord:String) -> [String]
{
var result : [String] = []
let fullWord : [Character] = aWord.characters.map{$0}
for index in 0..<fullWord.count
{
var pattern = fullWord
pattern[index] = "*"
result.append(String(pattern))
}
return result
}
// Group words around matching patterns
// and add unique pairs from each group
func addHamming1Edges()
{
// Prepare pattern groups ...
//
var patternIndex:[String:Int] = [:]
var hamming1Groups:[[String]] = []
for word in allWords
{
for pattern in wordH1Patterns(word)
{
if let index = patternIndex[pattern]
{
hamming1Groups[index].append(word)
}
else
{
let index = hamming1Groups.count
patternIndex[pattern] = index
hamming1Groups.append([word])
}
}
}
// add edge nodes ...
//
for h1Group in hamming1Groups
{
for (index,sourceWord) in h1Group.dropLast(1).enumerated()
{
for targetIndex in index+1..<h1Group.count
{ addEdge(source:sourceWord, neighbour:h1Group[targetIndex]) }
}
}
}
On my 2012 MacBook Pro, the 8500 words go through 22817 (unique) edge pairs in 0.12 sec.
[EDIT] to illustrate my first point, I made a "brute force" algorithm using arrays of characters instead of Strings :
let wordArrays = allWords.map{Array($0.unicodeScalars)}
for i in 0..<wordArrays.count-1
{
let word1 = wordArrays[i]
for j in i+1..<wordArrays.count
{
let word2 = wordArrays[j]
if word1.count != word2.count { continue }
var distance = 0
for c in 0..<word1.count
{
if word1[c] == word2[c] { continue }
distance += 1
if distance > 1 { break }
}
if distance == 1
{ addEdge(source:allWords[i], neighbour:allWords[j]) }
}
}
This goes through the unique pairs in 0.27 sec. The reason for the speed difference is the internal model of Swift Strings which is not actually an array of equal length elements (characters) but rather a chain of varying length encoded characters (similar to the UTF model where special bytes indicate that the following 2 or 3 bytes are part of a single character. There is no simple Base+Displacement indexing of such a structure which must always be iterated from the beginning to get to the Nth element.
Note that I used unicodeScalars instead of Character because they are 16 bit fixed length representations of characters that allow a direct binary comparison. The Character type isn't as straightforward and take longer to compare.
What is the Swift equivalent of isEqualToString in Objective-C?
With Swift you don't need anymore to check the equality with isEqualToString
You can now use ==
Example:
let x = "hello"
let y = "hello"
let isEqual = (x == y)
now isEqual is true
.
How to find the number of matching character between two string in swift?
You can use zip to find out the number of matching characters in the same position.
let x = "1010"
let y = "1100"
let intersection = zip(x, y).filter{ $0 == $1 }
let numberOfMatches = intersection.count
How to check if two [String: Any] are identical?
For Xcode 7.3, swift 2.2
A dictionary is of type : [String:AnyObject]
or simply put NSDictionary
let actual: [String: AnyObject] = ["id": 12345, "name": "Rahul Katariya"]
var expected: [String: AnyObject] = ["id": 12346, "name": "Aar Kay"]
print(NSDictionary(dictionary: actual).isEqualToDictionary(expected))//False
For Xcode 8.beta 6, Swift 3
Dictionary is defined as:
struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral
NSDictionary has the following convenience initializer:
convenience init(dictionary otherDictionary: [AnyHashable : Any])
So you can use AnyHashable type for Key and Any type for Value
let actual: [String: Any] = ["id": 12345, "name": "Rahul Katariya"]
var expected: [String: Any] = ["id": 12346, "name": "Aar Kay"]
print(NSDictionary(dictionary: actual).isEqual(to: expected))//False
Related Topics
iOS 8 Swift Audio Playback Execute Method on Completion
Swift Switch Char{ Case "\U{E2}:
Swift Catch Enum Case with Binding
How to Display Mtkview with Rgba16Float Mtlpixelformat
How to Run the Simulator the Operation Couldn't Be Completed. (Launchserviceserror Error 0.)
Alamofire 5 Escaping Forward Slashes
Swift Package Manager with Resources Compile Errors
Swift Check If Value Is of Type Array (Of Any Type)
Which Measuring Unit Is Used in Scnvector3 Position for X, Y and Z in Arkit
Differences Between Ways of Initializing a Dictionary
Auth.Auth().Currentuser.Reload() Doesn't Refresh Currentuser.Isemailverified
Tube Physics Body Acting Like Scncylinder, But How to Make It Act Like Scntube
What Does the Underscore in a Function Declaration Do
Secure Text .Echosbullets Not Working for Password Field
Swift Implement Literalconvertible Protocol
Accessing Actor Properties Synchronously from Task-Less Context