Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 234 Vote(s) - 3.49 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to enumerate an enum with String type?

#31
I did it using computed property, which returns the array of all values (thanks to this post

[To see links please register here]

). However, it also uses int raw-values, but I don't need to repeat all members of enumeration in separate property.

**UPDATE** Xcode 6.1 changed a bit a way how to get enum member using `rawValue`, so I fixed listing. Also fixed small error with wrong first `rawValue`.

enum ValidSuits: Int {
case Clubs = 0, Spades, Hearts, Diamonds
func description() -> String {
switch self {
case .Clubs:
return "♣︎"
case .Spades:
return "♠︎"
case .Diamonds:
return "♦︎"
case .Hearts:
return "♥︎"
}
}

static var allSuits: [ValidSuits] {
return Array(
SequenceOf {
() -> GeneratorOf<ValidSuits> in
var i=0
return GeneratorOf<ValidSuits> {
return ValidSuits(rawValue: i++)
}
}
)
}
}
Reply

#32
Here is a method I use to both iterate an `enum` and provide multiple values types from one `enum`


enum IterateEnum: Int {
case Zero
case One
case Two
case Three
case Four
case Five
case Six
case Seven

//tuple allows multiple values to be derived from the enum case, and
//since it is using a switch with no default, if a new case is added,
//a compiler error will be returned if it doesn't have a value tuple set
var value: (french: String, spanish: String, japanese: String) {
switch self {
case .Zero: return (french: "zéro", spanish: "cero", japanese: "nuru")
case .One: return (french: "un", spanish: "uno", japanese: "ichi")
case .Two: return (french: "deux", spanish: "dos", japanese: "ni")
case .Three: return (french: "trois", spanish: "tres", japanese: "san")
case .Four: return (french: "quatre", spanish: "cuatro", japanese: "shi")
case .Five: return (french: "cinq", spanish: "cinco", japanese: "go")
case .Six: return (french: "six", spanish: "seis", japanese: "roku")
case .Seven: return (french: "sept", spanish: "siete", japanese: "shichi")
}
}

//Used to iterate enum or otherwise access enum case by index order.
//Iterate by looping until it returns nil
static func item(index: Int) -> IterateEnum? {
return IterateEnum.init(rawValue: index)
}

static func numberFromSpanish(number: String) -> IterateEnum? {
return findItem { $0.value.spanish == number }
}

//use block to test value property to retrieve the enum case
static func findItem(predicate: ((_: IterateEnum) -> Bool)) -> IterateEnum? {

var enumIndex: Int = -1
var enumCase: IterateEnum?

//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)

if let eCase = enumCase {

if predicate(eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}
}

var enumIndex: Int = -1
var enumCase: IterateEnum?

// Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)
if let eCase = enumCase {
print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
}
} while enumCase != nil

print("Total of \(enumIndex) cases")

let number = IterateEnum.numberFromSpanish(number: "siete")

print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")

This is the output:

The number Zero in french: zéro, spanish: cero, japanese: nuru<br>
The number One in french: un, spanish: uno, japanese: ichi<br>
The number Two in french: deux, spanish: dos, japanese: ni<br>
The number Three in french: trois, spanish: tres, japanese: san<br>
The number Four in french: quatre, spanish: cuatro, japanese: shi<br>
The number Five in french: cinq, spanish: cinco, japanese: go<br>
The number Six in french: six, spanish: seis, japanese: roku<br>
The number Seven in french: sept, spanish: siete, japanese: shichi<br>

Total of 8 cases

siete in japanese: shichi


----------
***UPDATE***

I recently created a protocol to handle the enumeration. The protocol requires an enum with an Int raw value:

protocol EnumIteration {

//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil

static func item(index:Int) -> Self?
static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
static func count() -> Int
}

extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {

//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
static func item(index:Int) -> Self? {
return Self.init(rawValue: index)
}

static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {

var enumIndex:Int = -1
var enumCase:Self?

//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)

if let eCase = enumCase {
item(index: enumIndex, enumCase: eCase)
}
} while enumCase != nil
completion?()
}

static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {

var enumIndex:Int = -1
var enumCase:Self?

//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)

if let eCase = enumCase {

if predicate(enumCase:eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}

static func count() -> Int {
var enumIndex:Int = -1
var enumCase:Self?

//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
} while enumCase != nil

//last enumIndex (when enumCase == nil) is equal to the enum count
return enumIndex
}
}
Reply

#33
I have used the below method, the assumption is that I know which is the last value in the `Rank` `enum` and all the ranks have incremental values after `Ace`

I prefer this way as it is clean and small, easy to understand

func cardDeck() -> Card[] {
var cards: Card[] = []
let minRank = Rank.Ace.toRaw()
let maxRank = Rank.King.toRaw()

for rank in minRank...maxRank {
if var convertedRank: Rank = Rank.fromRaw(rank) {
cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
cards.append(Card(rank: convertedRank, suite: Suite.Spades))
}
}

return cards
}

Reply

#34
My solution is to declare an array with all the `enum` possibilities. So `for` loop can traverse through all of them.

//Function inside struct Card
static func generateFullDeck() -> [Card] {
let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
var myFullDeck: [Card] = []

for myRank in allRanks {
for mySuit in allSuits {
myFullDeck.append(Card(rank: myRank, suit: mySuit))
}
}
return myFullDeck
}

//actual use:
let aFullDeck = Card.generateFullDeck() //Generate the desired full deck

var allDesc: [String] = []
for aCard in aFullDeck {
println(aCard.simpleDescription()) //You'll see all the results in playground
}
Reply

#35
It took me a little more than just one method in the struct like the swift book called for but I set up next functions in the `enum`. I would have used a protocol I'm not sure why but having rank set as int messes it up.

enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "Queen"
case .King:
return "King"
default:
return String(self.toRaw())
}
}
mutating func next() -> Rank {
var rank = self
var rawrank = rank.toRaw()
var nrank: Rank = self
rawrank = rawrank + 1
if let newRank = Rank.fromRaw(rawrank) {
println("\(newRank.simpleDescription())")
nrank = newRank
} else {
return self
}
return nrank
}
}

