2
\$\begingroup\$

I have a report with a group by filter. A stored procedure returns all the data and maps it to IEnumerable<EventListRow>. The code should then group the data as per the user group by selection.

In the following code, only the GroupID and GroupName changes each time. Is there a better way to write the code that avoids duplicating the following code in each case statement:

EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
EventCount = group.Count(),
OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
OeeEventTypeName = group.Max(e => e.OeeEventTypeName)

Or maybe there is just a better way to write it in general. Here is the full method:

public virtual IEnumerable<EventListGroup> GetEventsGrouped(IEnumerable<EventListRow> eventList, GroupByEventData groupBy)
{
 var eventListGrouped = Enumerable.Empty<EventListGroup>();
 switch (groupBy)
 {
 case GroupByEventData.Hour:
 eventListGrouped = eventList
 .GroupBy(e => new { e.StartDateTime.Date, e.StartDateTime.Hour })
 .Select(group => new EventListGroup
 {
 GroupID = group.Min(e => e.StartDateTime.CimToSql()),
 GroupName = group.Min(e => e.StartDateTime.ToString("MMM dd yyyy HH:00tt")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.Day:
 eventListGrouped = eventList
 .GroupBy(e => new { e.Date })
 .Select(group => new EventListGroup
 {
 GroupID = group.Min(e => e.Date.CimToSql()),
 GroupName = group.Min(e => e.Date.ToString("MMM dd yyyy")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.Week:
 eventListGrouped = eventList
 .GroupBy(e => new { Week = e.StartDateTime.GetWeekNumber(), e.StartDateTime.Year })
 .Select(group => new EventListGroup
 {
 GroupID = group.Min(e => e.StartDateTime.CimToSql()),
 GroupName = "Wk " + group.Key.Week + ", " + group.Key.Year,
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.Month:
 eventListGrouped = eventList
 .GroupBy(e => new { e.StartDateTime.Month, e.StartDateTime.Year })
 .Select(group => new EventListGroup
 {
 GroupID = group.Min(e => e.StartDateTime.CimToSql()),
 GroupName = group.Key.Month + "/" + group.Key.Year,
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.Year:
 eventListGrouped = eventList
 .GroupBy(e => new { e.StartDateTime.Year })
 .Select(group => new EventListGroup
 {
 GroupID = group.Min(e => e.StartDateTime.CimToSql()),
 GroupName = group.Key.Year.ToString(),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.Job:
 eventListGrouped = eventList
 .GroupBy(e => e.JobID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.JobName ?? "No Job")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.Product:
 eventListGrouped = eventList
 .GroupBy(e => e.ProductID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.ProductName ?? "No Product")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.System:
 eventListGrouped = eventList
 .GroupBy(e => e.SystemID)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.SystemName ?? "No System")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.EventDefinition:
 eventListGrouped = eventList
 .GroupBy(e => e.EventDefinitionID)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.EventDefinitionName ?? "No Event Definition")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.EventCategory01:
 eventListGrouped = eventList
 .GroupBy(e => e.EventCategory01ID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.EventCategory01Name ?? "Unassigned")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.EventCategory02:
 eventListGrouped = eventList
 .GroupBy(e => e.EventCategory02ID ?? e.EventCategory01ID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e =>
 (e.EventCategory01Name ?? "Unassigned") +
 (e.EventCategory02Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory02Name) : "")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.EventCategory03:
 eventListGrouped = eventList
 .GroupBy(e => e.EventCategory03ID ?? e.EventCategory02ID ?? e.EventCategory01ID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e =>
 (e.EventCategory01Name ?? "Unassigned") +
 (e.EventCategory02Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory02Name) : "") +
 (e.EventCategory03Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory03Name) : "")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.EventCategory04:
 eventListGrouped = eventList
 .GroupBy(e => e.EventCategory04ID ?? e.EventCategory03ID ?? e.EventCategory02ID ?? e.EventCategory01ID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e =>
 (e.EventCategory01Name ?? "Unassigned") +
 (e.EventCategory02Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory02Name) : "") +
 (e.EventCategory03Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory03Name) : "") +
 (e.EventCategory04Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory04Name) : "")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.EventCode:
 eventListGrouped = eventList
 .GroupBy(e => e.EventCodeID ?? -1)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.EventCodeName ?? "Unassigned")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 case GroupByEventData.OeeEventType:
 eventListGrouped = eventList
 .GroupBy(e => e.OeeEventType)
 .Select(group => new EventListGroup
 {
 GroupID = group.Key.ToString(),
 GroupName = group.Max(e => (e.OeeEventTypeName ?? "None")),
 EventDurationSeconds = group.Sum(e => e.EventDurationSeconds),
 EventCount = group.Count(),
 OeeEventTypeColourHex = group.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = group.Max(e => e.OeeEventTypeName)
 });
 break;
 default:
 Log.WriteError(string.Format("Group by: \"{0}\" not coded for", groupBy), "EventService.GetEventsGrouped");
 break;
 }
 return eventListGrouped;
}
t3chb0t
44.6k9 gold badges84 silver badges190 bronze badges
asked Oct 17, 2018 at 12:48
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Welcome to Code Review. The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly. \$\endgroup\$ Commented Oct 17, 2018 at 13:01
  • \$\begingroup\$ Is it better now? \$\endgroup\$ Commented Oct 17, 2018 at 13:52
  • \$\begingroup\$ There's still no information (in the title or in the body) to indicate why you wrote the code, only how it does it. What real-world problem are you addressing here? \$\endgroup\$ Commented Oct 17, 2018 at 14:10
  • \$\begingroup\$ I updated it again, i don't know how to make it more obvious \$\endgroup\$ Commented Oct 17, 2018 at 15:00

3 Answers 3

1
\$\begingroup\$
  • Temporary result holder, eventListGrouped, is not necessary if you are going to return it directly. Each case can just return ... instead of storing value (eventListGrouped = .... And, in the default case, you can just return Enumerable.Empty<...>().
  • Minus the edge default case where you log an error and return an empty enumerable, the rest of code is essentially branching on slightly different group reduce. However, we can't really convert to a dictionary since the key of IGrouping<TKey,TValue> is different for each item. While it is possible use the IEnumerable<TValue> interface, but that would mean losing type inference on TKey. Re-accessing TKey would require a runtime casting, which could be error prone if you are not careful.

    It is easy to notice that last four properties (EventDurationSeconds, EventCount, OeeEventTypeColourHex, OeeEventTypeName) of EventListGroup is initialized the way. And, we can extract these like so:

    EventListGroup CreateEventListGroup(IEnumerable<EventListRow> source, Action<EventListGroup> setup)
    {
     var result = new EventListGroup
     {
     EventDurationSeconds = source.Sum(e => e.EventDurationSeconds),
     EventCount = source.Count(),
     OeeEventTypeColourHex = source.Max(e => e.OeeEventTypeColourHex),
     OeeEventTypeName = source.Max(e => e.OeeEventTypeName)
     };
     setup(result);
     return result;
    }
    

    The first property GroupID seems the same, but the TKey is different in each case.

    With setup used to inject any extra properties that doesn't fit into the observed pattern:

    .Select(group => CreateEventListGroup(group, result =>
    {
     result.GroupID = group.Min(e => e.StartDateTime.CimToSql());
     result.GroupName = group.Min(e => e.StartDateTime.ToString("MMM dd yyyy HH:00tt"));
    }));
    

Full code:

public virtual IEnumerable<EventListGroup> GetEventsGrouped(IEnumerable<EventListRow> eventList, GroupByEventData groupBy)
{
 switch (groupBy)
 {
 case GroupByEventData.Hour:
 return eventList
 .GroupBy(e => new { e.StartDateTime.Date, e.StartDateTime.Hour })
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Min(e => e.StartDateTime.CimToSql());
 result.GroupName = group.Min(e => e.StartDateTime.ToString("MMM dd yyyy HH:00tt"));
 }));
 case GroupByEventData.Day:
 return eventList
 .GroupBy(e => new { e.Date })
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Min(e => e.Date.CimToSql());
 result.GroupName = group.Min(e => e.Date.ToString("MMM dd yyyy"));
 }));
 case GroupByEventData.Week:
 return eventList
 .GroupBy(e => new { Week = e.StartDateTime.GetWeekNumber(), e.StartDateTime.Year })
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Min(e => e.StartDateTime.CimToSql());
 result.GroupName = "Wk " + group.Key.Week + ", " + group.Key.Year;
 }));
 case GroupByEventData.Month:
 return eventList
 .GroupBy(e => new { e.StartDateTime.Month, e.StartDateTime.Year })
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Min(e => e.StartDateTime.CimToSql());
 result.GroupName = group.Key.Month + "/" + group.Key.Year;
 }));
 case GroupByEventData.Year:
 return eventList
 .GroupBy(e => new { e.StartDateTime.Year })
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Min(e => e.StartDateTime.CimToSql());
 result.GroupName = group.Key.Year.ToString();
 }));
 case GroupByEventData.Job:
 return eventList
 .GroupBy(e => e.JobID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.JobName ?? "No Job"));
 }));
 case GroupByEventData.Product:
 return eventList
 .GroupBy(e => e.ProductID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.ProductName ?? "No Product"));
 }));
 case GroupByEventData.System:
 return eventList
 .GroupBy(e => e.SystemID)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.SystemName ?? "No System"));
 }));
 case GroupByEventData.EventDefinition:
 return eventList
 .GroupBy(e => e.EventDefinitionID)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.EventDefinitionName ?? "No Event Definition"));
 }));
 case GroupByEventData.EventCategory01:
 return eventList
 .GroupBy(e => e.EventCategory01ID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.EventCategory01Name ?? "Unassigned"));
 }));
 case GroupByEventData.EventCategory02:
 return eventList
 .GroupBy(e => e.EventCategory02ID ?? e.EventCategory01ID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e =>
 (e.EventCategory01Name ?? "Unassigned") +
 (e.EventCategory02Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory02Name) : ""));
 }));
 case GroupByEventData.EventCategory03:
 return eventList
 .GroupBy(e => e.EventCategory03ID ?? e.EventCategory02ID ?? e.EventCategory01ID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e =>
 (e.EventCategory01Name ?? "Unassigned") +
 (e.EventCategory02Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory02Name) : "") +
 (e.EventCategory03Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory03Name) : ""));
 }));
 case GroupByEventData.EventCategory04:
 return eventList
 .GroupBy(e => e.EventCategory04ID ?? e.EventCategory03ID ?? e.EventCategory02ID ?? e.EventCategory01ID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e =>
 (e.EventCategory01Name ?? "Unassigned") +
 (e.EventCategory02Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory02Name) : "") +
 (e.EventCategory03Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory03Name) : "") +
 (e.EventCategory04Name != null ? (this.GetEventCategoryDelimiter() + e.EventCategory04Name) : ""));
 }));
 case GroupByEventData.EventCode:
 return eventList
 .GroupBy(e => e.EventCodeID ?? -1)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.EventCodeName ?? "Unassigned"));
 }));
 case GroupByEventData.OeeEventType:
 return eventList
 .GroupBy(e => e.OeeEventType)
 .Select(group => CreateEventListGroup(group, result =>
 {
 result.GroupID = group.Key.ToString();
 result.GroupName = group.Max(e => (e.OeeEventTypeName ?? "None"));
 }));
 default:
 Log.WriteError(string.Format("Group by: \"{0}\" not coded for", groupBy), "EventService.GetEventsGrouped");
 return Enumerable.Empty<EventListGroup>();
 }
 EventListGroup CreateEventListGroup(IEnumerable<EventListRow> source, Action<EventListGroup> setup)
 {
 var result = new EventListGroup
 {
 EventDurationSeconds = source.Sum(e => e.EventDurationSeconds),
 EventCount = source.Count(),
 OeeEventTypeColourHex = source.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = source.Max(e => e.OeeEventTypeName)
 };
 setup(result);
 return result;
 }
}
answered Oct 17, 2018 at 18:59
\$\endgroup\$
2
  • \$\begingroup\$ Is the performance of your solution the same as the original? \$\endgroup\$ Commented Oct 17, 2018 at 23:32
  • 1
    \$\begingroup\$ Close to none. To be pedantic, there is certain some performance lose when a method call plus an action invocation is introduced vs inline statements. However, it would be negligible when compared what this code is already doing: grouping and multiple aggregations. \$\endgroup\$ Commented Oct 18, 2018 at 1:02
