Search code examples
iosswiftnssetswift3nscountedset

`CountedSet` initialization issue


I'm comparing the characters contained within two words. In seeking to accomplish this, Set (aka NSSet) seemed like the way to go to accomplish this task. I've discovered it returns false positives on matches, so I am attempting to use CountedSet (aka NSCountedSet) instead.

I'm able to initialize a Set without issue, but I can't get the CountedSet initializer to work. Here's what I've done...

I start with a String:

// Let's say myTextField.text = "test"
let textFieldCharacters = myTextField.text?.characters
// word is a string from the ENABLE list of words
let wordCharacters = word.characters

Then I dump the characters into an Array:

var wordCharactersArray = [Character]()
for character in wordCharacters {
    wordCharacterArray.append(character)
}


var textFieldCharactersArray = [Character]()    
for character in textFieldCharacters {
    wordCharacterArray.append(character)
}

Then I create a Set from the character arrays:

let textFieldSet = Set<Character>(textFieldCharactersArray)
let wordSet = Set<Character>(wordCharactersArray)

Finally, I test to see if the textFieldSet is a superSet of wordSet with the following:

textFieldSet.isSuperset(of: wordSet)

Going back to my example, if myTextField.text is "test", I'm returning values for word whose characters are a superset of the wordSet, but the counts of the individual elements don't match the character counts of myTextField.text

In researching my issue, I've found CountedSet (fka NSCountedSet), which I think would resolve my issue. It has two method signatures:

public convenience init(array: [AnyObject])
public convenience init(set: Set<NSObject>)

I've tried initializing the 2 sets of characters like so:

let textFieldSet = CountedSet(array: textFieldCharacterArray)
let wordSet = CountedSet(array: wordCharacterArray)

I get the following error for the sets

Cannot convert value of type '[Character]' to expected argument type '[AnyObject]'.

So I tried initializing the set like this:

let textFieldSet = CountedSet(array: textFieldCharacterArray as! [AnyObject])

Which yields the following error:

'AnyObject' is not a subtype of 'Character'

I've also tried to initialize the CountedSet with a Set, per the method signature, but I get errors when I try to do that, too.

Any suggestions how to initialize a CountedSet would be greatly appreciated.


Solution

  • You are correct that if you need to compare not just the presents of elements but also their count, you should use CountedSet, which is a renaming of NSCountedSet for swift 3.0. The problem you are running into is CountedSet can only accept elements that are objects and Characters are not. As Eric D points out in their comment, the easies way to get around this is by mapping your [Character] to [String] which will bridge to [NSString].

    You are not running into this problem using Set, because it is a native Swift collection type that initialize with elements of any type. This is why you can initialize a Set with [Character].

    To see the difference:

    let word = "helo"
    let wordCharacters = Array(word.characters)
    let wordSet = Set(wordCharacters)
    let wordCharStrings = wordCharacters.map{String($0)}
    let wordCountedSet = CountedSet(array: wordCharStrings)
    
    let textField = "hello"
    let textFieldCharacters = Array(textField.characters)
    let textSet = Set(textFieldCharacters)
    let textFieldCharStrings = textFieldCharacters.map{String($0)}
    let textFieldCountedSet = CountedSet(array: textFieldCharStrings)
    
    textFieldCountedSet.isSubset(of: wordCountedSet as! Set<NSObject>) // returns false, but if word had two or more l's it would return true
    textSet.isSubset(of: wordSet) // returns true