Swift enum operator overloading - ios

I have define an enum, and I want to use it as a key for a dictionary.
When I'm trying to access a value using the enum as a key, i get an error about the enum not being convertible to DictionaryIndex<Constants.PieceValue, Array<String>> where Constants.PieceValue is an enum that looks like this:
public enum PieceValue: Int {
case Empty = 0,
WKing = 16,
WQueen = 15,
WRook = 14,
WBishop = 13,
WKnight = 12,
WPawn = 11,
BKing = 26,
BQueen = 25,
BRook = 24,
BBishop = 23,
BKnight = 22,
BPawn = 21
}
I read a few threads but haven't found any clear answers.
I also declared the operator overloading function for the enum outside the Constants class.
func == (left:Constants.PieceValue, right:Constants.PieceValue) -> Bool {
return Int(left) == Int(right)
}
This is the line that Xcode complains about:
self.label1.text = Constants.pieceMapping[self.pieceValue][0]
Constants.pieceMapping has the following type: Dictionary<PieceValue, Array<String>>

That's the typical optional problem: when you query a dictionary, it returns an optional value, to account for cases when the key is not found. So this:
Constants.pieceMapping[self.pieceValue]
is of Array<String>? type. In order to access to that array, you first have to unwrap from the optional, using either forced unwrapping:
Constants.pieceMapping[Constants.PieceValue.BPawn]![0]
or in a safer way using optional binding:
if let array = Constants.pieceMapping[Constants.PieceValue.BPawn] {
let elem = array[0]
}

Related

Access to key inside Dictionary get in Swift

Is it possible to access the key inside of a Dictionary get in Swift ?
The main idea is:
In this code
var _dict:[String:String] = [:]
var dict:[String:String] {
//get the key
return _dict
}
_dict = ["key 1":"Value 1","key 2":"Value 2"]
print(dict["key 1"])
Access the key to check if the value exists, if it exists return the value if not generate the value for that key
Did you know that Dictionary allows you to specify a default value in its subscript to avoid dealing with optional values. It works like so:
let dict = ["a": 1, "b": 2]
let c = dict["c", default: 3]
print(c) // 3
but that doesn't change the dictionary - it's still only has "a" and "b" keys, which is the expected behavior.
I think what you're asking about is whether it's possible to mutate the dictionary with a default value. The answer is yes - you could create a subscript with a mutating get.
But it's the wrong thing to do!
You will effectively have a getter with side-effects, which is typically a bad practice.
In any case, this is how you could implement a subscript with a new parameter setDefault:
extension Dictionary {
subscript(key: Key, setDefault defaultVal: #autoclosure () -> Value) -> Value {
mutating get {
if let val = self[key] {
return val
} else {
let val = defaultVal()
self[key] = val
return val
}
}
}
}
// dict needs to be a var now
var dict = ["a": 1, "b": 2]
let c = dict["c", setDefault: 3]
Now, this will mutate dict and it will be ["a": 1, "b": 2, "c": 3]

Access Value in Dictionary using Random Number

I am trying to access the value from a dictionary using a random number, but I am lost, can someone please guide?
Here is what I have:
var themes = ["Halloween": "πŸ˜ˆπŸ’€πŸ€‘πŸ‘»πŸ€–πŸ‘½", "Sports": "πŸ‰πŸ“β›³οΈβš½οΈπŸŽ³πŸŽ±" , "Faces": "πŸ˜€πŸ˜πŸ˜¨πŸ€—πŸ˜€πŸ€€", "Animal": "πŸ¦“πŸ˜ΌπŸ˜ΊπŸ˜ΏπŸ™€πŸ™ˆ"]
// This Does not work for some reason?
lazy var themeRandomNumber = themes.count.arc4random
lazy var currentTheme = themes[themeRandomNumber]
//Cannot subscript a value of type[String : String]' with an index of type 'Int'
This makes sense since, I am trying to access the key using an Int when it is obviously a String, but not sure how to proceed?
lazy var currentEmoji = themes[currentTheme]
extension Int{
var arc4random: Int{
if self > 0 {
return Int(arc4random_uniform(UInt32(self)))
} else if self < 0 {
return -Int(arc4random_uniform(UInt32(abs(self))))
} else {
return 0
}
}
}
Just replace
lazy var currentEmoji = themes[currentTheme]
with
var currentTheme = themes.randomElement()
print(currentTheme?.value) //Optional("πŸ‰πŸ“β›³οΈβš½οΈπŸŽ³πŸŽ±")
print(currentTheme?.key) //Optional("Sports")
Here randomElement is new property which you can use to get random element.
Because you're not accessing the key of your dictionary, you need to select "Halloween", "Sports", "Faces" or "Animal" - your themes dict's keys.
You can use some custom mapping method with Int.random(in: 0...3) or a Keys enum conforming to CaseIterable, and then you need to select a random character (emoji) in the String for your given Key (via a random number in the range 0..<string.length).
EDIT
With Swift 4.2+ (Xcode 10) you can use randomElement():
var themes = ["Halloween": "πŸ˜ˆπŸ’€πŸ€‘πŸ‘»πŸ€–πŸ‘½", "Sports": "πŸ‰πŸ“β›³οΈβš½οΈπŸŽ³πŸŽ±" , "Faces": "πŸ˜€πŸ˜πŸ˜¨πŸ€—πŸ˜€πŸ€€", "Animal": "πŸ¦“πŸ˜ΌπŸ˜ΊπŸ˜ΏπŸ™€πŸ™ˆ"]
var randomEmoji = themes.randomElement()?.value.randomElement()

Pass Int to enum and return a String

