Bagaimana cara menghitung enum dengan tipe String?

530
enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

Misalnya, bagaimana saya bisa melakukan sesuatu seperti:

for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

Contoh yang dihasilkan:

♠
♥
♦
♣
Lucien
sumber
Dalam hal apa Anda tidak tahu jenisnya?
mtbennett
Anda benar, dalam hal ini tipe String.
Lucien
Belum ada refleksi di Swift ...
Sulthan
22
Bukankah ironis kalau mereka disebut enumerasi, tetapi mereka sangat menjengkelkan untuk disebutkan di Swift?
Charlton Provatas
3
@CharltonProvatas Jika itu satu-satunya kelemahan di Swift, saya akan menyebutnya sehari. Melihat berapa banyak orang yang menawarkan solusi yang berbeda untuk ini, saya hanya mengunyah keyboard saya.
qwerty_so

Jawaban:

267

Swift 4.2+

Dimulai dengan Swift 4.2 (dengan Xcode 10), tambahkan saja kesesuaian protokol CaseIterableuntuk mendapat manfaat allCases. Untuk menambahkan kesesuaian protokol ini, Anda hanya perlu menulis di suatu tempat:

extension Suit: CaseIterable {}

Jika enum adalah milik Anda, Anda dapat menentukan kesesuaian langsung dalam deklarasi:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Kemudian kode berikut akan mencetak semua nilai yang mungkin:

Suit.allCases.forEach {
    print($0.rawValue)
}

Kompatibilitas dengan versi Swift sebelumnya (3.x dan 4.x)

Jika Anda perlu mendukung Swift 3.x atau 4.0, Anda dapat meniru implementasi Swift 4.2 dengan menambahkan kode berikut:

#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
Cur
sumber
@DmitryPetukhov Saya akan dengan senang hati membantu, tetapi: (1) Anda yakin mendapat kode versi terbaru? (beberapa kerusakan diperbaiki sebulan yang lalu) dan (2) tolong beri MCVE tipe khusus Anda yang dapat mereproduksi kerusakan, dan versi Xcode Anda.
Cœur
Ini berfungsi baik bagi saya untuk membangun debug, tetapi segera setelah saya membuat rilis dan mengunggah ke TestFlight macet. Apakah Apple entah bagaimana menghapus ini?
Daniel Wood
1
Sepertinya versi Anda memiliki satu positif atas versi inbuilt CaseIterator. Saya dapat memperluas enum yang didefinisikan dalam file lain dengan versi Anda. Jika Anda membuat allCases di ekstensi publik, Anda juga dapat memperluas enum yang ditentukan dalam kerangka kerja yang berbeda.
adamfowlerphoto
1
@ CyberMew Saya telah memperbarui jawaban untuk memperjelasnya. Buku Swift dan catatan rilis Xcode 10 menyebutkan CaseIterable lebih rendah dari jawaban saya dan mereka menggunakan contoh-contoh sederhana di mana enum tidak didukung String, sebagai lawan dari pertanyaan Stack Overflow saat ini.
Cœur
1
Saya ingin menekankan pentingnya "# jika! Cepat (> = 4.2)". Jika Anda menulis kode Anda sebelum swift 4.2 dan Anda lupa menghapus kode di bawah "# if! Swift (> = 4.2)", ketika Anda menggunakan Xcode Versi 11.4 untuk membuat secara lokal di perangkat pengujian Anda, semuanya akan baik-baik saja. Tetapi ketika aplikasi Anda diunduh dari app store atau uji penerbangan, potongan kode itu akan merusak aplikasi Anda. Bug semacam itu sangat sulit dikenali atau didebug.
Oliver Zhang
524

Posting ini relevan di sini https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Pada dasarnya solusi yang diusulkan adalah

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

for category in ProductCategory.allValues{
     //Do something
}
rougeExciter
sumber
188
Bagus, tapi ... Anda harus memasukkan elemen enumerasi dua kali - satu kali untuk enumerasi, satu kali untuk semua Nilai. Tidak seanggun yang diharapkan.
Jay Imerman
2
Setuju dengan "tetapi" ... namun seperti yang dinyatakan dalam artikel mungkin ada masalah bahwa enum benar-benar sebuah set dan karenanya tidak tertata ... ingatlah Anda ... memesan kasus yang ditentukan tidak akan menjadi awal yang buruk!
rougeExciter
3
Di Jawa kompiler melakukan ini untuk Anda, mungkin Swift 2.0 akan melakukan ini juga. Khususnya di Jawa, semua enum mendapatkan deskripsi (toString di Jawa) metode yang memberikan String sebagai nama case (Cincin, ...) dan satu Set case secara otomatis dibuat. Java juga memberi Anda indeks posisi. Seperti yang saya katakan, mungkin Swift 2.0.
Howard Lovatt
1
Idealnya Anda akan memiliki sesuatu yang mirip dengan implementasi c # di mana Anda dapat melakukannya Enum.Values(typeof(FooEnum))tetapi diekspos sebagai metode ekstensi (seperti peta atau kurangi). FooEnum.values() :: values(EnumType -> [EnumType])
rodrigoelp
1
Artikel ini membuat poin yang baik di akhir tentang membuat setiap nilai enum dalam array allValues ​​dengan tes unit. Namun, saya masih bisa melihat seseorang menambahkan lebih banyak elemen, tetapi tidak memaksakannya dalam unit test yang masih meninggalkan kita di awal, tidak tahu pasti bahwa semua nilai enum disimpan di semua Nilai.
DonnaLea
278

