Skitterm
Skitterm

Reputation: 4575

Order a list but still be able to access index and fields

I am trying to group a list of objects by a certain field.

Later on, I need to use this list as follows:

for(i=0; i< list.Count; i++)
{
   if(i == 0)
    continue;

   if (list[i].property != list[i-1].property)
   dosomething;
}

I need to do a for loop so I can say "if the property of the item right before this one was different...

The problem comes when I try to order the list. If I do list.GroupBy, I am unable to access the list items' members. If I do list.OrderBy, I have to set it to a ienumerable, and thereby can't use a for loop because ienumerables don't work with list indexes.

Is there a way to implement this functionality in a foreach loop (without referencing the index)? If not, is there another way to order my list that doesn't require it to be set to an ienumerable?

Upvotes: 2

Views: 99

Answers (3)

paparazzo
paparazzo

Reputation: 45096

This is just a repeat of one of the options from Skeet. Give him the check.

ForEach does not require an index. Cannot delete an item from the list with ForEach.

class Program
{
    static void Main(string[] args)
    {
        List<SortMe> SortMes = new List<SortMe>();

        SortMes.Add(new SortMe("aaa"));
        SortMes.Add(new SortMe("bbb"));
        SortMes.Add(new SortMe("ccc"));
        SortMes.Add(new SortMe("aaa"));
        SortMes.Add(new SortMe("bbb"));
        SortMes.Add(new SortMe("ccc"));
        SortMes.Add(new SortMe("ccc"));
        SortMes.Add(new SortMe("bbb"));
        SortMes.Add(new SortMe("aaa"));
        SortMes.Add(new SortMe("ccc"));
        SortMes.Add(new SortMe("bbb"));
        SortMes.Add(new SortMe("aaa"));

        SortMe SortMeLast = null;

        foreach (SortMe SortMeCurrent in SortMes.OrderBy(x => x.Name))
        {
            Console.WriteLine("   Current     " + SortMeCurrent.Name);
            if (SortMeLast != null)
            {
                if (SortMeCurrent.Name != SortMeLast.Name)
                {
                    // do something
                    Console.WriteLine("Do Something");
                }
            }
            SortMeLast = SortMeCurrent;
        }
        Console.ReadLine();
    }
}
public class SortMe
{
    public string Name { get; set; }
    public SortMe(string name) { Name = name; } 
}

Upvotes: 0

Matt Klein
Matt Klein

Reputation: 8424

You can define a myitem previous just before the loop (where myitem is of the same type in your list) and then at the end of each list, set previous to the current item. If you do this, make sure to check previous for null since it won't have a value the first time around.

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1499840

Four options:

  • Use OrderBy and then ToList to get a sorted list. You can then use your existing code, although I'd recommend starting the loop at 1 rather than starting at 0 and breaking immediately in the first iteration.
  • Use OrderBy and then Zip:

    var ordered = input.OrderBy(...);
    var zipped = ordered.Zip(ordered.Skip(1),
                             (Previous, Current) => new { Previous, Current });
    
    foreach (var pair in zipped)
    {
        if (pair.Previous.Property == pair.Current.Property)
        {
            ... Whatever you want to do
        }
    }
    

    Note that this will sort the input twice, unfortunately. There's no built-in way of avoiding that (without calling ToList or whatever). It wouldn't be too hard to write a "self-zip" extension method, admittedly.

  • Use OrderBy and keep a "previous property":

    var ordered = input.OrderBy(...);
    // This is like using foreach, but with a simple way to get the
    // first item
    using (var iterator = ordered.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            return; // Or whatever. No items!
        }
        var previousProperty = iterator.Current.Property;
        while (iterator.MoveNext())
        {
            var currentProperty = iterator.Current.Property;
            if (previousProperty == currentProperty)
            {
                ... Whatever you want to do
            }
            previousProperty = currentProperty;
        }
    }
    
  • Sort the list in-place (if that doesn't mess anything else up) using List<T>.Sort

Upvotes: 3

Related Questions