I'm trying to accomplish a task which is passing an integer value to enum, and return a specific String for the passed in integrer.
I'm using enum because the integers are known and each of them has a meaning. I have done the following:
enum Genre: String {
case 28 = "Action"
case 12 = "Adventure"
case 16 = "Animation"
case 35 = "Comedy"
case 80 = "Crime"
}
What I'm expecting: when passing one of the cases, I want to return the String association.
Please, if you have a question or need any further into, ask it in the comment.
How about this
enum Genre: Int {
case action = 28
case adventure = 12
case animation = 16
case comedy = 35
case crime = 80
}
And use it like this
// enum as string
let enumName = "\(Genre.action)" // `action`
// enum as int value
let enumValue = Genre.action.rawValue // 28
// enum from int
let action = Genre.init(rawValue: 28)
Hope it helps. Thanks.
We can not have Int as an enum case name.
Try this:
enum Genre: Int {
case action = 28, adventure = 12, animation = 16, comedy = 35, crime = 80
func getString() -> String {
switch self {
case .action: return "Action"
case .adventure: return "Adventure"
case .animation: return "Animation"
case .comedy: return "Comedy"
case .crime: return "Crime"
}
}
}
let gener = Genre.action
print(gener.getString())//"Action"
And if you only know integer value, do this:
let gener1 = Genre(rawValue: 12)!
print(gener1.getString())//"Adventure"
let Genre = [28:"action",
12: "adventure",
16: "animation",
35: "comedy",
80: "crime"]
Example use:
let retValue = Genre[28]//"action"
Here is playground demo:
I suggest creating a dictionary that achieves the mapping you need, and creating constants for your keys to use them.
You can start by creating a class called Constants and putting the following constants in it:
static let action = 28
static let adventure = 12
// ... The rest of your constants.
// Then create a dictionary that contains the values:
static let genre = [action : "Action", adventure : "Adventure"] // And so on for the rest of your keys.
Then you could access any value you need using that dictionary like so:
let actionString = Constants.genre[Constants.action]
Hope this helps.

Data structure for bidimensional array

I have an array of string arrays.
it has to be declared empty, and it's later filled one array at a time.
I have tried any possible data structure, but none of them seems to be working.
For example, if I declare
var array = [[String]]()
the app crashes when I try to append elements, like
array[1][0] = "Some string"
The only way I managed to make it work is by declaring the array with a fixed number of elements, but that's not good for my purpose.
I thought this was very easy to accomplish, but I encountered a lot of problems, any solution?
You cannot append to empty array like this array[1][0] = "Some string". If you want to do that, you need to create an array with certain size and repeated value. But you want to create 2d-array, so that can be implemented like this:
extension Array {
static func bidimensional<T>(row: Int, _ column: Int, repeatedValue: T) -> [[T]] {
var resultArray = [[T]]()
for _ in 0...column {
resultArray.append([T](count: row, repeatedValue: repeatedValue))
}
return resultArray
}
}
var arr = [String].bidimensional(2, 2, repeatedValue: "")
arr[1][0] = "Hello there!"
print(arr[1][0]) // "Hello there!"
Update
Extension add new functionality to an existing class, structure, or enumeration type. In our case we extend Array type with function bidimensional. The static means, that it's type method, not instance method. The T means, that it's generic method. In this case, you can call your bidimensional function not only with one specific type (like String), but any type you want (String, Int, Double, etc.). Well, the bidimensional's func code is pretty simple. We just create empty 2D resultArray with our custom (T) type. Then we fill this array with our repeatedValue and return it. Thats all.
P.S. To be clear with generics there are several examples:
[Int].bidimensional(2, 2, repeatedValue: 0) // 2x2 with repeatedValue 0
[Double].bidimensional(5, 1, repeatedValue: 2.1) // 5x1 with repeatedValue 2.1
------
struct MyCustomStruct {
var variable: String
}
var myCustomStruct = MyCustomStruct(variable: "repeat")
var arr = [MyCustomStruct].bidimensional(2, 2, repeatedValue: myCustomStruct)
print(arr[0][1].variable) // "repeat"
Update 2 for this comment.
class MyClass {
var arr = [[String]]()
func fill2DArrayInLoop() {
arr = [String].bidimensional(2, 2, repeatedValue: "")
for i in 0...1 {
for j in 0...1 {
arr[i][j] = "row: \(i); col: \(j)"
}
}
}
}
let myClass = MyClass()
myClass.fill2DArrayInLoop()
print(myClass.arr[0][1]) // "row: 0, col: 1"
Found the way to do this better (see this answer):
class MyClass {
var arr = Array(count: 2, repeatedValue: Array(count: 2, repeatedValue: ""))
func fill2DArrayInLoop() {
for i in 0...1 {
for j in 0...1 {
arr[i][j] = "row: \(i); col: \(j)"
}
}
}
}
let myClass = MyClass()
myClass.fill2DArrayInLoop()
print(myClass.arr[0][1]) // "row: 0, col: 1"
Now you don't need to create extension. Just put another declaration of array to repeatedValue: parameter.

Find the index of a certain value in array using swift

Following the instructions reported in the documented swift manual
http://swift-ios.co/standard-functions-in-swift/
I found the code the extract the index of a certain value of an array using
var languages = ["Swift", "Objective-C"]
find(languages, "Objective-C") == 1
find(languages, "Java") == nil
find([29, 85, 42, 96, 75], 42) == 2
The problem is that the output value doesn' t have the same type of the elements os the starting array, since the output in the console is (for the last line)
Optional(2)
What if I want to get the 2 as Int or Double?
It is Int? (a.k.a. Optional<Int>). You need to extract Int from it. You can use if-let syntax
if let index = find([29, 85, 42, 96, 75], 42) {
// index is Int
}

Resources