I want to make a grouping function, that could be reusable by many others. Here is a concrete example :
I made a function to group by Numero
public static List<Meb> GroupByNumber(this List<Meb> listMeb)
{
return listMeb.GroupBy(x => new { x.Numero })
.Select(cl => new Meb
{
ID = -1,
IdUser = cl.First().IdUser,
Barre = cl.First().Barre,
CNC = cl.First().CNC,
// ... and so long
}).OrderBy(x => x.Numero).ThenBy(x => x.Avancement).ToList();
}
Then another grouping by CNC
public static List<Meb> GroupByCNC(this List<Meb> listMeb)
{
return listMeb.GroupBy(x => new { x.CNC })
.Select(cl => new Meb
{
ID = -1,
IdUser = cl.First().IdUser,
Barre = cl.First().Barre,
CNC = cl.First().CNC,
// ... and so long
}).OrderBy(x => x.Numero).ThenBy(x => x.Avancement).ToList();
}
The problem is in the group function, I have a lot of attributes following (around 40). So, when I want to change the way I want to make grouping (evolution of code), I need to do it in each GroupBy function. (I also can have many sometimes).
So, I'm looking a way I could write the function only once, then call it changing only the GroupBy(x => new { x.Numero })
part.
CodePudding user response:
Enumerable.GroupBy
takes a Func<TSource, TKey>
as keyselector.
Make your function accept such an argument too:
public static <TKey> List<Meb> GroupMeb(
this List<Meb> listMeb,
Func<Meb,TKey> keySelector)
{
return listMeb.GroupBy(keySelector)
.Select(cl => new Meb
{
ID = -1,
IdUser = cl.First().IdUser,
Barre = cl.First().Barre,
CNC = cl.First().CNC,
// ... and so long
}).OrderBy(x => x.Numero).ThenBy(x => x.Avancement).ToList();
}
Usage:
yourList.GroupMeb(x => new { x.CNC })
CodePudding user response:
I see two options.
- Make your own version of
GroupBy()
that also does the mapping and ordering:
public static List<Meb> MyGroupBy<TKey>(
this List<Meb> mebs,
Func<Meb, TKey> groupBy)
=> mebs
.GroupBy(groupBy)
.Select(cl => new Meb
{
// ...and so on
})
.OrderBy(x => x.Numero).ThenBy(x => x.Avancement)
.ToList();
Usage:
myList
.MyGroupBy(x => x.CNC)
- Extract the mapping and ordering into a reusable function and use the standard
GroupBy()
:
public static IEnumerable<Meb> MapAndOrder<TKey>(
this IEnumerable<IGrouping<TKey, Meb>> mebs)
=> mebs
.Select(cl => new Meb
{
// ...and so on
})
.OrderBy(x => x.Numero).ThenBy(x => x.Avancement);
Usage:
myList
.GroupBy(x => x.CNC)
.MapAndOrder()
.ToList()
It's a little more "duplication" but it's also more modular IMHO.
Note that MapAndOrder()
now receives an IEnumerable<IGrouping<TKey, Meb>>
and returns an IEnumerable<Meb>
instead.
Also be aware that there's no need to use new { ... }
in a GroupBy()
when you group by a single property.
You could take this even further by extracting the mapping into a function and creating a helper extension for your ordering:
public Meb MapMebGroup<TKey>(IGrouping<TKey, Meb> g) => new Meb
{
// ...and so on
}
public static IEnumerable<Meb> WithDefaultOrdering(
this IEnumerable<Meb> mebs)
=> mebs.OrderBy(x => x.Numero).ThenBy(x => x.Avancement);
Usage:
myList
.GroupBy(x => x.CNC)
.Select(MapMebGroup)
.WithDefaultOrdering()
.ToList();
Again, this looks like duplication, but in reality you are splitting up your logic into reusable and composable functions/operators.