`CountedSet` initialization issue - ios

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.

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

Related

iOS Swift 3 - Argument labels '(of:)' do not match any available overloads Error

I'm getting the error message Argument labels '(of:)' do not match any available overloads. Below is the code I'm using.
let prefs = UserDefaults.standard
var id: String!
if var array = prefs.string(forKey: "myArray"){
if let index = array.index(of: id) {
array.remove(at: index)
prefs.setValue(array, forKey: "myArray")
}
}
I've seen a lot of answers on Stack Overflow with very similar code to that. So I'm not quite sure why this wouldn't be working.
Basically I'm just trying to remove the element in the array that = id then set that new array to the user defaults.
Update
Just updated the code above to show how array is getting defined. id is a string that is defined in a separate section.
By accessing prefs.string(forKey: "myArray"), you are getting a String, not an array of strings. You should use this:
if var prefs.array(forKey: "myArray") as? [String] { }
or
if var prefs.value(forKey: "myArray") as? [String] { }
Make sure to not forget putting as! [String], because the first method returns [Any], an which can contain objects of any type, not specifically String. Then your error should be solved, because index(of: ) can only be used on Arrays of specified types.
Hope it helps!
Just make an alt + Click on an "array" variable to make sure it is of type Array ([String]), not a String. To apply .index(of:) method it must be an array.
Like this:
String does not have a method .index(of:). That's what the error is pointing at. And sure make a cast to [String]? if it fits.

Hangman Program 2

I have asked a question before about this program, but it seems that not all problems are resolved. I am currently experiencing an error that states: "Cannot convert value of type 'String' to expected argument type '_Element' (aka 'Character') on the "guard let indexInWord" line:
guard let letterIndex = letters.indexOf(sender)
else { return }
let letter = letterArray[letterIndex]
guard let indexInWord = word.characters.indexOf(letter)
else {
print("no such letter in this word")
return
}
// since we have spaces between dashes, we need to calc index this way
let indexInDashedString = indexInWord * 2
var dashString = wordLabel.text
dashString[indexInDashedString] = letter
wordLabel.text = dashString
I tried converting the String 'letter' to Character but it only resulted in more errors. I was wondering how I can possibly convert String to argument type "_Element." Please help.
It is hard to treat a string like a list in swift, mostly because the String.characters is not a typical array. Running a for loop on that works, but if you are looking for a specific character given an index, it is a bit more difficult. What I like doing is adding this function to the string class.
extenstion String {
func getChars() -> [String] {
var chars:[String] = []
for char in characters {
chars.append(String(char))
}
return chars
}
}
I would use this to define a variable when you receive input, then check this instead of String.characters

Firebase query hex codes contained within randomly generated keys

I would like to query a particular child from the array of color hex codes.
Here's a snapshot of my database structure:
How do I query a particular hex code and obtain the entire array of its parent object?
You cannot query whether a specific value exists in a list. This is one of the many reasons why the Firebase documentation recommends against using arrays in the database.
But in this case (and most cases that I encounter), you may likely don't really need an array. Say that you just care about what colors your user picked. In that case, you can more efficiently store the colors as a set:
palettes
-KSmJZ....A5I
"0x474A39": true
"0xbA9A7C": true
"0xDEDEDF": true
"0x141414": true
"0x323E35": true
I did it in a different way,
made a function that does this:
let databaseRef = FIRDatabase.database().reference()
let HEX1 = hex1.text! as String
let HEX2 = hex2.text! as String
let HEX3 = hex3.text! as String
let HEX4 = hex4.text! as String
let HEX5 = hex5.text! as String
let URL = url.text! as String
// First set
let colorArray1 = [HEX2, HEX3, HEX4, HEX5, URL]
databaseRef.child("palette").child(HEX1).setValue(colorArray1)
// second set
let colorArray2 = [HEX1, HEX3, HEX4, HEX5, URL]
databaseRef.child("palette").child(HEX2).setValue(colorArray2)
// third set
let colorArray3 = [HEX1, HEX2, HEX4, HEX5, URL]
databaseRef.child("palette").child(HEX3).setValue(colorArray3)
// fourth set
let colorArray4 = [HEX1, HEX2, HEX3, HEX5, URL]
databaseRef.child("palette").child(HEX4).setValue(colorArray4)
// fifth set
let colorArray5 = [HEX1, HEX2, HEX3, HEX4, URL]
databaseRef.child("palette").child(HEX5).setValue(colorArray5)
so that when I target any of the 5 hexes, it will bring me back the whole array together with it.

Syntax explanation: square brackets in Swift

I'm studying Swift and got confusing with following syntax:
var treasures: [Treasure] = []
Treasure is custom class, declared as follow:
class Treasure: NSObject { }
In Objective-C square brackets mean method, but what do they mean in Swift?
Ok, this is the meaning of
var treasures: [Treasure] = []
var: you are declaring a variable
treasures: the name of your variable
[Treasure]: the type of your variable, in this case the type is Array of Treasure, the compiler will allow you to insert only object of type Treasure in your Array
[]: the actual object (Array) referenced by your variable, in this case an empty Array.
E.g. if you want the Array to hold 2 elements you can write
var treasures: [Treasure] = [Treasure(), Treasure()]
Hope this helps.
Update:
My example can also be written this way
var treasures = [Treasure(), Treasure()]
Infact thanks to the Type Inference the compiler can deduce the type of the variable treasures looking at the type of the assigned value.
[Treasure] is just a syntax sugar for Array<Treasure>.
The same way [String:Treasure] is just a syntax sugar for Dictionary<String,Treasure>.
[] is just an empty array of the type you defined. The same way [:] is an empty dictionary.
When it comes to Swift and square brackets, the rules are simple. They are used only in two situations:
1) working with Array and Dictionary types:
let vectors : [[Int]] = [[1,2,3],[4,5,6]]
let birthBook : [Int:[String]] = [1987:["John","William"], 1990: ["Mary"]]
2) for subscripting objects that support subscripting:
class RouteMapper {
private var routeMap : [String:String] = [:]
subscript(endpoint: String) -> String {
get {
if let route = routeMap[endpoint] {
return route
}
return "/"
}
set(newValue) {
routeMap[endpoint] = newValue
}
}
}
let routeMapper = RouteMapper()
routeMapper["users"] = "/v1/confirmed/users"
let url = routeMapper["admins"]
Since [ and ] are not allowed in custom operators, these are the only usages for now.

Is it possible to convert a JSValue into an NSNumber?

This will show the following error: 'JSValue' is not convertible to 'NSNumber'. If it's not possible to convert, how should I go about getting the JSValue and assigning it to my NSNumber variable?
var sentences:NSNumber = getSentences.callWithArguments([])
According to the header file JSValue.h, you need to call toNumber() on your JSValue object. So it's probably:
var sentences:NSNumber = getSentences.callWithArguments([]).toNumber()
You can try:
if let sentences = getSentences.callWithArguments([]) as? NSNumber {
// consume sentences here
}
The if let structure is probably the simplest access to it since it may not actually BE a number, If that doesn't work, you'll have to go back to JavaScriptCore and call JSValueToNumber:
let sentences = getSentences.callWithArguments([])
let nSentences = JSValueToNumber(context, sentences, nil)

Resources