Saya membuat fungsi utilitas iterateEnum()untuk iterasi kasus untuk enumtipe yang sewenang-wenang .

Berikut adalah contoh penggunaannya:

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

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

Output yang mana:

♠
♥
♦
♣

Tapi, ini hanya untuk keperluan debug atau tes : Ini bergantung pada beberapa perilaku kompiler Swift1.1 yang tidak berdokumen, jadi, gunakan dengan risiko Anda sendiri.

Ini kodenya:

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

Ide dasarnya adalah:

  • Representasi memori enum, tidak termasuk enums dengan tipe terkait, hanya indeks kasus ketika jumlah kasus 2...256, identik dengan UInt8, kapan 257...65536, itu UInt16dan seterusnya. Jadi, itu bisa unsafeBitcastdari tipe integer yang tidak ditandatangani.
  • .hashValue nilai enum sama dengan indeks kasus ini.
  • .hashValuedari enum nilai bitcasted dari tidak valid indeks 0.

Direvisi untuk Swift2 dan menerapkan gagasan casting dari jawaban @ Kametrixom :

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

Direvisi untuk 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
    }
}

Direvisi untuk 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
    }
}
rintaro
sumber
20
Luar biasa dan satu-satunya jawaban yang menjawab pertanyaan! Tapi Ya ... tidak akan menyentuhnya! +1 untuk upaya!
dotToString
Saya baru saja memposting jawaban saya yang pada dasarnya berfungsi dengan cara yang sama (hanya melihat jawaban ini nanti). Ini menggunakan Swift 2.0 beta 6 dan fitur modern bahasa.
Kametrixom
2
Versi Swift 3 bekerja dengan baik. Hanya perlu memodifikasi penggunaan sedikit: untuk f di iterateEnum (Suit.self) {print (f.rawValue)}
Gene De Lisa
16
+1 ini cukup brilian. Ini juga, IMHO, terlalu pintar untuk digunakan, sebagaimana dibuktikan oleh itu melanggar secara signifikan dalam setiap perubahan versi Swift utama. Untuk penghargaan penulis, versi Swift 3 dilakukan sebulan sebelum Swift 3 keluar dari beta ... Jika Anda akan mengambil jawaban ini dan mempelajari semua ini withUnsafePointer withMemoryRebounddan pointeehal - hal, maka gunakan ini dengan segala cara. Kalau tidak, aku akan menghindarinya.
Dan Rosenstark
5
Saya hanya ingin menambahkan ini sekarang rusak di swift 4, tetapi hanya di linux, jadi beri +1 pada komentar di atas bahwa ini terlalu pintar untuk digunakan.
Andrew Plummer
130

Solusi lain bekerja tetapi mereka semua membuat asumsi misalnya jumlah peringkat dan pakaian yang mungkin, atau apa peringkat pertama dan terakhir. Benar, tata letak setumpuk kartu mungkin tidak akan banyak berubah di masa mendatang. Secara umum, bagaimanapun, lebih rapi untuk menulis kode yang membuat asumsi sesedikit mungkin. Solusi saya:

Saya telah menambahkan jenis mentah ke Suitenum, jadi saya bisa gunakan Suit(rawValue:)untuk mengakses Suitkasing:

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

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

Di bawah penerapan createDeck()metode Kartu . init(rawValue:)adalah inisialisasi yang tersedia dan mengembalikan opsional. Dengan membuka dan memeriksa nilainya dalam kedua pernyataan sementara, tidak perlu mengasumsikan jumlah Rankatau Suitkasus:

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
    func createDeck() -> [Card] {
        var n = 1
        var deck = [Card]()
        while let rank = Rank(rawValue: n) {
            var m = 1
            while let suit = Suit(rawValue: m) {
                deck.append(Card(rank: rank, suit: suit))
                m += 1
            }
            n += 1
        }
        return deck
    }
}

Inilah cara memanggil createDeckmetode:

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
sdduursma
sumber
12
Jawaban TERBAIK mutlak yang saya lihat di berbagai utas tentang topik ini. Sangat elegan. Ini bekerja dengan enumerasi tipe Int, tapi saya bertanya-tanya bagaimana orang bisa beralih melalui tipe lain (string, tipe kustom, dll.).
Jay Imerman
3
Ini jelas merupakan solusi terbaik. Satu hal yang perlu diperhatikan. Dalam contoh di buku ini tidak ada "case Spades = 1" seperti yang dimiliki sdduursma. Saya tidak menangkap ini pada awalnya. itu adalah salah satu opsi, atau Anda dapat menggunakan "var m = 0"
Joshua Goossen
10
Ini membuat asumsi bahwa nilai mentah berurutan. Ketika ini tidak benar, misalnya ketika enum mewakili bendera bit mask, loop keluar sebelum waktunya.
Jim
1
Solusi ini mengasumsikan Anda dapat mengubah definisi Suit. Anda bisa dalam contoh ini, tetapi latihan itu dimaksudkan untuk membuat Anda bekerja dengan enumsAnda diberikan seolah-olah mereka berasal dari sumber eksternal.
Josh J
1
Satu-satunya hal yang ingin saya keluhkan adalah bahwa saya tidak dapat menyebutnya sebagai metode statis, tetapi harus membuat objek kartu terlebih dahulu.
qed
76

