1

I have a list of objects being returned and I need to check that they are returned in the correct order by one of the properties (as defined in enum).

To give a better example. Say I have a List() which contains the following

{ Name: "name1", Action: SomeAction.Read, Value: someValue },
{ Name: "name2", Action: SomeAction.Create, Value: someValue },
{ Name: "name3", Action: SomeAction.Update, Value: someValue },
{ Name: "name4", Action: SomeAction.Delete, Value: someValue },
{ Name: "name5", Action: SomeAction.Archive, Value: someValue }

and the enum contains the following:

public enum SomeAction
{
    Read,

    Create,

    Update,

    Delete,

    Download,

    Archive,

    Restore
}

Is there a way I can compare against the enum to check list is ordered correctly by the Action property?

One thing to note is that the list may not contain objects with properties for all the actions defined in the enum, so a list may just have read/write/delete properties etc.

Greg
  • 187
  • 3
  • 15

7 Answers7

1

If I understand you correctly you want to order by the order of items in your enum right?

If that's indeed the case then you probably know the enum items have an int value. With this in mind you could order it like this :

List<dynamic> demo = new List<dynamic>();
demo.Add(new { Name = "name1", Action = SomeAction.Read, Value = "someValue" });
demo.Add(new { Name = "name1", Action = SomeAction.Restore, Value = "someValue" });
demo.Add(new { Name = "name1", Action = SomeAction.Update, Value = "someValue" });

demo = demo.OrderBy(e => (int)e.Action).ToList();

Edit I do agree that this is more than likely not the right way to it. Ordering based on an enum. The OP might want to change the approach completely.

Andrei Dragotoniu
  • 6,155
  • 3
  • 18
  • 32
  • But a `Dictionary` would be much better. You're also not returning a bool, OP wants to know if the order is correct. – Tim Schmelter Jun 08 '16 at 09:14
  • Thanks Andrei, I already have a function which orders them by the action. I need to test that it has been ordered correctly. – Greg Jun 08 '16 at 09:14
  • 1
    @user2928010: note that this is a very bad approach. What if the implementation of enum will change in future? You have not even assigned a number yourself. What if someone else assigns numbers and doesn't know that some other logic depends on it? You could assign a custom [`EnumOrder`-attribute](http://stackoverflow.com/a/25147851/284240). But in my opinion it was much better to use another collection for the ordering. For example a `Dictionary ActionOrder`. Then you'd get it ordered with `var seq = list.OrderBy(x=>ActionOrder[x.Action])` – Tim Schmelter Jun 08 '16 at 09:19
  • Use `bool ordered = list.SequenceEqual(seq)` for the check. – Tim Schmelter Jun 08 '16 at 09:22
1

Enums can have an integer value (some might call it an "ordinal") assigned to them. You can get this integer value by casting.

int valueOfEnum = (int)SomeEnum.SomeEnumValue;

In your case, you haven't assigned the integer values, which is just fine. If you don't assign them, then C# will automatically assign them for you. They start at 0, and increment from there.

Here's the MSDN documentation on using these enum "values".

You can put the items in your enum SomeAction definition in the order you want them to occur, and let C# assign the values automatically. This might be all that you need to do.

You could also assign values to each of them that specify the order that you prefer. This can help if you want to declare them in a different order.

public enum SomeAction
{
    Read = 1,
    Create = 3,
    Update = 2, // Update should come before Create in my list, so I give it a lower value
// ...

If you want to sort actions by groups, you could use this manual assignment technique to assign duplicate values (perhaps Archive = 1 and Update = 1).

Now that you have a basis for comparing items in your list, there are a few ways to go about ensuring that they are in order:

Just sort it

A great way to ensure a constraint is to do the work yourself. Sort it, and it will be sorted :)

This will only work if your sorting function produces a stable sort. The documentation on MSDN says OrderBy does stable sorting so you're fine if you use this method.

It will also only work if you can afford to re-order the items in your list. Judging by the definition of your actions (a list of actions that are each dependent on previous state), I am not sure this is true. You might need to pick one of the other methods for checking order.

var sortedList = yourList.OrderBy(item => (int)item.Action).ToList();

Compare the list to a sorted version of itself

This is useful if you are doing error checking, but don't want to correct the error.

This method won't change the list, so it is probably safe for the type of actions you're looking at.

var sortedList = yourList.OrderBy(item => (int)item.Action);
bool isSorted = Enumerable.SequenceEqual(yourList, sortedList);

Write a comparison algorithm manually

This may be useful if you need to tightly control the memory allocation and CPU usage.

You probably don't need this level of control unless your list is really big, or you're in the middle of a tight loop in high performance code (like a video game draw loop). Even in those cases, you may want to consider refactoring the code so this check isn't in the middle of a tight loop, if it is possible.

