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)
}
Related
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)
}
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]
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)
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 })
}
I would like to associate multiple values with an enum value, in a generic way.
This can be done in Java:
enum Test {
A("test", 2);
final String var1;
final int var2;
Test (String var1, int var2) {
this.var1 = var1;
this.var2 = var2;
}
}
public static void main(String []args){
Test test = Test.A;
System.out.println(test.var1);
}
But it looks like it's not possible with Swift? So far, according to docs, there are:
Associated values. Example (from docs):
enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
}
But this is not what I need.
Raw value. Example (from docs):
enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}
This would be what I need, but it can have only one value!
Is there an elegant solution for this...? Seems like a language design decision, as it would conflict with the associated values concept, at least in the current form. I know I could use e.g. a dictionary to map the enum values to the rest, but really missing to do this in one safe step, like in Java.
For Swift enum, you can only use (String|Integer|Float)LiteralConvertible types as the raw value. If you want to use existing type(e.g. CGPoint) for the raw value, you should follow #Alex answer.
I will provide 2 alternatives in this answer
Very simple solution
enum Test: String {
case A = "foo:1"
case B = "bar:2"
var var1: String {
return split(self.rawValue, { $0 == ":" })[0]
}
var var2: Int {
return split(self.rawValue, { $0 == ":" })[1].toInt()!
}
}
let test = Test.A
println(test.var1) // -> "foo"
You don't like this? go to next one :)
Behavior emulation using struct and static constants
struct Test {
let var1: String
let var2: Int
private init(_ var1:String, _ var2:Int) {
self.var1 = var1
self.var2 = var2
}
}
extension Test {
static let A = Test("foo", 1)
static let B = Test("bar", 2)
static let allValues = [A, B]
}
let test = Test.A
println(test.var1) // -> "foo"
But of course, struct lacks some features from enum. You have to manually implement it.
Swift enum implicitly conforms Hashable protocol.
extension Test: Hashable {
var hashValue:Int {
return find(Test.allValues, self)!
}
}
func ==(lhs:Test, rhs:Test) -> Bool {
return lhs.var1 == rhs.var1 && lhs.var2 == rhs.var2
}
Test.A.hashValue // -> 0
Test.B.hashValue // -> 1
Test.A == Test.B // -> false
In the first code, we already have allValues that is corresponding to values() in Java. valueOf(...) in Java is equivalent to init?(rawValue:) in RawRepresentable protocol in Swift:
extension Test: RawRepresentable {
typealias RawValue = (String, Int)
init?(rawValue: RawValue) {
self.init(rawValue)
if find(Test.allValues, self) == nil{
return nil
}
}
var rawValue: RawValue {
return (var1, var2)
}
}
Test(rawValue: ("bar", 2)) == Test.B
Test(rawValue: ("bar", 4)) == nil
And so on...
I know this is not "in a generic way". And one thing we never can emulate is "Matching Enumeration Values with a Switch Statement" feature in Swift. you always need default case:
var test = Test.A
switch test {
case Test.A: println("is A")
case Test.B: println("is B")
default: fatalError("cannot be here!")
}
Yes it is a design decision but you can kind of work around it in some cases.
The idea is to extend a Type to conform to one of:
integer-literalĀ floating-point-literalĀ string-literal
The solution can be found here
Bryan Chen's solution:
How to create enum with raw type of CGPoint?
The second solution presented there by Sulthan may also be a way to go for you.
I'm not familiar enough with Swift's history to know if this was possible back when the question was asked. But this is what I would do today in Swift 5.x:
enum Direction {
case north
case east
case south
case west
func name() -> String {
switch self {
case .north: return "North"
case .east: return "East"
case .south: return "South"
case .west: return "West"
}
}
func degress() -> Double {
switch self {
case .north: return 0.0
case .east: return 90.0
case .south: return 180.0
case .west: return 270.0
}
}
}
It retains all the benefits of Swift enums, chief of all, IMO, the ability for the compiler to infer when your code is exhaustive when pattern matching.