Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting enum values based on key names

Tags:

c#

enums

linq

I have an enum like so:

public enum Animals 
{ 
    CatOne = 12, 
    CatTwo = 13, 
    CatThree = 14, 
    DogOne = 21, 
    DogTwo = 22 
};

Great.

Now I want to get the values of all the cats.. What I'm trying to do is this:

public static int[] GetCatValues()
{

    List<int> catValues = new List<int>();

    foreach(var cat in Enum.GetNames(typeof(Animals)))
    {
        Animals animal;

        if(cat.StartsWith("Cat"))
        {
            Enum.TryParse(cat, out animal);
            catValues.Add((int)animal);
        }       
    }

    return catValues.ToArray();
}

Which works okay. Except it looks ugly. Why can't I do something like

Animals
    .Select(r => (int)r)
    .Where(r => r.StartsWith("Cat"))
    .ToArray();

I know that doesn't work. So is there a better way of getting all values of enum that starts with certain string.

I know I could probably use regex to avoid false positives, but, I am keeping it simple for now.

Thanks.

like image 326
LocustHorde Avatar asked Mar 12 '13 15:03

LocustHorde


2 Answers

Here's a compromise set of code - it's not as clean as what you're looking for, but it's far better than the foreach loop version.

Enum.GetValues(typeof(Animals)).OfType<Animals>()
    .Where(x => x.ToString().StartsWith("Cat"))
    .Select(x => (int)x).ToArray();
like image 162
Bobson Avatar answered Nov 11 '22 09:11

Bobson


Well the simplest approach is simply to get the name using ToString:

return ((Animals[]) Enum.GetValues(typeof(Animals)))
    .Where(r => r.ToString().StartsWith("Cat"))
    .Select(r => (int) r) // Assuming you want this
    .ToArray();

Or using Cast<> to avoid the cast to Animals[]:

return Enum.GetValues(typeof(Animals))
    .Cast<Animals>()
    .Where(r => r.ToString().StartsWith("Cat"))
    .Select(r => (int) r) // Assuming you want this
    .ToArray();

(I prefer Cast<> to OfType<> here as we really are expecting every value to be Animals - if anything isn't, an exception is entirely appropriate!)

However, this all feels slightly icky to me - attaching any importance to the names of enum values always feels a little bit hairy, and parsing specific bits of the name even more so. You might want to try decorating each value with an attribute to specify a "group".

I'd also suggest calling the enum Animal rather than Animals - only flag-based enums should generally be plural, and "a collection of Animals values" sounds distinctly odd.

(Also, you may want to look at my Unconstrained Melody project which allows for type-safe, efficient generic access to enums, via generic constraints which are legal in IL but can't be expressed in C#...)

like image 44
Jon Skeet Avatar answered Nov 11 '22 10:11

Jon Skeet