Reputation: 4575
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
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
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
Reputation: 1499840
Four options:
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