Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ - group specific types of classes

Tags:

c#

.net

linq

This question is similar to LINQ group one type of item but handled in a more generic way.

I have a List that has various derived classes. I may have something like this:

List<BaseClass> list = new List<BaseClass>() {
  new Class1(1),
  new Class2(1),
  new Class1(2),
  new Class3(1),
  new Class2(2),
  new Class4(1),
  new Class3(2)
};

I am trying to use LINQ to semi-sort the list so that the natural order is maintained EXCEPT for certain classes which have base.GroupThisType == true. All classes with GroupThisType should be grouped together at the place that the first class of the same type occurs. Here is what the output should be like:

List<BaseClass> list = new List<BaseClass>() {
  new Class1(1),
  new Class1(2),
  new Class2(1),
  new Class3(1),
  new Class3(2)
  new Class2(2),
  new Class4(1),
};

Edit: Oops, forgot to say this result is assuming (Class1 and Class3).GroupThisType == true

like image 447
Nelson Rothermel Avatar asked Nov 06 '22 13:11

Nelson Rothermel


2 Answers

Like this:

list = list.Select((o, i) => new { Index = i * 10000, Value = o })
           .GroupBy(q => q.GetType())
           .SelectMany(g => {
               if (g.First().GroupThisType)
                   return g.Select((q, i) => 
                       new { Index = g.First().Index + i, Value = q.Value }
                   );
               else
                   return g;
           })
           .OrderBy(q => q.Index)
           .Select(q => q.Value)
           .ToList();

The i * 10000 allows up to 10,000 items from a group to be inserted between any two items.

You can replace g.First().GroupThisType with typesToGroup.Contains(g.Key).

like image 195
SLaks Avatar answered Nov 14 '22 23:11

SLaks


Here's a solution using two passes: in the first I build a dictionary of all the ones that should group. In the second I use SelectMany to gather up the elements that don't collate with the collated sequences for the first of any element that does collate.

// Build a dictionary of the items that group
var onesToGroup = list.Where(x => x.GroupThisClass)
                            .GroupBy(x => x.GetType())
                            .ToDictionary(x => x.Key, x => x.AsEnumerable());

var results = list.SelectMany(x => x.GroupThisClass ?
                             (onesToGroup[x.GetType()].First() == x ? onesToGroup[x.GetType()] : (new BaseClass[]{}))
                                                : (new []{x}));
like image 21
Ian Mercer Avatar answered Nov 15 '22 00:11

Ian Mercer