Saya tersandung dalam bit dan byte dan menciptakan ekstensi yang kemudian saya temukan bekerja sangat mirip dengan jawaban @rintaro . Ini digunakan seperti ini:

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

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

Yang luar biasa adalah bahwa ini dapat digunakan pada enum apa pun tanpa nilai terkait. Perhatikan bahwa ini tidak berfungsi untuk enum yang tidak memiliki kasing.

Seperti halnya jawaban @rintaro , kode ini menggunakan representasi enum yang mendasarinya. Representasi ini tidak didokumentasikan dan mungkin berubah di masa depan, yang akan mematahkannya. Saya tidak merekomendasikan penggunaan ini dalam produksi.

Kode (Swift 2.2, Xcode 7.3.1, tidak bekerja pada 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
            }
        }
    }
}

Kode (Swift 3, Xcode 8.1, tidak bekerja pada 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
            }
        }
    }
}

Saya tidak tahu mengapa saya perlu typealias, tetapi kompiler mengeluh tanpa itu.

Kametrixom
sumber
10
Jawaban ini bahkan lebih baik daripada jawaban saya, terutama di bagian casting :)
rintaro
Tapi saya pikir ini hanya berfungsi pada lingkungan endian kecil?
rintaro
5
Xcode 8 beta 6 telah mengubahnya lagi! Saya mendapatkan kesalahan berikut `'init' tidak tersedia: gunakan 'withMemoryRebound (ke: kapasitas: _)' untuk sementara melihat memori sebagai tipe yang kompatibel dengan tata letak lain.`
Confused Vorlon
1
@ConfusedVorlon: lihat jawaban di atas oleh @Rintaro: replace withUnsafePointerpointee}bywithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Stefan
1
Ini sepertinya tidak lagi berfungsi pada Xcode 10. (saya tahu ini tidak akan diperlukan dengan Swift 4.2) tetapi ketika menggunakan Swift 4 (.1) di Xcode 10 kode ini tidak lagi berfungsi (nilai mentah tidak sama)
benrudhart
26

Anda bisa melakukan iterasi melalui enum dengan mengimplementasikan ForwardIndexTypeprotokol.

The ForwardIndexTypeprotokol mengharuskan Anda untuk menentukan successor()fungsi untuk langkah melalui elemen.

enum Rank: Int, ForwardIndexType {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King

    // ... other functions

    // Option 1 - Figure it out by hand
    func successor() -> Rank {
        switch self {
            case .Ace:
              return .Two
            case .Two:
              return .Three

            // ... etc.

            default:
              return .King
        }
    }

    // Option 2 - Define an operator!
    func successor() -> Rank {
        return self + 1
    }
}

// NOTE: The operator is defined OUTSIDE the class
func + (left: Rank, right: Int) -> Rank {
    // I'm using to/from raw here, but again, you can use a case statement
    // or whatever else you can think of

    return left == .King ? .King : Rank(rawValue: left.rawValue + right)!
}

Iterasi melalui rentang terbuka atau tertutup ( ..<atau ...) akan memanggil successor()fungsi yang memungkinkan Anda untuk menulis ini:

// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
for r in Rank.Ace...Rank.King {
    // Do something useful
}
RndmTsk
sumber
2
Saya menemukan ini sebagai jawaban yang paling "tepat" untuk pertanyaan, bahkan yang paling "elegan" (kode tambahan yang diperlukan berhadapan dengan opsi-opsi lain di sini yang bertahan) mengingat sintaks yang dihasilkan ketika digunakan dalam suatu rentang (sintaksis yang saya gunakan). akan berharap dapat dilakukan jika enum di mana enumeratable sans workarounds). Terima kasih! Meskipun perlu dicatat bahwa jika operator kelebihan beban digunakan di tempat lain selain penggantinya () (yang tampaknya menggoda) maka jelas membuka paksa paksa berbahaya. Juga, infiks sepertinya tidak perlu ...?
iOS Gamer
Jawaban yang diperbarui untuk mencerminkan spesifikasi bahasa Swift terbaru
RndmTsk
successor()Metode yang didefinisikan dengan benar (opsi pertama) akan menghilangkan kebutuhan untuk enummemiliki jenis terkait. +1
nhgrif
1
Tetapi jawaban yang elegan ini tidak akan bekerja untuk String enum kan?
Ali
Solusi praktik yang paling "tepat" / terbaik! + 1-red
Siu Ching Pong -Asuka Kenji-
18

Masalah ini sekarang jauh lebih mudah. Inilah Solusi Swift 4.2 saya:

enum Suit: Int, CaseIterable {
  case None
  case Spade, Heart, Diamond, Club

  static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}

enum Rank: Int, CaseIterable {
  case Joker
  case Two, Three, Four, Five, Six, Seven, Eight
  case Nine, Ten, Jack, Queen, King, Ace

  static let allNonNullCases = Rank.allCases[Two.rawValue...]
}

func makeDeck(withJoker: Bool = false) -> [Card] {
  var deck = [Card]()
  for suit in Suit.allNonNullCases {
    for rank in Rank.allNonNullCases {
      deck.append(Card(suit: suit, rank: rank))
    }
  }
  if withJoker {
    deck.append(Card(suit: .None, rank: .Joker))
  }
  return deck
}

