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;
}
\$\endgroup\$
  • 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\$ – Zeta Oct 17 '18 at 13:01
  • \$\begingroup\$ Is it better now? \$\endgroup\$ – Stephen Richardson Oct 17 '18 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\$ – Toby Speight Oct 17 '18 at 14:10
  • \$\begingroup\$ I updated it again, i don't know how to make it more obvious \$\endgroup\$ – Stephen Richardson Oct 17 '18 at 15:00
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;
    }
}
\$\endgroup\$
  • \$\begingroup\$ Is the performance of your solution the same as the original? \$\endgroup\$ – Stephen Richardson Oct 17 '18 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\$ – Xiaoy312 Oct 18 '18 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);
}
\$\endgroup\$
  • \$\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\$ – Xiaoy312 Oct 18 '18 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\$ – Xiaoy312 Oct 18 '18 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\$ – Henrik Hansen Oct 18 '18 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);
\$\endgroup\$
  • 5
    \$\begingroup\$ Can you expand on your example a bit or link me to an example? \$\endgroup\$ – Stephen Richardson Oct 17 '18 at 15:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.