concatenate nested enum string in swift - swift

I want to declare an enum type (say, ParentEnum) which will be used as an argument of a function, it contains a number of "child" enums and each case of the child enum uses the name of the child enum as a prefix, here's an example:
enum ParentEnum: String {
enum ChildEnum1: String {
case c1 = "/ChildEnum1/c1"
case c2 = "/ChildEnum1/c2"
}
enum ChildEnum2: String {
case c1 = "/ChildEnum2/c1"
case c2 = "/ChildEnum2/c2"
}
...
}
Is there a way to generalize the "/ChildEnumX" part so I only need to define "/cX" as its rawValue, and when I call ParentEnum.ChildEnumX.c1, it will give me "/ChildEnumX/c1" like the example above.
I hope this makes sense, thanks in advance.

Just remembered to update this post in case someone else has the same problem, this is what I came up with:
enum ParentEnum {
case Child1(ChildEnum1)
case Child2(ChildEnum2)
enum ChildEnum1: String {
case c1 = "/c1"
case c2 = "/..."
}
enum ChildEnum2: String {
case c1 = "/..."
case c2 = "/..."
}
...
var rawValue: String {
switch self {
case .Child1(let child):
return "ChildEnum1/\(child.rawValue)"
case .Child2(let child):
return "ChildEnum2/\(child.rawValue)"
}
}
}
if you were using ParentEnum as an argument of a function, .Child1(.c1).rawValue would yield "ChildEnum1/c1". You could also override the rawValue of the Child enums to further extend the nesting levels. Hope this helps.

Related

Using two enums in Swift

I have two enums Items and Filters as
enum Items: Int {
case ItemA = 0,
ItemB,
ItemC,
ItemD,
ItemE
}
enum Filters: Int {
case FilterA = 0,
FilterB
}
I need to have the Items enum to be in Int.
And I am trying to implement Filters enum so that:
Filters(0).filter() returns [ItemA, ItemB]
Filters(1).filter() returns [ItemC, ItemD, ItemE]
Filters(0).count() returns 2
Filters(1).count() returns 3
Enums in swift is so much different than how I know enums from C... Can't get a grasp. Thanks!
There are several errors and bad practices in your code.
An enum name must be sigular
An enum case name should begin with a lowercase letter
An enum case name should not contain the name of the enum itself (eg. a instead of ItemA)
The filter and count methods IMO should be added to the Item enum
The filter and count methods should receive a Filter value, not an Int
You are free to manually assign 0 to the first case but this is not needed because the compiler will do it for you
IMO Condition is a better name for the Filter enum
Solution
Here's my solution
enum Condition: Int {
case a, b
}
enum Item: Int {
case a, b, c, d, e
static func filter(with condition: Condition) -> [Item] {
switch condition {
case .a: return [.a, .b]
case .b: return [.c, .d, .e]
}
}
static func count(with condition: Condition) -> Int {
return filter(with: condition).count
}
}
Usage
Item.filter(with: .a) // [a, b]
Item.filter(with: .b) // [c, d, e]
Item.count(with: .a) // 2
Item.count(with: .b) // 3
I hope that you can find this useful:
enum Items: Int {
case ItemA = 0,
ItemB,
ItemC,
ItemD,
ItemE
}
enum Filters: Int {
case FilterA = 0,
FilterB
func items() -> [Items]? {
switch self {
case .FilterA:
return [Items.ItemA, Items.ItemB]
case .FilterB:
return [Items.ItemC]
}
return nil
}
func count() -> Int {
return items()?.count ?? 0
}
}
let filter = Filters.FilterA
filter.items()
filter.count()
you can instantiate filter enum like this also:
let filter = Filters(rawValue: 0)
FYI swift cases should start with lowercase by the book, I left them uppercased to do not creat more confusions compare to your question code snippet.
Since this looks like static data a simple solution would be to keep the different sets of enum elements in a dictionary. And I agree with another poster that the naming of your enum could be better
enum Item: Int {
case a, b, c, d, e
}
let itemFilter = [0: [Item.a, Item.b]],
[1: [Item.c, Item.d, Item.e]]
if let filter = itemFilter[0] {
print(filter)
print(filter.count)
}

Dynamically initialize enum based on associated value