Pre 4.2:

Saya suka solusi ini yang saya kumpulkan setelah menemukan " Daftar pemahaman dalam Swift ".

Ia menggunakan Int raw bukan Strings tetapi ia menghindari mengetik dua kali, itu memungkinkan menyesuaikan rentang, dan tidak menyulitkan kode nilai mentah.

Ini adalah versi Swift 4 dari solusi asli saya tetapi lihat peningkatan 4.2 di atas:

enum Suit: Int {
  case None
  case Spade, Heart, Diamond, Club

  static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue
  static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! })
}
enum Rank: Int {
  case Joker
  case Two, Three, Four, Five, Six
  case Seven, Eight, Nine, Ten
  case Jack, Queen, King, Ace

  static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue
  static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! })
}
func makeDeck(withJoker: Bool = false) -> [Card] {
  var deck = [Card]()
  for suit in Suit.allCases {
    for rank in Rank.allCases {
      deck.append(Card(suit: suit, rank: rank))
    }
  }
  if withJoker {
    deck.append(Card(suit: .None, rank: .Joker))
  }
  return deck
}
adazacom
sumber
Oh, saya mengerti bahwa milik saya pada dasarnya sama dengan milik Sutean Rutjanalard.
adazacom
1
Sebenarnya, saya lebih menyukai implementasi Anda. Saya pikir ini lebih jelas! 1 suara positif. Sebenarnya jawaban yang paling banyak dipilih terlalu pintar dan pasti akan pecah di masa depan. Milik Anda menjanjikan stabilitas di masa depan.
jvarela
17

Pada prinsipnya adalah mungkin untuk melakukannya dengan asumsi bahwa Anda tidak menggunakan penetapan nilai mentah untuk kasus enum:

enum RankEnum: Int {
  case Ace
  case One
  case Two
}

class RankEnumGenerator: Generator {
    var i = 0
    typealias Element = RankEnum
    func next() -> Element? {
        let r = RankEnum.fromRaw(i)
        i += 1
        return r
    }
}

extension RankEnum {
    static func enumerate() -> SequenceOf<RankEnum> {
        return SequenceOf<RankEnum>({ RankEnumGenerator() })
    }
}

for r in RankEnum.enumerate() {
    println("\(r.toRaw())")
}
Alfa07
sumber
7
Ini bagus tetapi ini hanya berfungsi untuk bilangan bulat Integer yang berkesinambungan mulai dari 0
Robert
@ Robert, seperti komentar saya nyatakan di atas: "Anda tidak menggunakan penetapan nilai mentah untuk kasus enum"
Alfa07
Ya - jangan gunakan nilai mentah, serta mengatur tipe yang mendasarinya menjadi int. Dalam cepat Anda tidak perlu jenis untuk enum seperti pada contoh jas. enum ItWontWorkForThisEnum {case a, b, c}
Robert
Bagaimana ini mengatasi masalah jika tuple dikaitkan dengan kasus enumerasi?
rougeExciter
Anda tidak dapat mengaitkan tuple ke enum dengan sangat mudah.
nhgrif
13

Jika Anda memberikan enum nilai Int mentah itu akan membuat perulangan jauh lebih mudah.

Misalnya, Anda dapat menggunakan anyGeneratoruntuk mendapatkan generator yang dapat menyebutkan nilai Anda:

enum Suit: Int, CustomStringConvertible {
    case Spades, Hearts, Diamonds, Clubs
    var description: String {
        switch self {
        case .Spades:   return "Spades"
        case .Hearts:   return "Hearts"
        case .Diamonds: return "Diamonds"
        case .Clubs:    return "Clubs"
        }
    }
    static func enumerate() -> AnyGenerator<Suit> {
        var nextIndex = Spades.rawValue
        return anyGenerator { Suit(rawValue: nextIndex++) }
    }
}
// You can now use it like this:
for suit in Suit.enumerate() {
    suit.description
}
// or like this:
let allSuits: [Suit] = Array(Suit.enumerate())

Namun, ini terlihat seperti pola yang cukup umum, bukankah lebih baik jika kita dapat membuat enum type enumerable dengan hanya menyesuaikan dengan protokol? Baik dengan Swift 2.0 dan ekstensi protokol, sekarang kita bisa!

Cukup tambahkan ini ke proyek Anda:

protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstValue() -> Int
}
extension EnumerableEnum {
    static func enumerate() -> AnyGenerator<Self> {
        var nextIndex = firstRawValue()
        return anyGenerator { Self(rawValue: nextIndex++) }
    }
    static func firstRawValue() -> Int { return 0 }
}

Sekarang, setiap kali Anda membuat enum (asalkan memiliki nilai mentah Int), Anda dapat membuatnya enumerable dengan mematuhi protokol:

enum Rank: Int, EnumerableEnum {
    case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }

Jika nilai enum Anda tidak dimulai dengan 0(default), ganti firstRawValuemetode:

enum DeckColor: Int, EnumerableEnum {
    case Red = 10, Blue, Black
    static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())

Kelas Suit akhir, termasuk mengganti simpleDescriptiondengan protokol CustomStringConvertible yang lebih standar , akan terlihat seperti ini:

