45

Is it possible to loop through enum values in Objective-C?

mouviciel
  • 66,855
  • 13
  • 106
  • 140
Frank
  • 3,073
  • 5
  • 40
  • 67

9 Answers9

56

Given

enum Foo {Bar=0,Baz,...,Last};

you can iterate the elements like:

for(int i=Bar; i<=Last; i++) {
  ...
}

Note that this exposes the really-just-an-int nature of a C enum. In particular, you can see that a C enum doesn't really provide type safety, as you can use an int in place of an enum value and vice versa. In addition, this depends on the declaration order of the enum values: fragile to say the least. In addition, please see Chuck's comment; if the enum items are non-contiguous (e.g. because you specified explicit, non-sequential values for some items), this won't work at all. Yikes.

jameshfisher
  • 34,029
  • 31
  • 121
  • 167
Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • 18
    It also requires that all the items in the enum be contiguous. But it's the best you'll get. – Chuck Nov 02 '09 at 19:23
  • it doesn't recognize Foo in the for looo for me. – Frank Nov 02 '09 at 19:24
  • 6
    It's a pure nitpick, but it's spelled `vice versa`. – new123456 Sep 28 '11 at 16:02
  • 1
    @MilesRout There's actually a minimum character change of 6 characters. Since it's a 2 character fix, I can't fix it. – new123456 Sep 01 '13 at 16:13
  • 1
    Note that if the values are not contiguous, but is a bitmask enum, you can use: `for (int i=1; i<=Last; i=i*2)` – Charles Aug 27 '14 at 20:26
  • Please pay attention to the fact that, by specification, representation of enums in C is not defined, i.e. although most compilers treat them as `int` values, you cannot trust such an assumption. With this in mind, there is, unfortunately, no source-code-portable solution for this problem (known to me). – Powerslave Nov 22 '15 at 15:34
  • @Powerslave AFAIR It has to be representable as an integer or larger. – Kami Kaze Dec 04 '19 at 16:10
  • @GoodbyeSE I haven't been concerned with the topic for quite a while, so don't take my word for it, but I can't recall such requirement. I, however, recall being warned that treating them as comparable integer values has serious pitfalls as the standard does not define such behavior. – Powerslave Sep 05 '20 at 15:37
9

If you enum is defined as follows:

enum Direction {  East,  West,  North,  South};

You can loop through it this way:

for ( int direction = East; direction <= South; ++direction)
{
   /* Do something with Direction
}
ennuikiller
  • 46,381
  • 14
  • 112
  • 137
  • I've never done this, so I have some questions. Shouldn't it be Direction.East and Direction.South? Also shouldn't the condition be direction <= Direction.South? Again I've never done this, I'm not trying to correct you, just would like to learn. – Jorge Israel Peña Nov 02 '09 at 18:28
  • 6
    Is there a better way to do this? Just curious, but it seems like depending on the declaration order of the enum values is fragile at best. – Ed S. Nov 02 '09 at 18:34
  • @blaenk: i think you're confusing structs with enums. – pxl Nov 02 '09 at 18:37
  • 4
    @Ed: Yes this is fragile, and no there's not a better way. It's not advisable to do this at all. – Chuck Nov 02 '09 at 19:23
  • One could be more explicit as in enum Direction { East =1, West=2, North=3, South=4}; or even enum Direction { East =313, West, North, South}; but it all comes down to the same thing, except that numbering each element leaves more room for programmer error. In the end it seems to me to be as fragile or non-fragile as any const declaration. – Elise van Looij Nov 03 '09 at 14:01
  • @JorgeIsraelPeña In languages which are using enums as objects like Java or C# -> yes. C you can think of East like global. You are not allowed to define "East" in any other enum. – Offler Jun 19 '17 at 08:51
9

It's a bit of a kludge (as is the entire concept so... eh...) but if you're going to do this multiple times, and you want to be able to deal with the possibility of non-contiguous enums, you can create a (global constant) array, and have (to use ennukiller's example) Directions directions[4] = {East, West, North, South}; and then in your loop you can talk about directions[i] rather than iterating directly over the directions themselves...

As I said, it's ugly, but it's slightly less fragile I think...

Brian Postow
  • 11,709
  • 17
  • 81
  • 125
  • Looks good to me. Is there a way that we can add some kind of macro that will automatically generate C-arrays for us for each enum type we declare? – ArtOfWarfare Nov 11 '12 at 16:40
  • 2
    @ArtOfWarfare I use this all the time: `#define ENUM(type, ...) enum _##type { __VA_ARGS__ }; enum _##type type##s[] = { __VA_ARGS__ }`, though that will only work for contiguous ones. I don't use non-contiguous enums, because that to me signals bad design. Also I have `typedef struct _list List;`, `typedef enum _direction Direction` everywhere in my C code that uses this macro, which is why I'm prefixing the _ to the type. `ENUM(direction, NORTH, EAST, SOUTH, WEST);` gives `enum _direction { NORTH, EAST, SOUTH, WEST }; enum _direction directions = { NORTH, EAST, SOUTH, WEST };` I like it. – Miles Rout Aug 31 '13 at 07:29
7

For non-contiguous enums you can iterate over valid values by doing:

enum {
    value_one   = 0,
    value_two   = 1,
    value_three = 3,
    value_max
}
for ( i = value_one; i < value_max; i++ )
{ 
    switch(i)
    {
        case value_one:
        case value_two:
        case value_three:
        case value_four:
        {
            /* ... Do something with valid enum value ... */
        }
        break;
        default:
            /* Found an invalid enum entry; ignore it */
        break;
    }
}
Matt Conway
  • 229
  • 2
  • 8
  • 1
    In this case, I wouldn't use a loop at all. Just leave the the switch-case blocks one after another, it will be cleaner and sometimes more efficient. – Jorge Bellon Aug 23 '17 at 07:56
  • 1
    Sure, I completely agree with you and I wouldn't write this in production code; the only reason I encased the switch within a loop is because that's what was being asked by the original poster ;) – Matt Conway Sep 05 '17 at 17:17
