ฉันจะกำหนดจำนวนผู้ป่วยใน Swift enum ได้อย่างไร
(ฉันต้องการหลีกเลี่ยงการระบุค่าทั้งหมดด้วยตนเองหรือใช้ " เคล็ดลับ enum_count " ด้วยตนเองหากเป็นไปได้)
ฉันจะกำหนดจำนวนผู้ป่วยใน Swift enum ได้อย่างไร
(ฉันต้องการหลีกเลี่ยงการระบุค่าทั้งหมดด้วยตนเองหรือใช้ " เคล็ดลับ enum_count " ด้วยตนเองหากเป็นไปได้)
คำตอบ:
ในฐานะของ Swift 4.2 (Xcode 10) คุณสามารถประกาศความสอดคล้องกับCaseIterable
โปรโตคอลนี้ใช้ได้กับการแจกแจงทั้งหมดโดยไม่มีค่าที่เกี่ยวข้อง:
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
จำนวนกรณีที่ได้รับเพียงแค่ตอนนี้ด้วย
print(Stuff.allCases.count) // 4
สำหรับข้อมูลเพิ่มเติมโปรดดู
ฉันมีโพสต์บล็อกที่มีรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้ แต่ตราบใดที่ประเภทดิบของคุณเป็นจำนวนเต็มคุณสามารถเพิ่มจำนวนด้วยวิธีนี้:
enum Reindeer: Int {
case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen
case Rudolph
static let count: Int = {
var max: Int = 0
while let _ = Reindeer(rawValue: max) { max += 1 }
return max
}()
}
case A=1, B=3
?
enum
มีInt
ค่า raw ที่คุณลืมพูดถึง: Swift enum ที่มีค่า raw ดิบไม่จำเป็นต้องเริ่มต้นจาก 0 (แม้ว่าจะเป็นพฤติกรรมเริ่มต้น) และค่าดิบของมันอาจเป็นแบบสุ่มก็ได้ เพื่อเพิ่มขึ้น 1 (แม้ว่าจะเป็นพฤติกรรมเริ่มต้น)
อัปเดต Xcode 10
ใช้CaseIterable
โปรโตคอลใน enum มันให้allCases
คุณสมบัติคงที่ซึ่งมีทุกกรณี enum เป็นCollection
เป็น เพียงใช้count
ทรัพย์สินเพื่อทราบจำนวน Enum ที่มีอยู่
ดูคำตอบของมาร์ตินเพื่อเป็นตัวอย่าง (และยกเลิกคำตอบของเขามากกว่าของฉัน)
คำเตือน : วิธีการด้านล่างดูเหมือนจะไม่ทำงานอีกต่อไป
ฉันไม่ทราบวิธีทั่วไปใด ๆ ในการนับจำนวนกรณี enum ฉันได้สังเกตเห็นว่าhashValue
ทรัพย์สินของคดี enum นั้นเพิ่มขึ้นเริ่มจากศูนย์และตามลำดับที่กำหนดโดยลำดับที่มีการประกาศกรณี ดังนั้นแฮชของ enum สุดท้ายบวกหนึ่งจะสอดคล้องกับจำนวนของกรณี
ตัวอย่างเช่นกับ enum นี้:
enum Test {
case ONE
case TWO
case THREE
case FOUR
static var count: Int { return Test.FOUR.hashValue + 1}
}
count
ส่งคืน 4
ฉันไม่สามารถบอกได้ว่านี่เป็นกฎหรือหากมันจะเปลี่ยนแปลงในอนาคตดังนั้นให้ใช้ความเสี่ยงของคุณเอง :)
hashValues
สิ่งเหล่านี้จริงๆ สิ่งที่เรารู้ก็คือมันมีค่าที่ไม่ซ้ำแบบสุ่ม - สามารถเปลี่ยนแปลงได้อย่างง่ายดายในอนาคตขึ้นอยู่กับรายละเอียดการใช้งานคอมไพเลอร์ แต่โดยรวมแล้วการขาดฟังก์ชั่นการนับในตัวรบกวน
case ONE = 0
แล้วคุณสามารถแทนที่ด้วยhashValue
rawValue
static var count = 4
แทนที่จะทิ้งชะตากรรมของคุณในชะตากรรมของการใช้งานในอนาคตของ Swift
ฉันกำหนดโปรโตคอลที่ใช้ซ้ำได้ซึ่งดำเนินการนับจำนวนเคสโดยอัตโนมัติตามวิธีการโพสต์โดย Nate Cook
protocol CaseCountable {
static var caseCount: Int { get }
}
extension CaseCountable where Self: RawRepresentable, Self.RawValue == Int {
internal static var caseCount: Int {
var count = 0
while let _ = Self(rawValue: count) {
count += 1
}
return count
}
}
จากนั้นฉันสามารถนำโปรโตคอลนี้กลับมาใช้ใหม่ได้เช่น:
enum Planet : Int, CaseCountable {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//..
print(Planet.caseCount)
count++
เป็นcount+=1
เพราะ++
โน้ตจะถูกลบใน Swift 3
static var caseCount: Int { get }
ไม่ได้เหรอ? ทำไมต้องมีstatic func
?
case A=1, B=3
?
0
และไม่มีช่องว่าง
สร้างอาร์เรย์ค่าคงที่ตามที่แสดงในคำตอบนี้
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
...
let count = ProductCategory.allValues.count
สิ่งนี้จะมีประโยชน์เมื่อคุณต้องการระบุค่าและใช้ได้กับ Enum ทุกประเภท
static let count = allValues.count
ด้วยการทำ จากนั้นคุณสามารถทำให้เป็นallValues
ส่วนตัวถ้าต้องการ
หากการนำไปปฏิบัติไม่มีอะไรเทียบกับการใช้จำนวนเต็มคุณสามารถเพิ่มค่าสมาชิกพิเศษที่เรียกว่าCount
แทนจำนวนสมาชิกใน enum - ดูตัวอย่างด้านล่าง:
enum TableViewSections : Int {
case Watchlist
case AddButton
case Count
}
ตอนนี้คุณสามารถรับจำนวนสมาชิกใน enum ได้ด้วยการโทร TableViewSections.Count.rawValue
ซึ่งจะส่งคืน 2 ตามตัวอย่างด้านบน
เมื่อคุณจัดการกับ enum ในคำสั่ง switch ตรวจสอบให้แน่ใจว่าได้ทิ้งความล้มเหลวในการยืนยันเมื่อพบCount
สมาชิกที่คุณไม่คาดหวัง:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let currentSection: TableViewSections = TableViewSections.init(rawValue:section)!
switch(currentSection) {
case .Watchlist:
return watchlist.count
case .AddButton:
return 1
case .Count:
assert(false, "Invalid table view section!")
}
}
ฟังก์ชันประเภทนี้สามารถคืนค่าจำนวนของ enum ของคุณได้
สวิฟท์ 2 :
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
i += 1
}
return i
}
สวิฟท์ 3 :
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(to: &i, {
return $0.withMemoryRebound(to: T.self, capacity: 1, { return $0.pointee })
}).hashValue != 0) {
i += 1
}
return i
}
enum
เป็นยัง Hashable
ประเภทเดียวกัน
String Enum พร้อมดัชนี
enum eEventTabType : String {
case Search = "SEARCH"
case Inbox = "INBOX"
case Accepted = "ACCEPTED"
case Saved = "SAVED"
case Declined = "DECLINED"
case Organized = "ORGANIZED"
static let allValues = [Search, Inbox, Accepted, Saved, Declined, Organized]
var index : Int {
return eEventTabType.allValues.indexOf(self)!
}
}
นับ: eEventTabType.allValues.count
ดัชนี: objeEventTabType.index
สนุก :)
โอ้เฮ้ทุกคนสิ่งที่เกี่ยวกับการทดสอบหน่วย?
func testEnumCountIsEqualToNumberOfItemsInEnum() {
var max: Int = 0
while let _ = Test(rawValue: max) { max += 1 }
XCTAssert(max == Test.count)
}
สิ่งนี้รวมกับโซลูชันของ Antonio:
enum Test {
case one
case two
case three
case four
static var count: Int { return Test.four.hashValue + 1}
}
ในรหัสหลักช่วยให้คุณO (1)บวกคุณจะได้รับการทดสอบล้มเหลวถ้ามีคนเพิ่มกรณี enum และไม่ปรับปรุงการดำเนินงานของfive
count
ฟังก์ชั่นนี้ขึ้นอยู่กับพฤติกรรมปัจจุบัน (Swift 1.1) ที่ไม่มีเอกสาร 2 รายการenum
:
enum
case
ถ้านับเป็นกรณีที่ 2-256 UInt8
ก็enum
บิตคาสต์จากดัชนีเคสไม่ถูกต้องค่านั้นhashValue
คือ0
ใช้ความเสี่ยงของคุณเอง :)
func enumCaseCount<T:Hashable>(t:T.Type) -> Int {
switch sizeof(t) {
case 0:
return 1
case 1:
for i in 2..<256 {
if unsafeBitCast(UInt8(i), t).hashValue == 0 {
return i
}
}
return 256
case 2:
for i in 257..<65536 {
if unsafeBitCast(UInt16(i), t).hashValue == 0 {
return i
}
}
return 65536
default:
fatalError("too many")
}
}
การใช้งาน:
enum Foo:String {
case C000 = "foo"
case C001 = "bar"
case C002 = "baz"
}
enumCaseCount(Foo) // -> 3
ฉันเขียนส่วนขยายอย่างง่ายซึ่งให้ enums ทั้งหมดที่ค่าดิบเป็นจำนวนเต็มcount
คุณสมบัติ:
extension RawRepresentable where RawValue: IntegerType {
static var count: Int {
var i: RawValue = 0
while let _ = Self(rawValue: i) {
i = i.successor()
}
return Int(i.toIntMax())
}
}
น่าเสียดายที่มันให้count
คุณสมบัติแก่OptionSetType
ที่ซึ่งมันทำงานไม่ถูกต้องดังนั้นนี่เป็นอีกเวอร์ชั่นที่ต้องมีความสอดคล้องกับCaseCountable
โปรโตคอลสำหรับ enum ใด ๆ ที่คุณต้องการนับ:
protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue: IntegerType {
static var count: Int {
var i: RawValue = 0
while let _ = Self(rawValue: i) {
i = i.successor()
}
return Int(i.toIntMax())
}
}
มันคล้ายกับวิธีที่โพสต์โดย Tom Pelaia แต่ใช้ได้กับทุกประเภทจำนวนเต็ม
แน่นอนมันไม่ได้เป็นแบบไดนามิก แต่สำหรับการใช้งานหลายอย่างคุณสามารถรับได้โดยเพิ่ม var แบบคงที่ใน Enum ของคุณ
static var count: Int{ return 7 }
แล้วใช้มันเป็น EnumName.count
enum EnumNameType: Int {
case first
case second
case third
static var count: Int { return EnumNameType.third.rawValue + 1 }
}
print(EnumNameType.count) //3
หรือ
enum EnumNameType: Int {
case first
case second
case third
case count
}
print(EnumNameType.count.rawValue) //3
* บน Swift 4.2 (Xcode 10) สามารถใช้:
enum EnumNameType: CaseIterable {
case first
case second
case third
}
print(EnumNameType.allCases.count) //3
สำหรับกรณีการใช้งานของฉันใน codebase ที่หลายคนสามารถเพิ่มคีย์ใน enum และกรณีเหล่านี้ควรมีอยู่ในคุณสมบัติ allKeys เป็นสิ่งสำคัญที่ allKeys จะได้รับการตรวจสอบความถูกต้องกับคีย์ใน enum นี่คือเพื่อหลีกเลี่ยงบางคนลืมที่จะเพิ่มคีย์ของพวกเขาไปยังรายการคีย์ทั้งหมดการจับคู่การนับของอาร์เรย์ allKeys (สร้างขึ้นครั้งแรกเป็นชุดเพื่อหลีกเลี่ยงการซ้ำซ้อน) กับจำนวนของปุ่มใน enum ทำให้แน่ใจได้ว่ามีอยู่ทั้งหมด
บางคำตอบข้างต้นแสดงให้เห็นวิธีการที่จะบรรลุเป้าหมายนี้ในสวิฟท์ 2 แต่การทำงานไม่มีในสวิฟท์ 3 นี่คือเวอร์ชั่นที่จัดรูปแบบSwift 3 :
static func enumCount<T: Hashable>(_ t: T.Type) -> Int {
var i = 1
while (withUnsafePointer(to: &i) {
$0.withMemoryRebound(to:t.self, capacity:1) { $0.pointee.hashValue != 0 }
}) {
i += 1
}
return i
}
static var allKeys: [YourEnumTypeHere] {
var enumSize = enumCount(YourEnumTypeHere.self)
let keys: Set<YourEnumTypeHere> = [.all, .your, .cases, .here]
guard keys.count == enumSize else {
fatalError("Missmatch between allKeys(\(keys.count)) and actual keys(\(enumSize)) in enum.")
}
return Array(keys)
}
ขึ้นอยู่กับกรณีการใช้งานของคุณคุณอาจต้องการเรียกใช้การทดสอบการพัฒนาเพื่อหลีกเลี่ยงค่าใช้จ่ายทั้งหมดที่ใช้ในแต่ละคำขอ
ทำไมคุณถึงทำให้มันซับซ้อนทั้งหมด ตัวนับ SIMPLEST ของ Int enum คือการเพิ่ม:
case Count
ในที่สุด และ ... วิโอลา - ตอนนี้คุณมีจำนวน - รวดเร็วและง่าย
0
และไม่มีช่องว่างในลำดับ
หากคุณไม่ต้องการให้โค้ดของคุณอยู่ใน enum สุดท้ายคุณสามารถสร้างฟังก์ชั่นนี้ภายใน enum ของคุณได้
func getNumberOfItems() -> Int {
var i:Int = 0
var exit:Bool = false
while !exit {
if let menuIndex = MenuIndex(rawValue: i) {
i++
}else{
exit = true
}
}
return i
}
สวิฟท์ 3รุ่นการทำงานกับInt
ประเภท enums:
protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue == Int {
static var count: RawValue {
var i: RawValue = 0
while let _ = Self(rawValue: i) { i += 1 }
return i
}
}
เครดิต: ขึ้นอยู่กับคำตอบโดย bzz และ Nate Cook
ไม่รองรับ Generic IntegerType
(ใน Swift 3 ที่เปลี่ยนชื่อเป็นInteger
) เนื่องจากเป็นประเภททั่วไปที่แยกส่วนอย่างหนักซึ่งขาดฟังก์ชั่นมากมาย successor
ไม่สามารถใช้งานกับ Swift 3 ได้อีกต่อไป
โปรดทราบว่าความคิดเห็นจากคำสั่ง Code Commander ถึง Nate Cooks ยังคงใช้ได้:
ในขณะที่ดีเพราะคุณไม่จำเป็นต้อง hardcode ค่านี้จะยกตัวอย่างทุกค่า enum ทุกครั้งที่มีการเรียก นั่นคือ O (n) แทน O (1)
เท่าที่ฉันรู้ว่าขณะนี้ไม่มีวิธีแก้ไขเมื่อใช้สิ่งนี้เป็นส่วนขยายโพรโทคอล (และไม่นำไปใช้ในแต่ละ enum เช่น Nate Cook ได้) เนื่องจากคุณสมบัติที่จัดเก็บแบบคงที่ไม่ได้รับการสนับสนุนในประเภททั่วไป
อย่างไรก็ตามสำหรับ enums เล็ก ๆ นี้จะไม่มีปัญหา กรณีการใช้งานทั่วไปจะเป็นsection.count
สำหรับการUITableViews
ดังกล่าวแล้วโดย Zorayr
คำตอบของ Matthieu Riegler ที่ขยายออกไปนี้เป็นคำตอบสำหรับSwift 3ที่ไม่ต้องใช้ยาสามัญและสามารถเรียกได้ง่ายว่าใช้ชนิด enum ด้วยEnumType.elementsCount
:
extension RawRepresentable where Self: Hashable {
// Returns the number of elements in a RawRepresentable data structure
static var elementsCount: Int {
var i = 1
while (withUnsafePointer(to: &i, {
return $0.withMemoryRebound(to: self, capacity: 1, { return
$0.pointee })
}).hashValue != 0) {
i += 1
}
return i
}
ฉันแก้ไขปัญหานี้ด้วยตนเองโดยการสร้างโปรโตคอล (EnumIntArray) และฟังก์ชั่นยูทิลิตี้ระดับโลก (enumIntArray) ที่ทำให้ง่ายต่อการเพิ่มตัวแปร "All" ให้กับ enum ใด ๆ (โดยใช้ swift 1.2) ตัวแปร "all" จะมีอาร์เรย์ขององค์ประกอบทั้งหมดใน enum เพื่อให้คุณสามารถใช้ all.count สำหรับการนับ
ใช้งานได้เฉพาะกับ enums ที่ใช้ค่า raw ประเภท Int แต่อาจให้แรงบันดาลใจบางอย่างสำหรับประเภทอื่น ๆ
นอกจากนี้ยังแก้ไขปัญหา "ช่องว่างในการกำหนดหมายเลข" และ "เวลามากเกินไปในการทำซ้ำ" ที่ฉันได้อ่านด้านบนและที่อื่น ๆ
แนวคิดคือการเพิ่มโปรโตคอล EnumIntArray ให้กับ enum ของคุณแล้วกำหนดตัวแปร "ทั้งหมด" แบบคงที่โดยการเรียกใช้ฟังก์ชัน enumIntArray และให้องค์ประกอบแรก (และสุดท้ายหากมีช่องว่างในการกำหนดหมายเลข)
เนื่องจากตัวแปรคงที่จะเริ่มต้นได้เพียงครั้งเดียวค่าโสหุ้ยในการผ่านค่าดิบทั้งหมดจะเข้าสู่โปรแกรมของคุณเพียงครั้งเดียว
ตัวอย่าง (ไม่มีช่องว่าง):
enum Animals:Int, EnumIntArray
{
case Cat=1, Dog, Rabbit, Chicken, Cow
static var all = enumIntArray(Animals.Cat)
}
ตัวอย่าง (กับช่องว่าง):
enum Animals:Int, EnumIntArray
{
case Cat = 1, Dog,
case Rabbit = 10, Chicken, Cow
static var all = enumIntArray(Animals.Cat, Animals.Cow)
}
นี่คือรหัสที่ใช้มัน:
protocol EnumIntArray
{
init?(rawValue:Int)
var rawValue:Int { get }
}
func enumIntArray<T:EnumIntArray>(firstValue:T, _ lastValue:T? = nil) -> [T]
{
var result:[T] = []
var rawValue = firstValue.rawValue
while true
{
if let enumValue = T(rawValue:rawValue++)
{ result.append(enumValue) }
else if lastValue == nil
{ break }
if lastValue != nil
&& rawValue > lastValue!.rawValue
{ break }
}
return result
}
หรือคุณสามารถกำหนด_count
ค่า enum ภายนอกและแนบแบบคงที่:
let _count: Int = {
var max: Int = 0
while let _ = EnumName(rawValue: max) { max += 1 }
return max
}()
enum EnumName: Int {
case val0 = 0
case val1
static let count = _count
}
ด้วยวิธีนี้ไม่ว่าคุณจะสร้างขึ้นมาจำนวนเท่าใดมันจะถูกสร้างเพียงครั้งเดียว
(ลบคำตอบนี้ถ้าstatic
ไม่ได้)
วิธีการต่อไปนี้มาจากCoreKitและคล้ายกับคำตอบที่คนอื่น ๆ แนะนำ ใช้งานได้กับ Swift 4
public protocol EnumCollection: Hashable {
static func cases() -> AnySequence<Self>
static var allValues: [Self] { get }
}
public extension EnumCollection {
public static func cases() -> AnySequence<Self> {
return AnySequence { () -> AnyIterator<Self> in
var raw = 0
return AnyIterator {
let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } }
guard current.hashValue == raw else {
return nil
}
raw += 1
return current
}
}
}
public static var allValues: [Self] {
return Array(self.cases())
}
}
enum Weekdays: String, EnumCollection {
case sunday, monday, tuesday, wednesday, thursday, friday, saturday
}
Weekdays.allValues.count
แล้วคุณก็ต้องการเพียงแค่โทร
enum WeekDays : String , CaseIterable
{
case monday = "Mon"
case tuesday = "Tue"
case wednesday = "Wed"
case thursday = "Thu"
case friday = "Fri"
case saturday = "Sat"
case sunday = "Sun"
}
var weekdays = WeekDays.AllCases()
print("\(weekdays.count)")
struct HashableSequence<T: Hashable>: SequenceType {
func generate() -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
if next.hashValue == i {
i += 1
return next
}
return nil
}
}
}
extension Hashable {
static func enumCases() -> Array<Self> {
return Array(HashableSequence())
}
static var enumCount: Int {
return enumCases().enumCount
}
}
enum E {
case A
case B
case C
}
E.enumCases() // [A, B, C]
E.enumCount // 3
แต่ควรระมัดระวังในการใช้งานในประเภทที่ไม่ใช่ enum วิธีแก้ปัญหาบางอย่างอาจเป็น:
struct HashableSequence<T: Hashable>: SequenceType {
func generate() -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
guard sizeof(T) == 1 else {
return nil
}
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
if next.hashValue == i {
i += 1
return next
}
return nil
}
}
}
extension Hashable {
static func enumCases() -> Array<Self> {
return Array(HashableSequence())
}
static var enumCount: Int {
return enumCases().count
}
}
enum E {
case A
case B
case C
}
Bool.enumCases() // [false, true]
Bool.enumCount // 2
String.enumCases() // []
String.enumCount // 0
Int.enumCases() // []
Int.enumCount // 0
E.enumCases() // [A, B, C]
E.enumCount // 4
มันสามารถใช้ค่าคงที่คงที่ซึ่งมีค่าสุดท้ายของการแจงนับบวกหนึ่ง
enum Color : Int {
case Red, Orange, Yellow, Green, Cyan, Blue, Purple
static let count: Int = Color.Purple.rawValue + 1
func toUIColor() -> UIColor{
switch self {
case .Red:
return UIColor.redColor()
case .Orange:
return UIColor.orangeColor()
case .Yellow:
return UIColor.yellowColor()
case .Green:
return UIColor.greenColor()
case .Cyan:
return UIColor.cyanColor()
case .Blue:
return UIColor.blueColor()
case .Purple:
return UIColor.redColor()
}
}
}
นี่เป็นเรื่องเล็กน้อย แต่ฉันคิดว่าทางออกที่ดีกว่า O (1) จะเป็นดังต่อไปนี้ ( เฉพาะเมื่อ enum ของคุณInt
เริ่มต้นที่ x และอื่น ๆ ):
enum Test : Int {
case ONE = 1
case TWO
case THREE
case FOUR // if you later need to add additional enums add above COUNT so COUNT is always the last enum value
case COUNT
static var count: Int { return Test.COUNT.rawValue } // note if your enum starts at 0, some other number, etc. you'll need to add on to the raw value the differential
}
คำตอบที่เลือกในปัจจุบันที่ฉันยังเชื่อว่าเป็นคำตอบที่ดีที่สุดสำหรับ enums ทั้งหมดยกเว้นว่าคุณกำลังใช้งานอยู่Int
ฉันขอแนะนำวิธีแก้ปัญหานี้
guard
ต่อต้านCOUNT
และส่งข้อผิดพลาดคืนค่าเท็จ ฯลฯ เพื่อแก้ไขข้อกังวลของคุณเกี่ยวกับการเป็นตัวแทนของประเภท