enum Suit: Int, CustomStringConvertible, EnumerableEnum {
    case Spades, Hearts, Diamonds, Clubs
    var description: String {
        switch self {
        case .Spades:   return "Spades"
        case .Hearts:   return "Hearts"
        case .Diamonds: return "Diamonds"
        case .Clubs:    return "Clubs"
        }
    }
}
// ...
for suit in Suit.enumerate() {
    print(suit.description)
}

Sintaks Swift 3:

protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstRawValue() -> Int
}

extension EnumerableEnum {
    static func enumerate() -> AnyIterator<Self> {
        var nextIndex = firstRawValue()

        let iterator: AnyIterator<Self> = AnyIterator {
            defer { nextIndex = nextIndex + 1 }
            return Self(rawValue: nextIndex)
        }

        return iterator
    }

    static func firstRawValue() -> Int {
        return 0
    }
}
Masuk akal
sumber
nextIndex ++ akan dihapus dengan cepat 3. Apa yang Anda sarankan sebagai pengganti - var nextIndex = firstRawValue () mengembalikan anyGenerator {Self (rawValue: nextIndex ++)}
Avi
Menemukannya. defer {nextIndex + = 1} mengembalikan AnyGenerator {Self (rawValue: nextIndex)}
Avi
12

Diperbarui ke Swift 2.2 +

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

Kode ini diperbarui ke formulir Swift 2.2 @ Kametrixom's answer

Untuk Swift 3.0+ (terima kasih banyak ke @Philip )

func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).pointee
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}
ale_stro
sumber
@silvansky bisakah Anda menjelaskan apa maksud Anda?
ale_stro
Ups, maaf, saya memeriksa ulang dan ada bug taman bermain: dalam proyek nyata kode ini berfungsi seperti yang diharapkan, terima kasih! =)
silvansky
1
Solusi hebat! Juga, berfungsi dengan baik di Swift 3 dengan perubahan minimal ('AnyGenerator' berganti nama menjadi 'AnyIterator' dan '.memory' diubah namanya menjadi '.pointee').
Philip
9

Solusi Swift 5:

enum Suit: String, CaseIterable {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

// access cases like this:

for suitKey in Suit.allCases {
    print(suitKey)
}
JD Wooder
sumber
7

Saya menemukan diri saya melakukan .allValuesbanyak hal di seluruh kode saya. Saya akhirnya menemukan cara untuk sekadar menyesuaikan diri dengan Iteratableprotokol dan memiliki rawValues()metode.

protocol Iteratable {}
extension RawRepresentable where Self: RawRepresentable {

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

extension Iteratable where Self: RawRepresentable, Self: Hashable {
    static func hashValues() -> AnyIterator<Self> {
        return iterateEnum(self)
    }

    static func rawValues() -> [Self.RawValue] {
        return hashValues().map({$0.rawValue})
    }
}


// Example
enum Grocery: String, Iteratable {
    case Kroger = "kroger"
    case HEB = "h.e.b."
    case Randalls = "randalls"
}

let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
Fabian Buentello
sumber
7

Xcode 10 dengan Swift 4.2

enum Filter: String, CaseIterable {

    case salary = "Salary"
    case experience = "Experience"
    case technology = "Technology"
    case unutilized = "Unutilized"
    case unutilizedHV = "Unutilized High Value"

    static let allValues = Filter.allCases.map { $0.rawValue }
}

Sebut saja

print(Filter.allValues)

Cetakan:

["Gaji", "Pengalaman", "Teknologi", "Tidak Digunakan", "Nilai Tinggi Tidak Digunakan"]


Versi yang lebih lama

Untuk enummewakiliInt

enum Filter: Int {
    case salary
    case experience
    case technology
    case unutilized
    case unutilizedHV

    static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
    static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue }
}

Sebut saja seperti ini:

print(Filter.allValues)

Cetakan:

[0, 1, 2, 3, 4]


Untuk enummewakiliString

enum Filter: Int {
    case salary
    case experience
    case technology
    case unutilized
    case unutilizedHV

    static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
    static let allValues = allRawValues.map { Filter(rawValue: $0)!.description }
}

extension Filter: CustomStringConvertible {
    var description: String {
        switch self {
        case .salary: return "Salary"
        case .experience: return "Experience"
        case .technology: return "Technology"
        case .unutilized: return "Unutilized"
        case .unutilizedHV: return "Unutilized High Value"
        }
    }
}

Sebut saja

print(Filter.allValues)

Cetakan:

["Gaji", "Pengalaman", "Teknologi", "Tidak Digunakan", "Nilai Tinggi Tidak Digunakan"]

Warif Akhand Rishi
sumber
7

EDIT: Swift Evolution Proposal SE-0194 Berasal Koleksi Kasus Enum mengusulkan tingkat menuju solusi untuk masalah ini. Kami melihatnya di Swift 4.2 dan yang lebih baru. Proposal juga menunjukkan beberapa solusi yang mirip dengan beberapa yang telah disebutkan di sini, tetapi mungkin menarik untuk dilihat.

Saya juga akan menyimpan posting asli saya demi kelengkapan.


Ini adalah pendekatan lain berdasarkan jawaban @ Peymmankh, disesuaikan dengan Swift 3 .

public protocol EnumCollection: Hashable {}

extension EnumCollection {

public static func allValues() -> [Self] {
    typealias S = Self

    let retVal = AnySequence { () -> AnyIterator<S> in
        var raw = 0
        return AnyIterator {
            let current = withUnsafePointer(to: &raw) {
                 $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
            }
            guard current.hashValue == raw else { return nil }
            raw += 1
            return current
        }
    }

    return [S](retVal)
}
ff10
sumber
5
enum Rank: Int {
    ...
    static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }

}
enum Suit {
    ...
    static let suits = [Spades, Hearts, Diamonds, Clubs]
}

struct Card {
    ...
    static func fullDesk() -> [Card] {
        var desk: [Card] = []
        for suit in Suit.suits {
            for rank in Rank.ranks {
                desk.append(Card(rank: rank,suit: suit))
            }
        }
        return desk
    }
}

Bagaimana dengan ini?

Mossila
sumber
Terima kasih, ini berfungsi sesuai kebutuhan saya. Tetapi apakah ada peluang dalam penutupan peta untuk mendapatkan nilai bukan dengan indeks tetapi dengan nama?
Mikhail
4

Anda dapat mencoba menghitung seperti ini

enum Planet: String {
    case Mercury
    case Venus
    case Earth
    case Mars

