I have predefined enum for buttons IDs:
typedef enum
{
button1ID = 407,
button2ID = 999,
button3ID = 408,
button4ID = 409,
} TOP_MENU_BUTTON_TYPE;
I need to find out if the ID I recieve is defiened in the enum. How can I do that? Something like:
if(id in TOP_MENU_BUTTON_TYPE)
There is no way to dynamically iterate an enum. Enums are static feature, they don't exist during runtime. In runtime they are just plain integers (of some size) and values.
It's not possible with this requirement you stated in bounty:
In your answer do not use hard coded values of the enum, just its type.
The other answers show you pretty much all ways to do it statically.
If I understand your question clearly, then this would be helpful to you..
Instead of using enum alone, you should try that with struct and here it is an answer by #Richard will help you how to do that.
Change enum values at runtime?
https://stackoverflow.com/a/10305425/1083859
In the above link, he explains how to use a dynamic enum values with struct and also you can iterate the values to find out. I think you will get an idea.
You can simply do this:
int validValue = button1ID | button2ID | button3ID | button4ID;
if (validValue & id)
// Valid enum value
An enum is not an object, it's just an integer that the compiler understands at build time. Because of this, you would need to provide low level code to make your check.
If you aren't pre-defining the values of your enums, they will start at 0 and increase by one. This lets you compare a value to see if it's <= your last element.
try this method:
-(BOOL)isDefined:(TOP_MENU_BUTTON_TYPE)type{
BOOL isDefined;
switch (type) {
case button1ID:
case button2ID:
case button3ID:
case button4ID:
isDefined = TRUE;
break;
default:
isDefined = FALSE;
break;
}
return isDefined;
}
//(...)
TOP_MENU_BUTTON_TYPE test;
test = 407;
NSLog(#"is %d a TOP_MENU_BUTTON_TYPE? result: %d", test, [self isDefined:test]);
test = 2;
NSLog(#"is %d a TOP_MENU_BUTTON_TYPE? result: %d", test, [self isDefined:test]);
so:
if ([self isDefined:test]){
// OK, test is defined in TOP_MENU_BUTTON_TYPE
}
in .h
typedef enum
{
407,
999,
408,
409,
} TOP_MENU_BUTTON_TYPE;
#interface CheckoutController : UIViewController{
TOP_MENU_BUTTON_TYPE type;
}
In .m
switch (status) {
case 407:
//Your Task
break;
case 999:
//Your Task
break;
case 408:
//Your Task
break;
case 409:
//Your Task
break;
}
Answers about using switch or bunch of || in if are correct, but…
If you have big enums (enum with a lot of values) you can make this simplier. Also Cocoa uses this trick.
Your enum values must be incremented by one.
Then add to enum two additional values:
typedef enum {
buttonIDMin = 407, // Lowest value
button1ID = 407,
button2ID = 408, // Incremented by ONE
button3ID = 409,
button4ID = 410,
buttonIDMax = 410, // Highest value
} TOP_MENU_BUTTON_TYPE;
When you are comparing, you just need to do:
if (buttonID >= buttonIDMin && buttonID <= buttonIDMax) ...
Related
In my app, I am using Integers, Doubles, Floats, and CGFloats to represent a number of different values. According to my app’s semantic, these values may become “invalid“, a state which I represent using a reserved value, i. e. -1. The simplest approach to make this usable in code would be this:
anIntVariable = -1
aFloatVariable = -1
aDoubleVariable = -1.0
...
To get away from this convention driven approach and increase readability and adaptability I defined a number of extensions:
extension Int {
static var invalid = -1
}
extension Float {
static var invalid = -1.0
}
extension Double {
static var invalid = -1.0
}
...
So the above code would now read:
anIntVariable = .invalid
aFloatVariable = .invalid
aDoubleVariable = .invalid
...
It does work. However, I’m not really happy with this approach. Does anyone of you have an idea for a better way of expressing this?
To add some complexity, in addition to simple types like Int, Float, or Double, I also use Measurement based types like this:
let length = Measurement(value: .invalid, unit: UnitLength.baseUnit())
Extra bonus point if you find a way to include “invalid“ measurements in your solution as well...
Thanks for helping!
Some Additional Thoughts
I know I could use optionals with nil meaning “invalid”. In this case, however, you’d have additional overhead with conditional unwrapping... Also, using nil as “invalid” is yet another convention.
It isn’t better or worse, just different. Apple uses “invalid” values in its own APIs, i. e. the NSTableViewmethod row(for:) will return -1 if the view is not in the table view. I agree, however, that this very method perfectly illustrates that returning an optional would make a lot of sense...
I'd use optionals for that.
If you want lack of value and invalid value to be different states in your app, i'd suggest creating a wrapper for your values:
enum Validatable<T> {
case valid(T)
case invalid
}
And use it like that:
let validValue : Validatable<Int> = .valid(5)
let invalidValue : Validatable<Int> = .invalid
var validOptionalDouble : Validatable<Double?> = .valid(nil)
validOptionalDouble = .valid(5.0)
let measurement : Validatable<Measurement> = .invalid
etc.
You can then check for value by switch on that enum to access the associated value like this:
switch validatableValue {
case .valid(let value):
//do something with value
case .invalid:
//handle invalid state
}
or
if case .valid(let value) = validatableValue {
//handle valid state
}
etc
While reviewing some code, I found a Rank enum implemented as:
enum Rank: String {
case first
case second
case third
}
However, the surprising part for me was that I saw a code similar to this:
let gold = [300, 200, 100]
let firstPrize = gold[Rank.first.hashValue] // 300
means that Rank.first.hashValue has been used as an index! For the first look, it seems to be not a good idea to use a hash value as an index for an array:
Hash values are not guaranteed to be equal across different executions
of your program. Do not save hash values to use during a future
execution.
hashValue
Nevertheless it never causes an issue (at least that's what they said).
I tried to trace the issue, by implementing:
print(Rank.first.hashValue) // 0
print(Rank.second.hashValue) // 1
print(Rank.third.hashValue) // 2
and I saw is the output is always the same.
Although we could declare a property in the enum to do such a functionality, as:
var index: Int {
switch self {
case .first:
return 0
case .second:
return 1
case .third:
return 2
}
}
hence:
let firstPrize = gold[Rank.first.index] // 300
I would prefer to know why using the hashValue seems to be ok in this case? Could it be related to: I misunderstand what exactly is hashValue?
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
In Xcode, I have an if and an else statement.
I would like to make the statement have multiple conditions, but only one of them have to be 'YES'.
For example:
I have a NSString, the value of that string is:
[NSstring stringWithFormat:#"ABCDEFG12345"];
I need to have my if statement check for if A or 1 or 5 is in the string. I understand how to use [string rangeOfString:#"CheckHere"];.
I need my if statement to find one or all of those given letters/numbers. If one is found, execute the given code, if two are found, execute the given code, if all three are found, execute the given code.
You need no if-else. You can do something like this.
NSString* string = #"ABCDEFG12345";
int foundA = [string rangeOfString:#"A"].location == NSNotFound ? 0 : 1;
int found1 = [string rangeOfString:#"1"].location == NSNotFound ? 0 : 1;
int found5 = [string rangeOfString:#"5"].location == NSNotFound ? 0 : 1;
int foundCount = foundA + found1 + found5;
switch(foundCount) {
case 1: [self executeOne]; break;
case 2: [self executeTwo]; break;
case 3: [self executeThree]; break;
}
One possible approach:
Let's assume you can piece together the (actually somewhat tedious) use of rangeOfString and rangeOfCharacter calls together and can write a method like this:
-(NSInteger)numberOfMatchesFoundInString:(NSString*)inputString;
which lets you pass in a string, and returns a 0,1,2... based on how many matches are found.
To use this convenient result in a highly readable way, you can use a switch statement.
NSInteger* matches = [self numberOfMatchesFoundInString:someString];
switch (matches) {
case 0:
//execute some code here for when no matches are found
break;
case 1:
//execute some different code when one match is found
break;
case 2:
//you get the idea
break;
default:
//some code to handle exceptions if the numberOfMatchesFoundInString method went horribly wrong
break;
Of course some people will tell you that this is functionally no different than calling
if (someCondition) {
//do some stuff
}
else if (someOtherCondition) {
//do some different stuff
}
etc...
but really, you can make either one work.
There are a few useful techniques you can use for string comparisons.
If you just need test if your string is one of a list of strings, use something like this:
NSArray *options = #[#"first", #"second", #"third"];
if ([options contains:inputString]) {
// TODO: Write true block
} else {
// TODO: Write else block
}
If you want to check if your string contains at least one character from a set, use NSString -rangeOfCharacterFromSet:.
Unfortunately, if you want to check if your string contains one or more strings, you have no choice but to write it out the long way. If you do it frequently enough, you may choose to write a class category.
- (BOOL)containsAtLeastOneSubstring:(NSArray *)substrings
{
for (NSString *aString in substrings) {
NSRange range = [self rangeOfString:aString];
if (range.location!=NSNotFound) {
return YES;
}
}
return NO;
}
-
Why doesn't this work:
NSInteger sectionLocation = 0;
NSInteger sectionTitles = 1;
NSInteger sectionNotifications = 2;
switch (section) {
case sectionLocation:
//
break;
case sectionTitles:
//
break;
case sectionNotifications:
//
break;
default:
//
}
I get this compile error:
error: case label does not reduce to an integer constant
Is it not possible to use NSInteger's like this? If so, is there another way to use variables as cases in a switch statement? sectionLocation etc. have variable values.
The problem isn't the scalar type, but that the case labels may change value when they are variables like that.
For all intents and purposes, the compiler compiles a switch statement as a set of gotos. The labels can't be variable.
Use an enumerated type or #defines.
The reason is that the compiler will often want to create a 'jump table' using the switch value as the key into that table and it can only do that if it's switching on a simple integer value. This should work instead:
#define sectionLocation 0
#define sectionTitles 1
#define sectionNotifications 2
int intSection = section;
switch (intSection) {
case sectionLocation:
//
break;
case sectionTitles:
//
break;
case sectionNotifications:
//
break;
default:
//
}
The problem here is you are using variables. You can only use constants in switch statements.
Do something like
#define SOME_VALUE 1
or
enum Values {
valuea = 1,
valueb = 2,
...
}
And you will be able to use valuea and so forth in your switch statement.
If your case values truly change at runtime, that's what the if...else if...else if construct is there for.
or just do this
switch((int)secion)
I want to have a enum as a parameter of my function. Would this work?
(UIFont*) myMethodName:(UITableViewCellStyle) cellStyle {
//...
if (cellStyle == UITableViewCellStyleValue2)
// ...
}
Then I would call the method like this way
UIFont *resultFont = [self myMethodName:UITableViewCellStyleSubtitle];
Only the following parameters should be allowed:
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
Is it possible?
Would this work? → Yes
Only the following parameters should be allowed: → No it is not possible to restrict the input to just these values, i.e.
UIFont *resultFont = [self myMethodName:12345];
will still compile (assuming you are not using Objective-C++).
Sure:
typedef enum _MyType {
type_a = -1,
type_b = 0,
type_c = 1,
} MyType;
...
- (void) someMethod:(MyType)type {
if (type == type_a) ...
}
Yes, it's possible.
(This feels like an unnecessarily short answer but I can't think of anything else to add!)