0

Is there a way to preserve the order after this linq expression?

var results =
  DateList
    .GroupBy(x => x.Date.Subtract(firstDay).Days / 7 + 1)
     .SelectMany(gx => gx, (gx, x) => new {Week =  gx.Key,DateTime =x,Count = gx.Count(),});

I found this Preserving order with LINQ , but I'm not sure if its the GroupBy or SelectMany casing the issues

Community
  • 1
  • 1
Sewder
  • 754
  • 3
  • 11
  • 37
  • `GroupBy` retains order ([link](https://msdn.microsoft.com/library/bb534304(v=vs.100).aspx)) and `SelectMany` only iterates over the set ([link](https://msdn.microsoft.com/library/bb534336(v=vs.100).aspx)) so I don't see why it would change. Are you certain no order is preserved? – Jeroen Vannevel Mar 10 '16 at 01:52
  • 2
    @JeroenVannevel Think about what you are saying: Group the following sequence into odd and even numbers: [1,1,2,2,3,3,4,4,5,5,6,6] so now you have [1,1,3,3,5,5],[2,2,4,4,6,6] (yes, order was preserved *within each group*)... Now SelectMany on those groups and you'll get [1,1,3,3,5,5,2,2,4,4,6,6] (once again order was preserved)... But in combining the 2, clearly order has not been preserved. – spender Mar 10 '16 at 01:59
  • Thanks for the explanation @spender – Sewder Mar 10 '16 at 17:53

2 Answers2

2

Yes, if you first select your DateList and combine it with an index, using an overload of .Select that uses a delegate with a second (int) parameter that is called with the index of the items from the sequence :

DateList
    .Select((dateTime, idx) => new {dateTime, idx})
    .GroupBy(x => x.dateTime.Date.Subtract(firstDay).Days / 7 + 1)

...and persist the value through the linq chain

    .SelectMany(gx => gx, (gx, x) => new {Week =  gx.Key,
                                          DateTime = x.dateTime, 
                                          Count = gx.Count(), 
                                          x.idx})

...then use it to re-order the output

    .OrderBy(x => x.idx)

...and strip it from your final selection

    .Select(x => new {x.Week, x.DateTime, x.Count});

then you can maintain the same order as the original list.

spender
  • 117,338
  • 33
  • 229
  • 351
  • I got a blank result. What I do is run through this : var row2 = results.ElementAt(ii); row["Week"] = row2.Week; row["Count"] = row2.Count; row["DateTime2"] = row2.DateTime; To add each value to a datagridview. – Sewder Mar 10 '16 at 02:02
  • @Sewder I can't help you through a problem described so vaguely in a comment. – spender Mar 10 '16 at 02:04
  • @loneshark99 Comments aren't a great place to ask new questions. Feel free to [ask a new question](https://stackoverflow.com/questions/ask) and ping me here if you'd like me to take a look. – spender Aug 21 '18 at 20:52
0

Solution of @spender is good, but can it be done without OrderBy? It can, because we can use the index for direct indexing into array, but it would not be one linq query:

var resultsTmp =
    DateList.Select((d, i) => new { d, i })
    .GroupBy(x => x.d.Date.Subtract(firstDay).Days / 7 + 1)
    .SelectMany(gx => gx, (gx, x) => new { Week = gx.Key, DateTime = x.d, Count = gx.Count(), x.i })
    .ToArray();

var resultsTmp2 = resultsTmp.ToArray();
foreach (var r in resultsTmp) { resultsTmp2[r.i] = r; };
var results = resultsTmp2.Select(r => new { r.Week, r.DateTime, r.Count });

It looks a bit complex. I would probably do something more straightforward like:

var DateList2 = DateList.Select(d => new { DateTime = d, Week = d.Subtract(firstDay).Days / 7 + 1 }).ToArray();
var weeks = DateList2.GroupBy(d => d.Week).ToDictionary(k => k.Key, v => v.Count());
var results = DateList2.Select(d2 => new { d2.Week, d2.DateTime, Count = weeks[d2.Week] });
Antonín Lejsek
  • 6,003
  • 2
  • 16
  • 18