5

In search of this, I just concocted the following solution which fulfilled my own requirements. I thought I'd post it here for people searching something similar.

Say I have an enum.

typedef NS_ENUM (NSInteger, ISDirection) {
  ISDirectionUp,
  ISDirectionDown,
  ISDirectionLeft,
  ISDirectionRight
};

To be able to loop over it, I define the following preprocessor macro.

#define ISDirectionAllAsDir ISDirection dir = ISDirectionUp; dir <= ISDirectionRight; dir++

Then I use it inside a loop, as such:

for(ISDirectionAllAsDir) {
  NSLog(@"Direction %d", dir);
}

This puts the logic for iterating in one place (the macro) which increases code maintainability.

CloakedEddy
  • 1,965
  • 15
  • 27
2

The length (count of items) of any enumeration can be ascertained by getting the array of names in the enumeration and utilizing the array's length. Use the GetNames method of Enum. Enum.GetNames(enumType).Length.

{
enum Pig { Snort, Sty, Oink, Curly };
const int Pig_Count = Enum.GetNames(typeof(Pig)).Length;
}
GhengisDon
  • 21
  • 1
1

I like what I got from here Iteration over enum in Objective C?

typedef enum { ENUM1=0,  ENUM2, ENUM3, ENUM4 , TOTAL  } ;


for ( int i=ENUM1 ; i<TOTAL; i++)
{}

that way you can keep adding enums later on and not affect your loops?

Community
  • 1
  • 1
yeahdixon
  • 6,647
  • 1
  • 41
  • 43
1

If the above does not work for you, because you're using C++, the following should:

enum Foo {
  One,
  Two,
  Three,
  Last
};

for ( int fooInt = One; fooInt != Last; fooInt++ )
{
   Foo foo = static_cast<Foo>(fooInt);
   // ...
}
serv-inc
  • 35,772
  • 9
  • 166
  • 188
0

I hate dirtying up my enum with a "Last" entry (and the accompanying note that it shouldn't be used, and that it always hast to be kept, well, last), and have only found one way around it (other than using the last enum entry as a secret "last," which, again, makes me feel icky).

If you define your enum using X Macros, you can get a count of the number of items in your enum pretty simply. See the link FMI on when you might use this approach - to keep things brief, here's what it looks like:

#define COLOR_TABLE \
X(red, "red")       \
X(green, "green")   \
X(blue, "blue")

#define X(a, b) a,
enum COLOR {
  COLOR_TABLE
};
#undef X

#define X(a, b) b,
char *color_name[] = {
  COLOR_TABLE
};
#undef X

#define X(a, b) +1
int color_count = 0 COLOR_TABLE;
#undef X
beOn
  • 1,799
  • 1
  • 12
  • 13