    static var enumerate: [Planet] {
        var a: [Planet] = []
        switch Planet.Mercury {
            case .Mercury: a.append(.Mercury); fallthrough
            case .Venus: a.append(.Venus); fallthrough
            case .Earth: a.append(.Earth); fallthrough
            case .Mars: a.append(.Mars)
        }
    return a
    }
}

Planet.enumerate // [Mercury, Venus, Earth, Mars]
Karthik Kumar
sumber
1
Itu banyak kode yang tidak berguna! Ini setara dengan static var enumerate = [Mercury, Venus, Earth, Mars], menjadikannya jawaban di bawah standar dibandingkan dengan jawaban terbanyak yang dipilih stackoverflow.com/a/24137319/1033581
Cœur
@Cœur jawaban ini memiliki manfaat penting menggunakan kompiler untuk menjamin Anda tidak akan melewatkan kasus.
dchakarov
@ Cœur yang memiliki masalah yang sama yang memungkinkan Anda membuat kesalahan pengguna, yaitu kompiler tidak akan mengeluh jika Anda menulis return [Mercury, Venus, Mars]alih-alihreturn [Mercury, Venus, Earth, Mars]
dchakarov
@dchakarov Saya memutuskan untuk mengirim perbaikan sebagai jawaban, untuk kejelasan: stackoverflow.com/a/50409525/1033581
Cœur
@ Cœur Jika dalam jawaban baru Anda, Anda mengganti pernyataan kembali dengan ini return [.spades, .hearts, .clubs]kompiler tidak akan mengatakan apa-apa dan kemudian ketika Anda mencoba menggunakannya dalam kode, Anda akan mendapatkan [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- itu maksud saya - bahwa jika Anda berurusan dengan besar enum dan Anda harus menambah atau menghapus kasus dari waktu ke waktu, solusi Anda rentan terhadap kesalahan kelalaian sementara jawaban saat ini, meskipun tidak singkat, lebih aman.
dchakarov
4

Di Swift 3, ketika enum yang mendasarinya rawValue, Anda bisa mengimplementasikan Strideableprotokol. Keuntungannya adalah tidak ada array nilai yang dibuat seperti pada beberapa saran lain dan bahwa Swift standar "for in" bekerja, yang membuat sintaks yang bagus.

// "Int" to get rawValue, and Strideable so we can iterate
enum MyColorEnum: Int, Strideable {
    case Red
    case Green
    case Blue
    case Black

    // required by Strideable
    typealias Stride = Int

    func advanced(by n:Stride) -> MyColorEnum {
        var next = self.rawValue + n
        if next > MyColorEnum.Black.rawValue {
            next = MyColorEnum.Black.rawValue
        }
        return MyColorEnum(rawValue: next)!
    }

    func distance(to other: MyColorEnum) -> Int {
        return other.rawValue - self.rawValue
    }

    // just for printing
    func simpleDescription() -> String {
        switch self {
        case .Red: return "Red"
        case .Green: return "Green"
        case .Blue: return "Blue"
        case .Black: return "Black"
        }
    }
}

// this is how you use it:
for i in MyColorEnum.Red ... MyColorEnum.Black {
    print("ENUM: \(i)")
}
ACK
sumber
Ahh, hanya apa yang saya cari untuk menggantikan ForwardIndexType. Sekarang iterasi saya terlihat bagus di situs penggunaan ... hanya cara Swifty yang tepat.
Andrew Duncan
4

Solusi ini memberikan keseimbangan yang tepat antara keterbacaan dan pemeliharaan.

struct Card {

    // ...

    static func deck() -> Card[] {
        var deck = Card[]()
        for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
            for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
                let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
                deck.append(card)
            }
        }
    return deck
    }
}

let deck = Card.deck()
Andrew
sumber
Menurut saya, ini adalah solusi terbaik. Ketika saya melihat kode cepat, kebanyakan, keterbacaan tidak lebih baik daripada objc. Tapi bisa jadi, jika programmer lebih memperhatikan pembaca kode mereka. Masa depan mereka, misalnya :)
Vilém Kurz
4

Maaf, jawaban saya khusus untuk bagaimana saya menggunakan posting ini dalam apa yang harus saya lakukan. Bagi mereka yang menemukan pertanyaan ini, mencari cara untuk menemukan kasus dalam enum, inilah cara untuk melakukannya (baru dalam Swift 2):

