SteveC
SteveC

Reputation: 16813

How can you determine the current items position whilst looping through the collection?

How can you determine the current items position whilst looping through the collection?

I'm working through decision data, grouped by each client, but I have some business logic which depends on the "position" in the set, i.e. 1st, 2nd, 3rd, etc. in conjunction with other properties of the record, e.g. if it's the 3rd decision about a client and their rating in the instance is A then ...

var multiples = from d in context.Decision_Data
                group d by d.Client_No
                    into c
                    where c.Count() > 1
                    select c;

foreach (var grouping in multiples)
{
    foreach (var item in grouping)
    {
        // business logic here for processing each decision for a Client_No
        // BUT depends on item position ... 1st, 2nd, etc.
    }

UPDATE: I appreciate I could put a counter in and manually increment, but it feels wrong and I'd of thought there was something in .NET to handle this ??

Upvotes: 0

Views: 128

Answers (4)

Paciv
Paciv

Reputation: 1487

I fail to see how you can have any certainty about your decisions order. I'm guessing your data come from a long-term data source (e.g. data base or such) and that you doesn't have any control on the order in which the decision are fetched from the data source, especially after applying a "group by".

I would add an "order" field (or column) to the Decision entity to track the order in which the decision were made that would be set while adding the Decision to the data source. That way, you could directly use this field in your business logic.

There must be many ways to achieve the tracking of decision order but without, you can't even be sure in what order they have been made.

Upvotes: 0

jeroenh
jeroenh

Reputation: 26782

Something like this:

foreach (var grouping in multiples)
{
    foreach (var x in grouping.Select(index,item) => new {index, item})
    {
        // x.index is the position of the item in this group
        // x.item is the item itself
    }
}

Side note: you can make the implementation of your LINQ query a bit more efficient. Count() > 1 will completely enumerate each group fully, which you are likely to do in the foreach anyway. Instead you can use Skip(1).Any(), which will stop iterating the group as soon as it finds two items. Obviously this will only make a real difference for (very) large input lists.

var multiples = from d in context.Decision_Data
                group d by d.Client_No
                    into c
                    where c.Skip(1).Any() 
                    select c;

Upvotes: 4

Adam Houldsworth
Adam Houldsworth

Reputation: 64517

There isn't anything offered by the standard foreach. Simply maintain an external count.

There is an overload on the Enumerable.Select extension method that provides the index of the current item:

http://msdn.microsoft.com/en-us/library/bb534869

But without knowing what your code is trying to do in the foreach I cannot really offer an example of using it. In theory you could project an anonymous type that has the index stored and use that later on with the foreach. It appears that jeroenh's answer went down this route.

Upvotes: 3

DerApe
DerApe

Reputation: 3175

As Adam stated, you could either go with Adams solution, or do a ToList() on the query to be able to do

multiples.IndexOf(grouping)

Upvotes: 0

Related Questions