This is my enum:
enum E {
case a(Int), b(String)
}
The enum's associated value types are unique and always exactly one.
Let's say I have this variable:
let myInt = 0
I want to create an instance of E, based on variable myInt, dynamically. This should result in:
E.a(0)
But in the 'real world', I don't know what property I get. I only know one thing: I can initialize enum E with it. I need to dynamically initialize the enum, based on a property value. I currently have a huge switch on the property to initialize the enum, I don't want that.
But I have no idea how to accomplish this task. I tried mirroring the enum type, but I get a complex type and I have no idea how to proceed initializing it even if I know the types.
So I get a property of a certain type. I know that certain type matches a case in enum E, because there is exactly one case which associated value corresponds to the property type. I want to initialize an instance of that enum with that case, with the value of the property.
If your only starting point is the type of what will eventually be an associated value, you can use a switch statement:
enum E {
case a(Int)
case b(String)
init(associatedValue: Any) {
switch associatedValue {
case is Int:
self = .a(associatedValue as! Int)
case is String:
self = .b(associatedValue as! String)
default:
fatalError("Unrecognized type!")
}
}
}
let response = E(associatedValue: 1) // .a(1)
let other = E(associatedValue: "haha!") // .b("haha!")
The problem here is this switch must be exhaustive, meaning cover all types. So you either need a dumping ground case (.unreachable(Any)) or a fatalError so you can catch these in development.
You can use a custom initializer: (I have used more descriptive names)
enum TypeFinder {
case int(Int)
case string(String)
case unknown(Any)
init(value: Any) {
switch value {
case let v as Int: self = .int(v)
case let v as String: self = .string(v)
default: self = .unknown(value)
}
}
}
Testing:
var unknownTypeValue: Any = "Testing.."
print(TypeFinder(value: unknownTypeValue))
unknownTypeValue = 1234
print(TypeFinder(value: unknownTypeValue))
unknownTypeValue = true
print(TypeFinder(value: unknownTypeValue))
I believe you can do something like that
enum E: ExpressibleByStringLiteral, ExpressibleByIntegerLiteral {
case a(Int), b(String)
init(stringLiteral value: StringLiteralType) {
self = .b(value)
}
init(integerLiteral value: IntegerLiteralType) {
self = .a(value)
}
}

How to get strings from enum

I am trying to organize a bunch of string parameters in a enum. That way I can exclude the possibility of typos.
enum CompassPoint: String {
case n = "North"
case s = "South"
case e = "East"
case w = "West"
}
If I do it like this I need to use .rawValue to access the string. That is pretty ugly.
If I do it like this:
enum CompassPointAlt: String {
case n
case s
case e
case w
var str: String {
switch self {
case .n: return "North"
case .s: return "South"
case .e: return "East"
case .w: return "West"
}
}
}
I have to use the .str property to get the value. Which is visually more explicit, but the declaration is cumbersome.
There has to be a better way. Does anyone have a tipp for me?
Thanks
You could pull off some variation of the following:
import Foundation
enum Test: String, CustomStringConvertible {
case n = "North"
case s = "South"
case e = "East"
case w = "West"
var description: String {
return self.rawValue
}
}
print("Enum: \(Test.n)") // Prints: Enum: North
CustomStringConvertible lets you run the code in the description variable without explicitly referencing it when you want to convert to a string. If you dont need the enums to have custom associated values like Hi and Bye above and you are cool with having the name of the type be what prints, then you can get as small as this:
import Foundation
enum TestSmall {
case North
case South
}
print("Enum: \(TestSmall.North)") // prints: Enum: North
You can use static in combination with enum if you are mostly interested in using the enum for constants
enum CompassPoint {
static let n = "Nort"
static let s = "South"
static let e = "East"
static let w = "West"
}

Convert string to enum

I have the following situation
enum FooEnum: Int {
fooCase = 1245325,
fooCase2 = 3525325
}
Is there a way for me to somehow pass the string fooCase or fooCase2 to the FooEnum type and the FooEnum to generate a variable of type FooEnum with the following enum presentations: FooEnum.fooCase or FooEnum.fooCase2
PS: I can't change Int to String since I am keeping an order with the integers.
Example: (Pseudo code)
FooEnum c = FooEnum("fooCase")
c has chosen fooCase
Like structs and classes you can add initializers to enums
enum FooEnum: Int {
case fooCase = 1245325
case fooCase2 = 3525325
public init?(_ string: String) {
switch string {
case "fooCase": self = .fooCase
case "fooCase2": self = .fooCase2
default: return nil
}
}
}
let c = FooEnum("fooCase") // `FooEnum c =` is ObjC syntax ;)

Extract associated value from enum regardless of the case in Swift

I want to get the associated value of swift enum object, is there a way to do it shorter/better than in switch statement below?
enum Test {
case a(Int), b(Int), c(Int)
}
func printValue(_ t: Test) {
switch t {
case .a(let v), .b(let v), .c(let v): print("value \(v)")
}
}
Your code for extracting the associated value from multiple enums is the most economical and easy-to-read, there's no need to improve it.
However, the fact that you are looking to extract an associated value regardless of enum's case suggests that you are not using associated values correctly: rather than associating a value with each individual case, you should create a composite type that holds the Int and an enum without an associated value, i.e.
enum Test {
case a, b, c
}
class MyClass {
var num : Int
var tst : Test
}
Now that the associated value is "outside" each enum element, it can be accessed independently of the case, and you can also give it a meaningful name, which adds to readability of your program.
You might want to use mirror type - it's not the better way, but it can be helpful in some cases:
enum Test {
case a(Int), b(Int), c(Int)
}
func printValue(_ t: Test) {
let mirror = Mirror(reflecting: t)
print(mirror.children.first?.value ?? "")
}
printValue(.a(15))
Also using if/case like this, it's a shorter way if you need to extract value only from one case, sometimes it's helpful:
if case .a(let val) = t {
print("value \(val)")
}
Or may be raw value will fit better for your case:
enum Test: Int {
case a = 1
case b = 2
case c = 5
}
func printValue(_ t: Test) {
print("value \(t.rawValue)")
}
printValue(.a)