Làm cách nào để xác định số lượng trường hợp trong enum Swift?
(Tôi muốn tránh liệt kê thủ công thông qua tất cả các giá trị hoặc sử dụng " thủ thuật enum_count " cũ nếu có thể.)
Làm cách nào để xác định số lượng trường hợp trong enum Swift?
(Tôi muốn tránh liệt kê thủ công thông qua tất cả các giá trị hoặc sử dụng " thủ thuật enum_count " cũ nếu có thể.)
Câu trả lời:
Kể từ Swift 4.2 (Xcode 10), bạn có thể khai báo sự phù hợp với CaseIterable
giao thức, điều này hoạt động cho tất cả các liệt kê mà không có giá trị liên quan:
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
Số lượng các trường hợp bây giờ chỉ đơn giản là thu được với
print(Stuff.allCases.count) // 4
Để biết thêm thông tin, xem
Tôi có một bài đăng trên blog chi tiết hơn về vấn đề này, nhưng miễn là kiểu thô của enum của bạn là một số nguyên, bạn có thể thêm số đếm theo cách này:
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
có một Int
giá trị thô mà bạn quên đề cập: Swift enum với giá trị thô Int không phải bắt đầu từ 0 (mặc dù đó là hành vi mặc định) và giá trị thô của chúng có thể tùy ý, chúng không có tăng lên 1 (mặc dù đó là hành vi mặc định).
Cập nhật Xcode 10
Thông qua CaseIterable
giao thức trong enum, nó cung cấp một thuộc tính tĩnh allCases
chứa tất cả các trường hợp enum là a Collection
. Chỉ cần sử dụng count
tài sản của nó để biết có bao nhiêu trường hợp enum có.
Xem câu trả lời của Martin để biết ví dụ (và nêu lên câu trả lời của anh ấy thay vì của tôi)
Cảnh báo : phương pháp bên dưới dường như không hoạt động nữa.
Tôi không biết về bất kỳ phương pháp chung nào để đếm số trường hợp enum. Tuy nhiên, tôi nhận thấy rằng thuộc hashValue
tính của các trường hợp enum là tăng dần, bắt đầu từ 0 và với thứ tự được xác định theo thứ tự các trường hợp được khai báo. Vì vậy, hàm băm của enum cuối cùng cộng với một số tương ứng với số lượng các trường hợp.
Ví dụ với enum này:
enum Test {
case ONE
case TWO
case THREE
case FOUR
static var count: Int { return Test.FOUR.hashValue + 1}
}
count
trả về 4.
Tôi không thể nói nếu đó là một quy tắc hoặc nếu nó sẽ thay đổi trong tương lai, vì vậy hãy tự chịu rủi ro khi sử dụng :)
hashValues
những điều này; tất cả những gì chúng ta biết là đó là một số giá trị duy nhất ngẫu nhiên - có thể thay đổi rất dễ dàng trong tương lai tùy thuộc vào một số chi tiết triển khai trình biên dịch; nhưng nhìn chung, việc thiếu chức năng đếm tích hợp là đáng lo ngại.
case ONE = 0
, thì bạn có thể thay thế hashValue
bằng rawValue
.
static var count = 4
thay vì để số phận của bạn trong số phận của các triển khai trong tương lai của Swift
Tôi xác định một giao thức có thể sử dụng lại, tự động thực hiện đếm trường hợp dựa trên cách tiếp cận được đăng bởi 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
}
}
Sau đó, tôi có thể sử dụng lại giao thức này ví dụ như sau:
enum Planet : Int, CaseCountable {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//..
print(Planet.caseCount)
count++
để count+=1
từ ++
ký hiệu sẽ được loại bỏ trong Swift 3
static var caseCount: Int { get }
? Tại sao cần static func
?
case A=1, B=3
?
0
và không có khoảng trống.
Tạo mảng allValues tĩnh như trong câu trả lời này
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
...
let count = ProductCategory.allValues.count
Điều này cũng hữu ích khi bạn muốn liệt kê các giá trị và hoạt động cho tất cả các loại Enum
static let count = allValues.count
. Sau đó, bạn có thể làm cho allValues
riêng tư nếu muốn.
Nếu việc triển khai không có bất cứ điều gì chống lại việc sử dụng số nguyên Count
enum , bạn có thể thêm một giá trị thành viên bổ sung được gọi để thể hiện số lượng thành viên trong enum - xem ví dụ bên dưới:
enum TableViewSections : Int {
case Watchlist
case AddButton
case Count
}
Bây giờ bạn có thể nhận được số lượng thành viên trong enum bằng cách gọi, TableViewSections.Count.rawValue
sẽ trả về 2 cho ví dụ trên.
Khi bạn xử lý enum trong câu lệnh chuyển đổi, hãy đảm bảo đưa ra một lỗi xác nhận khi gặp Count
thành viên mà bạn không mong đợi:
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!")
}
}
Loại chức năng này có thể trả về số đếm của enum của bạn.
Swift 2 :
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
i += 1
}
return i
}
Swift 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
là cũng Hashable
cùng loại.
Chuỗi Enum với Index
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)!
}
}
đếm : eEventTabType.allValues.count
mục lục : objeEventTabType.index
Thưởng thức :)
Oh hey mọi người, những gì về bài kiểm tra đơn vị?
func testEnumCountIsEqualToNumberOfItemsInEnum() {
var max: Int = 0
while let _ = Test(rawValue: max) { max += 1 }
XCTAssert(max == Test.count)
}
Điều này kết hợp với giải pháp của Antonio:
enum Test {
case one
case two
case three
case four
static var count: Int { return Test.four.hashValue + 1}
}
trong mã chính cung cấp cho bạn O (1) cộng với bạn nhận được một bài kiểm tra thất bại nếu ai đó thêm trường hợp enum five
và không cập nhật việc thực hiện count
.
Hàm này dựa trên 2 hành vi hiện tại không có giấy tờ (Swift 1.1) enum
:
enum
chỉ là một chỉ số của case
. Nếu số trường hợp là từ 2 đến 256, thì đó là UInt8
.enum
bit được đúc từ chỉ mục trường hợp không hợp lệ , hashValue
thì đó là0
Vì vậy, sử dụng có nguy cơ của riêng bạn :)
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")
}
}
Sử dụng:
enum Foo:String {
case C000 = "foo"
case C001 = "bar"
case C002 = "baz"
}
enumCaseCount(Foo) // -> 3
Tôi đã viết một phần mở rộng đơn giản cung cấp cho tất cả các enum trong đó giá trị thô là số nguyên một thuộc count
tính:
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())
}
}
Thật không may, nó mang lại cho count
tài sản OptionSetType
nơi nó không hoạt động chính xác, vì vậy đây là một phiên bản khác yêu cầu tuân thủ rõ ràng với CaseCountable
giao thức cho bất kỳ trường hợp nào bạn muốn tính:
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())
}
}
Nó rất giống với cách tiếp cận được đăng bởi Tom Pelaia, nhưng hoạt động với tất cả các loại số nguyên.
Tất nhiên, nó không động nhưng với nhiều mục đích sử dụng, bạn có thể nhận được bằng một var tĩnh được thêm vào Enum của bạn
static var count: Int{ return 7 }
và sau đó sử dụng nó như là EnumName.count
enum EnumNameType: Int {
case first
case second
case third
static var count: Int { return EnumNameType.third.rawValue + 1 }
}
print(EnumNameType.count) //3
HOẶC LÀ
enum EnumNameType: Int {
case first
case second
case third
case count
}
print(EnumNameType.count.rawValue) //3
* Trên Swift 4.2 (Xcode 10) có thể sử dụng:
enum EnumNameType: CaseIterable {
case first
case second
case third
}
print(EnumNameType.allCases.count) //3
Đối với trường hợp sử dụng của tôi, trong một cơ sở mã nơi nhiều người có thể thêm khóa vào enum và tất cả các trường hợp này đều có sẵn trong thuộc tính allKeys, điều quan trọng là allKeys phải được xác thực đối với các khóa trong enum. Điều này là để tránh ai đó quên thêm khóa của họ vào danh sách tất cả các khóa.Khớp số đếm của mảng allKeys (lần đầu tiên được tạo thành một tập hợp để tránh các bản sao) so với số lượng khóa trong enum đảm bảo rằng tất cả chúng đều có mặt.
Một số câu trả lời ở trên chỉ ra cách để đạt được điều này trong Swift 2 nhưng không có câu trả lời nào trong Swift 3 . Đây là phiên bản định dạng 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)
}
Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể muốn chạy thử nghiệm trong quá trình phát triển để tránh chi phí sử dụng allKeys trên mỗi yêu cầu
Tại sao bạn làm cho nó quá phức tạp? Bộ đếm SIMPLEST của Int enum là thêm:
case Count
Đến cuối cùng. Và ... viola - bây giờ bạn có số đếm - nhanh chóng và đơn giản
0
và không có khoảng trống trong chuỗi.
Nếu bạn không muốn căn cứ mã của mình vào enum cuối cùng, bạn có thể tạo hàm này bên trong enum của mình.
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
}
Một Swift 3 phiên bản làm việc với Int
loại 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
}
}
Tín dụng: Dựa trên các câu trả lời của bzz và Nate Cook.
Chung IntegerType
(trong Swift 3 được đổi tên thành Integer
) không được hỗ trợ, vì đây là loại chung bị phân mảnh nặng nề, thiếu rất nhiều chức năng.successor
không có sẵn với Swift 3 nữa.
Xin lưu ý rằng nhận xét từ Code Commander đến Nate Cooks vẫn còn hiệu lực:
Mặc dù tốt vì bạn không cần mã hóa một giá trị, nhưng điều này sẽ khởi tạo mọi giá trị enum mỗi khi nó được gọi. Đó là O (n) thay vì O (1).
Theo như tôi biết hiện tại không có cách giải quyết nào khi sử dụng phần mở rộng giao thức này (và không triển khai trong mỗi enum như Nate Cook đã làm) do các thuộc tính lưu trữ tĩnh không được hỗ trợ trong các loại chung.
Dù sao, đối với các enum nhỏ thì điều này không có vấn đề gì. Một trường hợp điển hình sử dụng sẽ là section.count
cho UITableViews
như đã đề cập bởi Zorayr.
Mở rộng câu trả lời của Matthieu Riegler, đây là một giải pháp cho Swift 3 không yêu cầu sử dụng thuốc generic và có thể dễ dàng được gọi bằng cách sử dụng loại enum với 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
}
Tôi đã tự giải quyết vấn đề này bằng cách tạo một giao thức (EnumIntArray) và một hàm tiện ích toàn cầu (enumIntArray) để dễ dàng thêm biến "Tất cả" vào bất kỳ enum nào (sử dụng swift 1.2). Biến "all" sẽ chứa một mảng gồm tất cả các phần tử trong enum để bạn có thể sử dụng all.count cho số đếm
Nó chỉ hoạt động với các enum sử dụng các giá trị thô của kiểu Int nhưng có lẽ nó có thể cung cấp một số nguồn cảm hứng cho các loại khác.
Nó cũng giải quyết các vấn đề "khoảng cách trong đánh số" và "quá nhiều thời gian để lặp lại" mà tôi đã đọc ở trên và ở nơi khác.
Ý tưởng là thêm giao thức EnumIntArray vào enum của bạn và sau đó xác định biến tĩnh "tất cả" bằng cách gọi hàm enumIntArray và cung cấp cho nó phần tử đầu tiên (và cuối cùng nếu có khoảng trống trong việc đánh số).
Bởi vì biến tĩnh chỉ được khởi tạo một lần, nên chi phí đi qua tất cả các giá trị thô chỉ đánh vào chương trình của bạn một lần.
ví dụ (không có khoảng trống):
enum Animals:Int, EnumIntArray
{
case Cat=1, Dog, Rabbit, Chicken, Cow
static var all = enumIntArray(Animals.Cat)
}
ví dụ (có khoảng trống):
enum Animals:Int, EnumIntArray
{
case Cat = 1, Dog,
case Rabbit = 10, Chicken, Cow
static var all = enumIntArray(Animals.Cat, Animals.Cow)
}
Đây là mã thực hiện nó:
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
}
Hoặc bạn chỉ có thể định nghĩa _count
bên ngoài enum và đính kèm tĩnh:
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
}
Theo cách đó, bất kể bạn tạo ra bao nhiêu enum, nó sẽ chỉ được tạo một lần.
(xóa câu trả lời này nếu static
có)
Phương pháp sau đến từ CoreKit và tương tự như câu trả lời mà một số người khác đã đề xuất. Điều này hoạt động với 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
}
Sau đó, bạn chỉ cần gọi 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
nhưng hãy cẩn thận với việc sử dụng trên các loại không enum. Một số cách giải quyết có thể là:
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
Nó có thể sử dụng hằng số tĩnh chứa giá trị cuối cùng của phép liệt kê cộng với một.
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()
}
}
}
Điều này là nhỏ, nhưng tôi nghĩ rằng một giải pháp O (1) tốt hơn sẽ là như sau ( CHỈ nếu enum của bạn Int
bắt đầu ở x, v.v.):
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
}
Câu trả lời được chọn hiện tại tôi vẫn tin là câu trả lời tốt nhất cho tất cả các enum, trừ khi bạn đang làm việc với Int
thì tôi khuyên bạn nên giải pháp này.
guard
chống lại COUNT
và đưa ra lỗi, trả về false, v.v. để giải quyết mối quan tâm của bạn về biểu diễn các loại.