简体   繁体   中英

C# Check List is in correct order by comparing property to enum

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.

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.

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;
}

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

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));

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());
       }

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));

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM