39

Below is my Enumerator List:

public enum StatusEnum
{
    Open = 1,
    Rejected = 2,
    Accepted = 3,
    Started = 4,
    Completed = 5,
    Cancelled = 6,
    Assigned = 7
}

I need to bind this to a Combobox, but, only show a few specific statuses and ignore the rest.

This is what I have so far:

public static List<Activity.StatusEnum> StatusList()
{
        IEnumerable<Activity.StatusEnum> query = Enum.GetValues(typeof(Activity.StatusEnum)).Cast<Activity.StatusEnum>()
                        .Where(x => x == Activity.StatusEnum.Open
                            || x == Activity.StatusEnum.Rejected
                            || x == Activity.StatusEnum.Accepted
                            || x == Activity.StatusEnum.Started);
        return query.ToList();
}

However, I feel that the code is little messy and is not a correct approach to bind filtered Enum list to a Combobox. Can anyone suggest a more robust way of doing this?

Update

I might need to change the Order of selection. So I need a generic solution which doesn't only get the first X number of statuses.

Learner
  • 3,904
  • 6
  • 29
  • 44
  • 1
    Is `Activity` a namespace? You can slim your code down a bit by adding a `using` directive, and removing the `Activity` references. – Robert Harvey Jun 13 '13 at 16:11
  • You've moved the goalposts. If all you want is (for now) those 4 enum values hardcoded there's nothing terribly wrong with the code you posted - although pswg's is neater. The generic solutions are in the answers, especially mine which sorts the enum values by int (otherwise they sort naturally by unsigned magnitude) and then takes(x) for you. x could easily be an argument to an extension method. – Stephen Kennedy Jun 13 '13 at 16:30

6 Answers6

52
return Enum.GetValues(typeof(Activity.StatusEnum)).Cast<Activity.StatusEnum>().Where((n, x) => x < 4);

If you want to be able to change the list of items, just add them into a List<Activity.StatusEnum> and use Contains:

var listValid = new List<Activity.StatusEnum>() { Activity.StatusEnum.Open, Activity.StatusEnum.Rejected, Activity.StatusEnum.Accepted, Activity.StatusEnum.Started };
return Enum.GetValues(typeof(Activity.StatusEnum)).Cast<Activity.StatusEnum>().Where(n => listValid.Contains(n));
It'sNotALie.
  • 22,289
  • 12
  • 68
  • 103
  • 1
    I was thinking along these lines, but the use of a magic number bothers me. Maybe compare to `(int)StatusEnum.Started`? – Robert Harvey Jun 13 '13 at 16:12
  • @RobertHarvey He said 4, not to compare against Started. It bothers me too, but this does exactly what asked. Unlike the most upvoted answer, too. – It'sNotALie. Jun 13 '13 at 16:15
  • Sorry for the confusion but, I need a solution without Take(4) as the order might change for other situations. – Learner Jun 13 '13 at 16:23
12

Well if you're going to hard code the items that should be in the list anyway, why not just do this:

public static List<Activity.StatusEnum> StatusList()
{
    return new List<Activity.StatusEnum>
    { 
        Activity.StatusEnum.Open, 
        Activity.StatusEnum.Rejected, 
        Activity.StatusEnum.Accepted, 
        Activity.StatusEnum.Started 
    };
}

You could also dispose of the List<T> and just return the array itself. As long as you know these are the items you want, then there's no need for Linq.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • 1
    Upvoted, but he should either change the return type into the array type `Activity.StatusEnum[]`, like you suggest, or use collection initializer syntax on the `List<>` (just remove the five charaters `new[]` and the parenthesis `()`). It is crazy to allocate an array that is immediately discarded again (only the `List<>` is kept)! – Jeppe Stig Nielsen Jun 13 '13 at 16:21
  • @JeppeStigNielsen Thanks, I'd forgotten about collection initializers. – p.s.w.g Jun 13 '13 at 16:34
12

Steps:

  • Get the enum values and cast the results to the type of the enum
  • Sort the enum values by their integer values (otherwise they sort naturally by unsigned magnitude)
  • Take the first 4

Code:

return Enum.GetValues(typeof(Activity.StatusEnum))
.Cast<Activity.StatusEnum>()
.OrderBy(se =>(int)se)
.Take(4);

