Get enum value by nested variable - swift

If I have an enum like this:
enum Types: String {
case first = "First"
case second = "Second"
var id: Int {
switch self {
case .first: return 1
case .second: return 2
}
}
}
Given an Int value of 2, how would I get the Type, i.e. second?

If you make the enum use the CaseIterable extension you could get a array of all values using Types.allCases.
So you could get the element by doing this:
Types.allCases[id - 1]

Related

Swift: Is it possible to conditionally declare variables?

Let's say there is a struct with two variables: a and b.
It is possible to declare one of the two variables and ignore the other based on a condition?
In other words, is it possible from this struct:
struct example {
let a: Int
let b: Int
}
To do this:
struct example {
if (condition) {
let a: Int
} else {
let b: Int
}
}
This is not possible, because if you did this, what would this code do:
let e = exampleReturningFunction()
print(e.a)
If condition is false, should this crash? Should a have some default value? What value? (If so, just use a default value in the struct.)
In most cases, what you really want here is an enum with associated data:
enum Example {
case a(Int)
case b(Int)
}
If there are many related properties, you can group them together:
struct A { ... }
struct B { ... }
enum Example {
case a(A)
case b(B)
}

How can I get the property value enum?

I have an Enum with 2 cases and each take a String and an Int as properties:
public enum TestEnum {
case case1(String, Int? = nil)
case case2(String, Int? = nil)
}
I create an enum with value case1 and these 2 properties:
let e = TestEnum.case1("abc", 123)
my question is how can I get
I tried
let a = e.case1.0 // expect to get 'abc' back
let b = e.case1.1 // expect to get '123' back
print ("\(a)")
print ("\(b)")
But I get compile error 'Enum case 'case1' cannot be used as an instance member'
The type of the variables is TestEnum for both a and b. Which exact case the variable represents isn't encoded in the type. Because of this, you cannot access associated values of an enum case variable.
Instead, you need to use if case let to conditionally cast the enum case and assign its associated values to variables.
let a = TestEnum.case1("a", 1)
if case let .case1(string, int) = a {
print(string, int)
}
You can use pattern matching to access the values. (see patterns documentation)
Using switch:
switch e {
case .case1(let stringValue, let intValue):
print("stringValue: \(stringValue), intValue: \(intValue)")
default:
break
}
or if:
if case .case1(let stringValue, let intValue) = e {
print("stringValue: \(stringValue), intValue: \(intValue)")
}

Return typed value from enum associated value

I have a enum like so:
enum MyEnum {
case a(Int)
case b(String)
case c(Int)
}
let arrayOfMyEnums: [MyEnum] = [ .a(4), .b("hello"), .c(42) ]
Now I'd like to write an extension that returns the type of the associated value.
private extension MyEnum {
// this does pseudo code
func value() -> ... // Should return typed associated value, so either Int or String
{
}
}
Wonder whether this can be expressed somehow with generics so I can call arrayOfMyEnums[0].value() and would get an Int, whereas arrayOfMyEnums[1].value() would return a string?

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)
}

Accessing a String Enum by index

I have an enum in C and the index needs to be represented by a String.
How can a Swift enum of String type be used by integer index?
I would like to copy the enum to Swift, set the type to string and define all of the raw values to display text, and then use the C enum value to extract the raw value text for the Swift String enum.
Otherwise I will just create an array of strings.. But the enum would be more usable.
Swift 4.2 introduced CaseIterable which does exactly the same thing without the need to declare an allValues array. It works like this:
enum MyEnum: String, CaseIterable {
case foo = "fooString"
case bar = "barString"
case baz = "bazString"
}
and you can access its values by
MyEnum.allCases
or a value at a specific index by
MyEnum.allCases[index]
In Swift, enum types do not hold its index info of cases (at least, not provided for programmers).
So:
How can a Swift enum of String type be used by integer index?
The answer is "You cannot".
You can bind Int (or enum cases) and String values in many ways other than just create an array of strings..
For example, if your bound Strings can be the same as case labels, you can write something like this:
enum MyEnum: Int {
case foo
case bar
case baz
var string: String {
return String(self)
}
}
if let value = MyEnum(rawValue: 0) {
print(value.string) //->foo
}
If your Strings need to be a little more complex to display text, you can use Swift Dictionary to bind enum cases and Strings.
enum AnotherEnum: Int {
case foo
case bar
case baz
static let mapper: [AnotherEnum: String] = [
.foo: "FooString",
.bar: "BarString",
.baz: "BazString"
]
var string: String {
return AnotherEnum.mapper[self]!
}
}
if let value = AnotherEnum(rawValue: 1) {
print(value.string) //->BarString
}
A little bit more readable than a simple array of strings.
Simple workaround which is also useful if you want to enumerate a string enum.
enum MyEnum: String {
case foo = "fooString"
case bar = "barString"
case baz = "bazString"
static let allValues = [foo, bar, baz] //must maintain second copy of values
}
//enumeration advantage
for value in MyEnum.allValues {
print(value)
}
//get value by index
let value = MyEnum.allValues[1]
print(value) //barString
You can add an index as a part of the enum.
enum StringEnum: String, CaseIterable {
case pawn, rook, knight, bishop, king, queen
var name: String { self.rawValue.uppercased() }
var index: Int { StringEnum.allCases.firstIndex(of: self) ?? 0 }
}
And find enum cases by index with the function:
func findEnum(by index: Int) -> StringEnum? {
StringEnum.allCases.first(where: { $0.index == index })
}