enum Suit {
case Spades, Hearts, Diamonds, Clubs
func color() -> String {
switch self {
case .Spades, .Clubs:
return "black"
default:
return "red"
}
}
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
mutating func next() -> Suit {
switch self {
case .Spades:
return Hearts
case .Hearts:
return Diamonds
case .Diamonds:
return Clubs
case .Clubs:
return Spades
}
}
}

struct Card {
var rank: Rank
var suit: Suit
func deck() -> Card[] {
var tRank = self.rank
var tSuit = self.suit
let tcards = 52 // we start from 0
var cards: Card[] = []
for i in 0..tcards {
var card = Card(rank: tRank, suit: tSuit)
cards.append(card)
tRank = tRank.next()
tSuit = tSuit.next()
}
return cards
}
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}

var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()

I used a little general knowledge but that can be easily rectified by multiplying suits by rank (if you aren't using a standard deck of cards and you'd have to change the enums accordingly and if basically just steps through the different enums. To save time I used ranks `rawValues` you could do the same for suits if you wanted. However, the example did not have it so I decided to figure it out without changing suits `rawValue`
Reply

#36
There is a clever way, and frustrating as it is it illustrates the difference between the two different kinds of enums.

Try this:

func makeDeck() -> Card[] {
var deck: Card[] = []
var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
for i in 1...13 {
for suit in suits {
deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
}
}
return deck
}


The deal is that an enum backed by numbers (raw values) is implicitly explicitly ordered, whereas an enum that isn't backed by numbers is explicitly implicitly unordered.

E.g. when we give the enum values numbers, the language is cunning enough to figure out what order the numbers are in.
If on the other hand we don't give it any ordering, when we try to iterate over the values the language throws its hands up in the air and goes *"yes, but which one do you want to go first???"*

Other languages which can do this (iterating over unordered enums) might be the same languages where everything is 'under the hood' actually a map or dictionary, and you can iterate over the keys of a map, whether there's any logical ordering or not.

So the trick is to provide it with something that is explicitly ordered, in this case instances of the suits in an array in the order we want. As soon as you give it that, Swift is like *"well why didn't you say so in the first place?"*

The other shorthand trick is to use the forcing operator on the fromRaw function. This illustrates another 'gotcha' about enums, that the range of possible values to pass in is often larger than the range of enums. For instance if we said Rank.fromRaw(60) there wouldn't be a value returned, so we're using the **optional** feature of the language, and where we start using optionals, forcing will soon follow. (Or alternately the *if let* construction which still seems a bit weird to me)
Reply

#37
Sorry, my answer was specific to how I used this post in what I needed to do. For those who stumble upon this question, looking for a way to *find* a case within an enum, this is the way to do it (new in Swift 2):

Edit: lowercase camelCase is now the standard for Swift 3 enum values

// From apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case.

enum Theme: String
{
case white, blue, green, lavender, grey
}

func loadTheme(theme: String)
{
// this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block
if let testTheme = Theme(rawValue: theme)
{
// testTheme is guaranteed to have an enum value at this point
self.someOtherFunction(testTheme)
}
}

For those wondering about the enumerating on an enum, the answers given on this page that include a static var/let containing an array of all enum values are correct. The latest Apple example code for tvOS contains this exact same technique.

That being said, they should build a more convenient mechanism into the language (Apple, are you listening?)!
Reply

#38
### Swift 4.2+

Starting with [Swift 4.2](

[To see links please register here]

) (with Xcode 10), just add protocol conformance to [`CaseIterable`](

[To see links please register here]

) to benefit from [`allCases`](

[To see links please register here]

). 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
Reply

#39
I made a utility function `iterateEnum()` for iterating cases for arbitrary `enum` types.

Here is the example usage:

enum Suit: String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}