For why I used for instead of foreach, see - In .NET, which loop runs faster, 'for' or 'foreach'?

// Note that you have to build a custom type for your list items to keep high performance,
// or write this inline instead of as a function, to avoid the perf hit of IList<dynamic>
bool IsMySpecificListTypeSorted(List<MyCustomListItemType> theList) {
    int previousOrdinal = -1;

    // Not using foreach because it is "8x slower" in tests
    // and you're micro-optimizing in this scenario
    for(int index = 0; index < theList.Count; ++index) {
        var item = theList[index];
        var currentOrdinal = (int)item.Action;

        if(currentOrdinal < previousOrdinal) {
            return false;
        }

        previousOrdinal = currentOrdinal;
    }

    return true;
}
Community
  • 1
  • 1
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
0

I would do this, using Zip linq extension

var inorder= Enum.GetNames(typeof(SomeAction))
        .Zip(array,(x,y) => y.Contains(x))      // change this condition to get uniqueness. 
        .All(x=>x);

Check this Demo

Hari Prasad
  • 16,716
  • 4
  • 21
  • 35
0

If you have items of the SomeAction ordered right, then just sort and compare:

  List<MyType> list = ...

  var orderedRight = list
    .OrderBy(item => (int) (item.Action))
    .Select(item => item.Action);

  boolean inCorrectOrder = list.SequenceEqual(orderedRight
   .Select(item => item.Action));

If you want to use arbitrary order, add mapping:

  Dictionary<SomeAction, int> map = new Dictionary<SomeAction, int>() {
    {SomeAction.Read, 2},
    {SomeAction.Create, 1}, // Create should be 1st
    {SomeAction.Update, 2}, // Read and Update considered interchangeable
    ...
    {SomeAction.Create, 15},
  };

  ...

  var orderedRight = list
    .OrderBy(item => map[item.Action])
    .Select(item => item.Action);

  boolean inCorrectOrder = list.SequenceEqual(orderedRight
   .Select(item => item.Action));
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

You could assign specific values to your enums then implement IComparable as follows

public class TestObj : IComparable<TestObj>
{
    public string Name { get; set; }
    public SomeAction Action { get; set; }
    public string Value { get; set; }

    public TestObj(string name, SomeAction action, string value)
    {
        Name = name;
        Action = action;
        Value = value;
    }

    public enum SomeAction
    {
        Read = 0,
        Create = 1,
        Update = 2,
        Delete = 3,
        Download = 4,
        Archive = 5,
        Restore = 6
    }

    public override string ToString()
    {
        return string.Format("Name: {0}, Action: {1}, Value: {2}", Name, Action.ToString(), Value);
    }

    public int CompareTo(TestObj obj)
    {
        return this.Action.CompareTo(obj.Action);
    }
}

Creating a list as follows and sorting, outputs in the correct order

       List<TestObj> objs = new List<TestObj>();
       objs.Add(new TestObj("1", Form1.SomeAction.Delete));
       objs.Add(new TestObj("2", Form1.SomeAction.Archive));
       objs.Add(new TestObj("3", Form1.SomeAction.Read));
       objs.Add(new TestObj("4", Form1.SomeAction.Update));
       objs.Add(new TestObj("5", Form1.SomeAction.Create));

       objs.Sort();

       foreach (var item in objs)
       {
           Console.WriteLine(item.ToString());
       }
Barry O'Kane
  • 1,189
  • 7
  • 12
0

Suppose 'items' is the array of your items, use the following approach:

Enumerable.Range(1, items.Length - 1)
    .All(i => (int)(items[i - 1].Action) < (int)(items[i].Action));
omikad
  • 979
  • 8
  • 10
0

You can use a simple for loop to check the conditions are met.

var enums = Enum.GetValues(typeof(SomeAction)).Cast<SomeAction>();
for (int i = 0; i < list.Count; i++)
{
    var listAction = list.ElementAt(i).Action;
    var indexEnumAction = (SomeAction)i;
    Console.WriteLine("{0} == {1} ? {2}", listAction, indexEnumAction, listAction == indexEnumAction);
}

Output:

Read == Read ? True
Create == Create ? True
Update == Update ? True
Delete == Delete ? True
Archive == Download ? False

Fastest way:

var enums = Enum.GetValues(typeof(SomeAction)).Cast<SomeAction>();
for (int i = 0; i < list.Count; i++)
{
    if (list.ElementAt(i).Action != (SomeAction)i)
    {
        return false;
    }
}
return true;
Orel Eraki
  • 11,940
  • 3
  • 28
  • 36