Sunting: camelCase huruf kecil sekarang menjadi standar untuk nilai Swift 3 enum

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

Bagi mereka yang bertanya-tanya tentang enumerasi pada enum, jawaban yang diberikan pada halaman ini yang menyertakan var statis / biarkan berisi array semua nilai enum benar. Contoh kode Apple terbaru untuk tvOS berisi teknik yang sama persis ini.

Yang sedang berkata, mereka harus membangun mekanisme yang lebih nyaman ke dalam bahasa (Apple, apakah Anda mendengarkan?)!

Gene Loparco
sumber
3

Eksperimennya adalah: EKSPERIMEN

Tambahkan metode ke Kartu yang membuat setumpuk penuh kartu, dengan satu kartu dari setiap kombinasi peringkat dan kartu.

Jadi tanpa memodifikasi atau meningkatkan kode yang diberikan selain menambahkan metode (dan tanpa menggunakan hal-hal yang belum diajarkan), saya datang dengan solusi ini:

struct Card {
    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }

    func createDeck() -> [Card] {
        var deck: [Card] = []
        for rank in Rank.Ace.rawValue...Rank.King.rawValue {
            for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue {
                let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!)
                //println(card.simpleDescription())
                deck += [card]
            }
        }
        return deck
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
let deck = threeOfSpades.createDeck()
Hans-Peter
sumber
3

Berikut adalah metode yang saya gunakan untuk kedua iterate enumdan memberikan beberapa tipe nilai dari satuenum

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

Ini hasilnya:

Angka Nol dalam bahasa Prancis: zéro, Spanyol: cero, Jepang: nuru
Nomor satu dalam bahasa Prancis: un, bahasa Spanyol: uno, Jepang: ichi
Nomor dua dalam bahasa Prancis: deux, spanyol: dos, Jepang: ni
Nomor Tiga dalam bahasa perancis : trois, spanyol: tres, Jepang: san
Angka Empat dalam bahasa Prancis: quatre, Spanyol: cuatro, Jepang: shi
Angka lima dalam bahasa Prancis: cinq, bahasa Spanyol: cinco, jepang: go
Nomor Enam dalam bahasa Prancis: enam, spanyol: seis, Jepang: roku
Nomor Tujuh dalam bahasa Prancis: sept, spanyol: siete, Jepang: shichi

Total 8 kasus

siete dalam bahasa Jepang: shichi


MEMPERBARUI

Saya baru-baru ini membuat protokol untuk menangani enumerasi. Protokol membutuhkan enum dengan nilai mentah Int:

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
    }
}
MSimic
sumber
2

Sepertinya ini peretasan tetapi jika Anda menggunakan nilai mentah, Anda dapat melakukan sesuatu seperti ini

enum Suit: Int {  
    case Spades = 0, Hearts, Diamonds, Clubs  
 ...  
}  

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  
Ken
sumber
2

Saat berurusan dengan di Swift 2.0sini adalah saran saya:

Saya telah menambahkan jenis mentah ke Suit enum

enum Suit: Int {

kemudian:

struct Card {
    var rank: Rank
    var suit: Suit


    func fullDeck()-> [Card] {

        var deck = [Card]()

        for i in Rank.Ace.rawValue...Rank.King.rawValue {

            for j in Suit.Spades.rawValue...Suit.Clubs.rawValue {

                deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!))
            }
        }

        return deck
    }
}
Aladin
sumber
2

Seperti halnya jawaban @Kametrixom di sini saya percaya mengembalikan array akan lebih baik daripada mengembalikan AnySequence, karena Anda dapat memiliki akses ke semua barang Array seperti hitungan, dll.

Inilah re-write:

public protocol EnumCollection : Hashable {}
extension EnumCollection {
    public static func allValues() -> [Self] {
        typealias S = Self
        let retVal = 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
            }
        }

        return [S](retVal)
    }
}
Peymankh
sumber
2

Solusi lain:

enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"

    static var count: Int {
        return 4   
    }

    init(index: Int) {
        switch index {
            case 0: self = .spades
            case 1: self = .hearts
            case 2: self = .diamonds
            default: self = .clubs
        }
    }
}

for i in 0..<Suit.count {
    print(Suit(index: i).rawValue)
}
Miguel Gallego
sumber
2

Ini adalah pos yang cukup lama, dari Swift 2.0. Sekarang ada beberapa solusi yang lebih baik di sini yang menggunakan fitur-fitur baru swift 3.0: Iterating melalui Enum di Swift 3.0

Dan pada pertanyaan ini ada solusi yang menggunakan fitur baru (yang belum dirilis saat saya menulis suntingan ini) Swift 4.2: Bagaimana cara saya mendapatkan hitungan enum Swift?


Ada banyak solusi bagus di utas ini dan yang lainnya namun beberapa di antaranya sangat rumit. Saya suka menyederhanakan sebanyak mungkin. Berikut ini adalah solusi yang mungkin atau mungkin tidak berfungsi untuk kebutuhan yang berbeda tetapi saya pikir ini berfungsi dengan baik dalam kebanyakan kasus:

enum Number: String {
    case One
    case Two
    case Three
    case Four
    case EndIndex

