How to enumerate an enum with String type?
Swift 4.2+
Starting with Swift 4.2 (with Xcode 10), just add protocol conformance to CaseIterable
to benefit from allCases
. To add this protocol conformance, you simply need to write somewhere:
extension Suit: CaseIterable {}
If the enum is your own, you may specify the conformance directly in the declaration:
enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }
Then the following code will print all possible values:
Suit.allCases.forEach {
print($0.rawValue)
}
Compatibility with earlier Swift versions (3.x and 4.x)
If you need to support Swift 3.x or 4.0, you may mimic the Swift 4.2 implementation by adding the following code:
#if !swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif
How to enumerate an enum with String type?
Swift 4.2+
Starting with Swift 4.2 (with Xcode 10), just add protocol conformance to CaseIterable
to benefit from allCases
. To add this protocol conformance, you simply need to write somewhere:
extension Suit: CaseIterable {}
If the enum is your own, you may specify the conformance directly in the declaration:
enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }
Then the following code will print all possible values:
Suit.allCases.forEach {
print($0.rawValue)
}
Compatibility with earlier Swift versions (3.x and 4.x)
If you need to support Swift 3.x or 4.0, you may mimic the Swift 4.2 implementation by adding the following code:
#if !swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif
How to enumerate an enum
foreach (Suit suit in (Suit[]) Enum.GetValues(typeof(Suit)))
{
}
Note: The cast to (Suit[])
is not strictly necessary, but it does make the code 0.5 ns faster.
TypeScript iterate over string enum with the enum type instead of string type
I'll post my comments as an answer so the questions won't remain unanswered.
The type of cipher
will have the keys as the values of Enum
, that is:
let cipher: {
a: string;
b: string;
}
because you are using a string enum. But when iterating with for...in
you iterate over the enumerable properties of the generated object, those properties are ['A', 'B']
because the generated object(TypeScript v4) would be:
var Enum;
(function (Enum) {
Enum["A"] = "a";
Enum["B"] = "b";
})(Enum || (Enum = {}));
So you need to iterate over the enum values.
For this you can use Object.values
to get an array of it's values and for...of
to iterate over it. This way, letter
type will be Enum
.
for (const letter of Object.values(Enum)) {
cipher[letter] = 'test'; // error: letter is of type 'string' but needs to be 'Enum'
}
I never used for...in
with an enum before but I would have expected the compiler to have enough information so the for...in
strictly types letter
to a union of "A" | "B"
but it seems that it widens the type to string
.
Iterate on string enum
@Artem and @betadeveloper pointed out that I can use the keyof typeof Locales
type for my approach. The solution I eventually came up with looks like this:
const keys: (keyof typeof Locales)[] = <(keyof typeof Locales)[]>Object.keys(Locales);
for (const key of keys) {
const locale: string = Locales[key];
console.log(locale); // Prints 'en', 'fr' and so on
}
Enum from String
Using mirrors you could force some behaviour. I had two ideas in mind. Unfortunately Dart does not support typed functions:
import 'dart:mirrors';
enum Visibility {VISIBLE, COLLAPSED, HIDDEN}
class EnumFromString<T> {
T get(String value) {
return (reflectType(T) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}
}
dynamic enumFromString(String value, t) {
return (reflectType(t) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}
void main() {
var converter = new EnumFromString<Visibility>();
Visibility x = converter.get('COLLAPSED');
print(x);
Visibility y = enumFromString('HIDDEN', Visibility);
print(y);
}
Outputs:
Visibility.COLLAPSED
Visibility.HIDDEN
How to define an enum with string value?
You can't - enum values have to be integral values. You can either use attributes to associate a string value with each enum value, or in this case if every separator is a single character you could just use the char
value:
enum Separator
{
Comma = ',',
Tab = '\t',
Space = ' '
}
(EDIT: Just to clarify, you can't make char
the underlying type of the enum, but you can use char
constants to assign the integral value corresponding to each enum value. The underlying type of the above enum is int
.)
Then an extension method if you need one:
public string ToSeparatorString(this Separator separator)
{
// TODO: validation
return ((char) separator).ToString();
}
Related Topics
Swiftui: How to Make Textfield Become First Responder
The Use of Swift 3 @Objc Inference in Swift 4 Mode Is Deprecated
How to Make a Weak Protocol Reference in 'Pure' Swift (Without @Objc)
Unexpected Non-Void Return Value in Void Function (Swift 2.0)
Cannot Assign Property in Method of Struct
Swift Protocol Inheritance and Protocol Conformance Issue
Why Is 'Nil' Not Compatible With 'Unsafepointer≪Cgaffinetransform≫' in Swift 3
Swiftui Picker Separate Texts For Selected Item and Selection View
How to Resolve "Ambiguous Use Of" Compile Error With Swift #Selector Syntax
How to Get the Count of a Swift Enum
Nsurlsession Concurrent Requests With Alamofire
Multiple Functions With the Same Name
Why My Return Is Nil But If I Press the Url in Chrome/Safari, I Can Get Data
Swift - Spritekit Cgpoint Alignment