1
\$\begingroup\$

I like the concept in the answer from Xiaoy312, but in my opinion you should only return one time (at the bottom of the method). So with a little refactoring it could look like:

public virtual IEnumerable<EventListGroup> GetEventsGrouped(IEnumerable<EventListRow> eventList, GroupByEventData groupBy)
{
 Func<EventListRow, object> grouper = null;
 Func<IEnumerable<EventListRow>, EventListGroup, EventListGroup> extraSetter = null;
 EventListGroup CreateGroup(IEnumerable<EventListRow> source)
 {
 return extraSetter(source, new EventListGroup
 {
 EventDurationSeconds = source.Sum(e => e.EventDurationSeconds),
 EventCount = source.Count(),
 OeeEventTypeColourHex = source.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = source.Max(e => e.OeeEventTypeName)
 });
 }
 switch (groupBy)
 {
 case GroupByEventData.Hour:
 grouper = e => new { e.StartDateTime.Date, e.StartDateTime.Hour };
 extraSetter = (source, group) =>
 {
 group.GroupID = source.Min(e => e.StartDateTime.CimToSql());
 group.GroupName = source.Min(e => e.StartDateTime.ToString("MMM dd yyyy HH:00tt"));
 return group;
 };
 break;
 case GroupByEventData.Day:
 grouper = e => new { e.Date };
 extraSetter = (source, group) =>
 {
 group.GroupID = source.Min(e => e.Date.CimToSql());
 group.GroupName = source.Min(e => e.Date.ToString("MMM dd yyyy"));
 return group;
 };
 break;
 // TODO: The rest of the cases
 default:
 Log.WriteError(string.Format("Group by: \"{0}\" not coded for", groupBy), "EventService.GetEventsGrouped");
 return null; // Or throw an exception
 }
 return eventList.GroupBy(grouper).Select(CreateGroup);
}

