Consider this enum.
enum FilmGenre: String, CaseIterable {
case horror = "Horror"
case comedy = "Comedy"
case animation = "Animation"
case romance = "Romance"
case fantasy = "Fantasy"
case adventure = "Adventure"
}
Is there a way to write it like this?
let filmGenres: [FilmGenre.RawValue] = [.horror.rawValue,
.comedy.rawValue,
.animation.rawValue]
The compiler complains with an error:
Type 'FilmGenre.RawValue' (aka 'String') has no member 'horror'
The best I can do is like this.
let filmGenres: [FilmGenre.RawValue] = [FilmGenre.horror.rawValue,
FilmGenre.comedy.rawValue,
FilmGenre.animation.rawValue]
I've tried various combinations from the auto-complete.
let filmGenres: [FilmGenre.AllCases.Element.RawValue] = [...]
Is it not possible to do it in Swift 5.4?
[FilmGenre.RawValue] translates to [String] in the case and clearly String doesn't know anything about .horror that is defined in other type FilmGenre.
What you can do is -
enum FilmGenre: String, CaseIterable {
case horror = "Horror"
case comedy = "Comedy"
case animation = "Animation"
case romance = "Romance"
case fantasy = "Fantasy"
case adventure = "Adventure"
}
/// Have a variable that is of `[FilmGenre]` type
/// This will allow you to use the type safety you are looking for
let filmGenres: [FilmGenre] = [.horror, .comedy, .animation]
/// This one you can use anywhere else as you like
/// This will give you `[String]` which is what you want in this case
let filmGenreRawValues = filmGenres.map({ $0.rawValue })
You can add a static function with var args to your enum for a more dynamic way to create the array
static func arrayWithRawValues(_ genre: FilmGenre...) -> [Self.RawValue] {
genre.map(\.rawValue)
}
Example
print(FilmGenre.arrayWithRawValues(.horror, .comedy, .animation))
Output
["Horror", "Comedy", "Animation"]
Related
I was wondering if there is a possibility to set a class property via an array:
class Test {
var x: Int = 0
var y: Int = 0
var z: Int = 0
}
let newTest = Test()
let myArray = [newTest.x, newTest.y, newTest.z]
So I created an Array with the properties of a class. Now I want to access the property object itself and set it to a value, kind of like this (I know this sets the array at index 0 to 1, but I hope the general idea is clear):
myArray[0] = 1 // I want to set newTest.x = 1
The following one of course works but I need the property objects themself in the array:
let myArray = [newTest, newTest, newTest]
myArray[0].x = 1
UPDATE
What I have now is an array with integers, but I need the reference to the class property, so I can set its value.
The Use Case:
I have an TextField as a user input. The user input should be splitted by a ','. And I don't know in advance, how many items I will get.
And therefore I thought I run a for loop, so I can set class properties based on the number of items.
newItem = Item()
var myClassPorperties = [newItem.cat1, newItem.cat2, newItem.cat3]
if let categoriesArray = categoriesTextField.text?.components(separatedBy: ",") {
for i in 0...categoriesArray.count - 1 {
myClassProperties[i] = categoriesArray[i]
}
}
It seems a bit strange to take a list of comma separated values and use them to update an object but given that here is one way to do it.
Create a function to map between an index (in the array) and a property in your class
func update(at index: Int, with value: Int) {
switch index {
case 0:
x = value
case 1:
y = value
case 2:
z = value
default:
print("Value \(value) at \(index) igonred")
}
}
and use it like this
categoriesArray.compactMap(Int.init).enumerated().forEach(newTest.update)
Another solution is to work with KeyPath
func keyPath(for index: Int) -> WritableKeyPath<Test, Int>? {
switch index {
case 0:
return \.x
case 1:
return \.y
case 2:
return \.z
default:
return nil
}
}
and then use the following code to update the object using your array
categoriesArray.compactMap(Int.init).enumerated().forEach {
if let keyPath = keyPath(for: $0.offset) {
newTest[keyPath: keyPath] = $0.element
}
}
I still think it would be better to have one input field in the UI for every property in your class so you can have a direct one-to-one connection between them.
I apologize in advance, this is hard to explain. I will provide more detail if needed.
This is the Constants struct that I use to reference UIButtons in a collection array and use as keys for dictionaries.
struct Constants {
static let scoreA = "score_a"
static let scoreB = "score_b"
static let scoreC = "score_c"
static let scoreD = "score_d"
static let constantsArray = [kScoreA, kScoreB, kScoreC, kScoreD]
enum Scores: Int, CaseIterable { case scoreA = 1, ScoreB, ScoreC, ScoreD}
}
My initial view controller has a lot of UIButtons. All the score UIButtons are tagged from 1 and up. The UIButtons are hooked to an IBOutlet UIButton array. This way I can avoid having too many IBOutlets
#IBOutlet var collectionOfScoreButtons: Array<UIButton>!
I reference the UIButtons using code like this throughout my App.
if let scoreAButton = collectionOfScoreButtons[Constants.Scores.scoreA.rawValue - 1]
The UIButtons order is the same as the enum's order e.g. scoreA is the first item in the enum and scoreA button is the first button in the array.
And I can retrieve a dictionary key like this, so I can update its value
// after pushing a score button
func handleScoreValue(tag: Int) {
let scoreKey = Constants.constantScoreArray[tag - 1]
dictionary[scoreKey, default: 0] += 1
}
I am not sure if there is a better way to handle this situation. The code works well, but I feel like there is a better way.
I can't see any advantage of using Scores enum to get reference for certain button, you have to specify index anyway
if let scoreAButton = collectionOfScoreButtons[0]
also you can make your Constants enum and implement CaseIterable protocol which allows you to make array of all enum's cases using Enum.allCases
enum Score: String, CaseIterable {
case A = "score_a"
case B = "score_b"
case C = "score_c"
case D = "score_d"
}
then I believe you have IBAction for your button so you can get index of sender in your array of buttons. Then you don't have to set tag of UIButton
#IBAction func buttonPressed(_ sender: UIButton) {
if let index = collectionOfScoreButtons.index(of: sender) {
handleScoreValue(index: index)
}
}
Finally you can get scoreKey as rawValue of case for certain index in allCases array
func handleScoreValue(index: Int) {
let scoreKey = Score.allCases[index].rawValue
dictionary[scoreKey, default: 0] += 1
}
Why not just use an enum directly ?
enum Constants: String, CaseIterable {
case scoreA = "score_a"
case scoreB = "score_b"
case scoreC = "score_c"
case scoreD = "score_d"
}
So you can loop through your enum cases like
Constants.allCases[anyIndex].rawValue
In the popular board game Monopoly, players have the opportunity to buy/trade different properties, and when they monopolize a certain neighborhood, they can build houses. I am trying to express all of these properties as a nested enumeration in Swift, but when it comes to expressing a player's properties in an Array, I'm stumped.
Here is what I have tried so far.
enum Property {
enum Brown {
case mediterranean, baltic
}
enum LightBlue {
case oriental, vermont, connecticuit
}
enum pink {
case stCharles, states, virginia
}
...
}
var properties: [Property] = [
Property.Brown.baltic, // ERROR: Cannot convert value of type 'Property.Brown' to expected element type 'Property'
Property.Brown.mediterranean
]
As you can see, I cannot store these Properties in a [Property] Array, because a Property.Brown isn't a Property (understandable). What would I need to change to be able to store Property.<Insert Neighborhood Here>s in an array? I understand that an [Any] would work, but I am concerned about type safety, so this won't work.
Edit 05 July 2018, 13:18 PDT
I am writing a program that will act as the bank for a Monopoly game and need a way to determine which players (or the bank) owns properties. I'm currently Playgrounding and prototyping to figure out what works for me.
As you know,
because a Property.Brown isn't a Property (understandable).
You may need a type common to your Property.Brown, Property. LightBlue, ...
Maybe you can use a protocol just for storing them in an Array:
protocol PropertyEnums {}
enum Property {
enum Brown: PropertyEnums {
case mediterranean, baltic
}
enum LightBlue: PropertyEnums {
case oriental, vermont, connecticuit
}
//...
}
var properties: [PropertyEnums] = [
Property.Brown.baltic,
Property.Brown.mediterranean,
//...
]
But I cannot be sure if this might be the best solution for you, as you are not showing the use cases of properties.
Something like this might be better for some use cases:
enum Property {
enum Brown {
case mediterranean, baltic
}
enum LightBlue {
case oriental, vermont, connecticuit
}
//...
case brown(Brown)
case lightBlue(LightBlue)
//...
}
var properties: [Property] = [
.brown(.baltic),
.brown(.mediterranean),
//...
]
Please show us how do you want to use your properties.
I would like to convert/add a string to a type [Talent.Otherlanguages]
Talent.Otherlanguages is an enum who contain many languages.
I would like to do this : otherlanguages?.append(Talent.Otherlanguage(rawValue: langue)!)
but when i do print(otherlanguages) the value is set to nil.
Do any of you have an idea to help me ?
In case you consider otherlanguages is nil it is my assamtion. Because if you send wrong rawValue to the enum constructor you going to receive crash.So you are not checking if otherlanguages is not nil and try to append something.
This is example:
enum Languages:String {
case uk = "english "
case ua = "ukrainian"
}
var languages = [Languages]()
print(languages) //[]
languages.append(Languages(rawValue: "ukrainian")!)
print(languages) //[Languages.ua]
Here is my enum:
enum myEnum : Int {
case apple = 0
case orange
case lemon
}
I would like to create it from an array.
The array elements could be the name of the enums.
let myEnumDictionary : Array<String> = ["apple","orange","lemon"]
So is it possible to create enums from an array?
No, it's not possible. In the same way you can't create classes from arrays.
Enums must be compiled. You can't create them dynamically.
If your enum rawValue must be an Int, you could either map from an array of integer raw values to a collection of enums, or add a convenience initializer which returns the enum matching a given string literal:
enum MyEnum : Int {
case apple = 0
case orange
case lemon
init?(fruit: String) {
switch fruit {
case "apple":
self = .apple
case "orange":
self = .orange
case "lemon":
self = .lemon
default:
return nil
}
}
}
let myEnumDictionary = [0, 1, 2].map{ MyEnum(rawValue: $0) }.flatMap{ $0 }
let myEnumDictionary2 = ["apple", "orange", "lemon"].map{ MyEnum(fruit: $0) }.flatMap{ $0 }
If the enum rawValue type was a string, you wouldn't need to provide an initializer:
enum MyEnum: String {
case apple
case orange
case lemon
}
let myEnumDictionary = ["apple", "orange", "lemon"].map{ MyEnum(rawValue: $0) }.flatMap{ $0 }
Of course, the easiest way to create an array of enums is just to provide a literal list of enums:
let myEnumDictionary: [MyEnum] = [.apple, .orange, .lemon]