for f in iterateEnum(Suit) {
println(f.rawValue)
}

Which outputs:






But, this is **only for debug or test** purposes: This relies on several undocumented Swift1.1 compiler behaviors, so, use it at your own risk.

Here is the code:

func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
var cast: (Int -> T)!
switch sizeof(T) {
case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
default: fatalError("cannot be here")
}

var i = 0
return GeneratorOf {
let next = cast(i)
return next.hashValue == i++ ? next : nil
}
}

The underlying idea is:

- Memory representation of `enum`, excluding `enum`s with associated types, is just an index of cases when the count of the cases is `2...256`, it's identical to `UInt8`, when `257...65536`, it's `UInt16` and so on. So, it can be `unsafeBitcast` from corresponding unsigned integer types.
- `.hashValue` of enum values is the same as the index of the case.
- `.hashValue` of enum values bitcasted from *invalid* index is `0`.


---

Revised for Swift2 and implemented casting ideas from [@Kametrixom's answer][1]:

func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return anyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
return next.hashValue == i++ ? next : nil
}
}

---

Revised for Swift3:

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(to: &i) {
$0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
}
if next.hashValue != i { return nil }
i += 1
return next
}
}

---

Revised for Swift3.0.1:

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
if next.hashValue != i { return nil }
i += 1
return next
}
}

[1]:

[To see links please register here]

Reply

#40
I stumbled around in the bits and bytes and created an extension that I later found out works very similar to [@rintaro][1]'s answer. It's used like this:

enum E : EnumCollection {
case A, B, C
}

Array(E.cases()) // [A, B, C]

What's remarkable is that it's usable on any enum without associated values. Note that this doesn't work for enums that have no cases.


As with [@rintaro][1]'s answer, this code uses the underlying representation of an enum. This representation isn't documented and might change in the future, which would break it. _I don't recommend the usage of this in production._

Code (Swift 2.2, Xcode 7.3.1, not working on Xcode 10):

protocol EnumCollection : Hashable {}
extension EnumCollection {
static func cases() -> AnySequence<Self> {
typealias S = Self
return AnySequence { () -> AnyGenerator<S> in
var raw = 0
return AnyGenerator {
let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
}
}

Code (Swift 3, Xcode 8.1, not working on Xcode 10):

protocol EnumCollection : Hashable {}
extension EnumCollection {
static func cases() -> AnySequence<Self> {
typealias S = Self
return AnySequence { () -> AnyIterator<S> in
var raw = 0
return AnyIterator {
let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
}
}

I have no idea why I need `typealias`, but the compiler complains without it.

[1]:

[To see links please register here]

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through