One of the advantages of this is that you only manipulate eventList in one place, and you can easily change the LINQ-expression, if you want to for instance order it before returning or filter before grouping etc.


Or slightly modified:

public virtual IEnumerable<EventListGroup> GetEventsGrouped(IEnumerable<EventListRow> eventList, GroupByEventData groupBy)
{
 Func<EventListRow, object> grouper = null;
 // Instead of just an IEnumerable<EventListRow> the entire group is provided for the group property setters:
 Func<IGrouping<object, EventListRow>, string> groupIdSetter = null;
 Func<IGrouping<object, EventListRow>, string> groupNameSetter = null;
 EventListGroup CreateGroup(IGrouping<object, EventListRow> source)
 {
 return new EventListGroup
 {
 GroupID = groupIdSetter(source),
 GroupName = groupNameSetter(source),
 EventDurationSeconds = source.Sum(e => e.EventDurationSeconds),
 EventCount = source.Count(),
 OeeEventTypeColourHex = source.Max(e => e.OeeEventTypeColourHex),
 OeeEventTypeName = source.Max(e => e.OeeEventTypeName)
 };
 }
 switch (groupBy)
 {
 case GroupByEventData.Hour:
 grouper = e => new { e.StartDateTime.Date, e.StartDateTime.Hour };
 groupIdSetter = source => source.Min(e => e.StartDateTime.CimToSql());
 groupNameSetter = source => source.Min(e => e.StartDateTime.ToString("MMM dd yyyy HH:00tt"));
 break;
 case GroupByEventData.Day:
 grouper = e => new { e.Date };
 groupIdSetter = source => source.Min(e => e.Date.CimToSql());
 groupNameSetter = source => source.Min(e => e.Date.ToString("MMM dd yyyy"));
 break;
 case GroupByEventData.Week:
 grouper = e => new { Week = e.StartDateTime.GetWeekNumber(), e.StartDateTime.Year };
 groupIdSetter = source => source.Min(e => e.StartDateTime.CimToSql());
 // Here is the first element in the group used to create the group name instead of the anonymous group object:
 groupNameSetter = source => "Wk " + source.First().StartDateTime.GetWeekNumber() + ", " + source.First().StartDateTime.Year;
 break;
 case GroupByEventData.Month:
 grouper = e => new { e.StartDateTime.Month, e.StartDateTime.Year };
 groupIdSetter = source => source.Min(e => e.StartDateTime.CimToSql());
 // Here the group key is casted to dynamic in order to use the properties of the anonymous key directly in the group name:
 groupNameSetter = source => ((dynamic)source.Key).Month + "/" + ((dynamic)source.Key).Year;
 break;
 default:
 Console.WriteLine(string.Format("Group by: \"{0}\" not coded for", groupBy), "EventService.GetEventsGrouped");
 throw new InvalidOperationException("Undefined Group By case");
 }
 return eventList.GroupBy(grouper).Select(CreateGroup);
}
answered Oct 18, 2018 at 8:12
\$\endgroup\$
3
  • \$\begingroup\$ As I've said before: While it is possible use the IEnumerable<TValue> interface, but that would mean losing type inference on TKey. Re-accessing TKey would require a runtime casting, which could be error prone if you are not careful. \$\endgroup\$ Commented Oct 18, 2018 at 18:33
  • \$\begingroup\$ And OP did make use of the key property in the week, month, & year cases. That is why I choose to inline the final selector. \$\endgroup\$ Commented Oct 18, 2018 at 18:37
  • \$\begingroup\$ @Xiaoy312: yes, but you can just use source.First() go create the group id and name, which is safe to use, because a group only exists if there is any elements. \$\endgroup\$ Commented Oct 18, 2018 at 19:51
-2
\$\begingroup\$

You can write a factory to construct EventGroupList:

var groupFunc = new Func<IGrouping<EventListRow>, EventListGroup>(row => ...);

Then just select using the factory:

.Select(groupFunc);
answered Oct 17, 2018 at 14:34
\$\endgroup\$
1
  • 5
    \$\begingroup\$ Can you expand on your example a bit or link me to an example? \$\endgroup\$ Commented Oct 17, 2018 at 15:03

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.