    func nextCase () -> Number
    {
        switch self {
        case .One:
            return .Two
        case .Two:
            return .Three
        case .Three:
            return .Four
        case .Four:
            return .EndIndex

        /* 
        Add all additional cases above
        */
        case .EndIndex:
            return .EndIndex
        }
    }

    static var allValues: [String] {
        var array: [String] = Array()
        var number = Number.One

        while number != Number.EndIndex {
            array.append(number.rawValue)
            number = number.nextCase()
        }
        return array
    }
}

Untuk beralih:

for item in Number.allValues {
    print("number is: \(item)")
}
Abbey Jackson
sumber
1
Itu terasa seperti banyak pekerjaan yang khusus untuk enum individu yang Anda buat - Saya tidak yakin mengembalikan [Number.One.rawValue, Number.Two.rawValue, ...] tidak bersih, dalam hal ini .
Scott Austin
Ini adalah pos yang cukup lama, dari Swift 2.0. Sekarang ada beberapa solusi yang lebih baik di sini yang menggunakan fitur swift 3.0 yang lebih baru: stackoverflow.com/questions/41352594/... Dan pada pertanyaan ini ada solusi yang menggunakan fitur baru (yang belum dirilis saat saya menulis suntingan ini). ) Swift 4.2: stackoverflow.com/questions/27094878/…
Abbey Jackson
2

Enum punya toRaw()dan fromRaw()metode. Jadi, jika nilai mentah Anda adalah Int, Anda dapat beralih dari yang pertama ke yang terakhir enum:

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}

for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
    if let covertedSuit = Suit.fromRaw(i) {
        let description = covertedSuit.simpleDescription()
    }
}

Satu gotcha adalah bahwa Anda perlu menguji nilai-nilai opsional sebelum menjalankan simpleDescriptionmetode, jadi kami menetapkan convertedSuitnilai kami terlebih dahulu dan kemudian menetapkan konstanta untukconvertedSuit.simpleDescription()

Adrian Harris Crowne
sumber
2
Pertanyaan aslinya adalah tentang tipe String enum bukan Int
cynistersix
2

Inilah pendekatan yang saya sarankan. Ini tidak sepenuhnya memuaskan (saya sangat baru di Swift dan OOP!) Tapi mungkin seseorang dapat memperbaikinya. Idenya adalah meminta masing-masing enum menyediakan informasi .firstdan .lastproperti jangkauannya sendiri . Ia menambahkan hanya dua baris kode untuk masing-masing enum: masih agak sulit dikodekan, tetapi setidaknya itu tidak menduplikasi seluruh rangkaian. Itu memang mengharuskan memodifikasi Suitenum menjadi Int seperti Rankenum, bukan untyped.

Daripada mengulangi seluruh solusi, inilah kode yang saya tambahkan ke .enum, di suatu tempat setelah pernyataan kasus ( Suitenum serupa):

var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

dan loop yang saya gunakan untuk membangun dek sebagai array dari String. (Definisi masalah tidak menyatakan bagaimana deck akan disusun.)

func createDeck() -> [String] {
    var deck: [String] = []
    var card: String
    for r in Rank.Ace.first...Rank.Ace.last {
        for s in Suit.Hearts.first...Suit.Hearts.last {
            card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)()
           deck.append( card)
       }
    }
    return deck
}

Ini tidak memuaskan karena sifat-sifatnya terkait dengan suatu elemen daripada enum. Tapi itu menambah kejelasan pada loop 'untuk'. Saya ingin mengatakannya Rank.firstsebagai ganti Rank.Ace.first. Ini bekerja (dengan elemen apa pun), tapi jelek. Adakah yang bisa menunjukkan cara menaikkan itu ke level enum?

Dan untuk membuatnya bekerja, saya mengangkat createDeckmetode keluar dari struct Kartu. Saya tidak tahu bagaimana cara mendapatkan array [String] kembali dari struct itu, dan itu tampaknya tempat yang buruk untuk meletakkan metode seperti itu pula.

Roger Worden
sumber
2

Saya melakukannya dengan menggunakan properti yang dihitung, yang mengembalikan array dari semua nilai (berkat posting ini http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ ). Namun, ini juga menggunakan nilai mentah int, tapi saya tidak perlu mengulangi semua anggota enumerasi di properti terpisah.

PEMBARUAN Xcode 6.1 sedikit mengubah cara mendapatkan anggota enum rawValue, jadi saya memperbaiki daftar. Juga memperbaiki kesalahan kecil dengan kesalahan terlebih dahulu 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++)
                }
            }
        )
    }
}
gleb vodovozov
sumber
1
enum Rank: Int
{
    case Ace = 0
    case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
    case Jack, Queen, King
    case Count
}

enum Suit : Int
{
    case Spades = 0
    case Hearts, Diamonds, Clubs
    case Count
}

struct Card
{
    var rank:Rank
    var suit:Suit
}

class Test
{
    func makeDeck() -> Card[]
    {
        let suitsCount:Int = Suit.Count.toRaw()
        let rankCount:Int = Rank.Count.toRaw()
        let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
        let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)

        for i:Int in 0..rankCount
        {
            for j:Int in 0..suitsCount
            {
                deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
            }
        }
        return deck
    }
}

Berdasarkan jawaban Rick: ini 5 kali lebih cepat

John Lluch-Zorrilla
sumber
Menambahkan Countcase akan merusak switchimplementasi dan CaseIterablekesesuaian yang lengkap.
Cœur