Output:

Open Rejected Accepted Started

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
8

First, if possible, I'd make your enum values powers of 2, so they could be OR'd together.

public enum StatusEnum
{
    Open = 1,
    Rejected = 2,
    Accepted = 4,
    Started = 8,
    Completed = 16,
    Cancelled = 32,
    Assigned = 64
}

Then you could do something like this:

public static List<Activity.StatusEnum> StatusList()
{
    var statusesToShow = Activity.StatusEnum.Open | Activity.StatusEnum.Rejected | Activity.StatusEnum.Accepted | Activity.StatusEnum.Started;

    return Enum
        .GetValues(typeof(Activity.StatusEnum))
        .Cast<Activity.StatusEnum>()
        .Where(x => (x & statusesToShow) == x)
        .ToList();
}

EDIT: In light of the fact that you can't change the enum values, I'd just recommend you use something like:

public static List<Activity.StatusEnum> StatusList()
{
    return new List<Activity.StatusEnum> {
        Activity.StatusEnum.Open, 
        Activity.StatusEnum.Rejected, 
        Activity.StatusEnum.Accepted, 
        Activity.StatusEnum.Started
    };
}
Chris Doggett
  • 19,959
  • 4
  • 61
  • 86
  • Thanks for the suggestion but I am using objects which I don't have control over. +1 for solution. – Learner Jun 13 '13 at 16:30
  • @Learner: In that case, you don't need to use Enum.GetValues at all, and can just initialize a List with the known enums, which would be much easier. I'd also recommend using an IEnumerable instead of List, unless the binding specifically requires a List. – Chris Doggett Jun 13 '13 at 16:36
  • May I know the reason for recommending IEnumerable? – Learner Jun 13 '13 at 16:39
  • 1
    In general, it's considered a best practice to prefer using interfaces over specific implementations. If your code that calls this expects an IEnumerable, it doesn't matter if the function itself uses List, HashSet, T[] (array), or any other implementation. It would only care that there's a number of items it can iterate over. It would also prevent code that called it from calling .Add() on the List. Basically, you just want to give users what they NEED access to, and nothing more. It's probably overkill for this, but it's just generally a good practice. – Chris Doggett Jun 13 '13 at 16:42
  • BTW, I will wait for Android version of Milestone ;) – Learner Jun 13 '13 at 17:02
6

". . . only show the first 4 statuses and ignore the rest."

To get the first n elements of an IEnumerable<T>, use the Take method:

return Enum.GetValues(typeof(Activity.StatusEnum))
    .Cast<Activity.StatusEnum>()
    .Take(4)
    .ToList();
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
  • Very sorry for the confusion about quoted text. Please see the update. But this will help me. +1 – Learner Jun 13 '13 at 16:28
3

How about something along the lines of:

.Where(x => x <= Activity.StatusEnum.Started)
John Bustos
  • 19,036
  • 17
  • 89
  • 151
  • It's a good solution for this scenario, but I might need to change the order in future. Like just show Open, Completed and Assigned. – Learner Jun 13 '13 at 16:18
  • 1
    @RobertHarvey I'd prefer to make a `List` and use `Contains` tbh – It'sNotALie. Jun 13 '13 at 16:26
  • What the heck is `<=` I have not seen that before!? :) Is that meant to be lest or equal or some other linq function I have never seen before? – Piotr Kula Nov 15 '13 at 13:54
  • @ppumkin it's "less than or equal" operator :) – Bronek Oct 18 '16 at 14:38
  • 1
    So embarrassing:) noob at Linq 3 years ago. :D – Piotr Kula Oct 18 '16 at 14:40
  • 2
    Right, @ppumkin??? - I look at some of my old questions and LOVE to see how far I've come! - Got to absolutely love this community for how much they've helped!!!! – John Bustos Oct 19 '16 at 13:47
  • 1
    Completely agreed. So many problems solved, some pages are boookmarked because I keep on forgetting, like complex Grouping with Linq :) But for sure, since Spolsky opened Stack Overflow, the quality of code increased by 66%, globally :) – Piotr Kula Oct 19 '16 at 13:49