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;
}
-
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– Zeta2018年10月17日 13:01:15 +00:00Commented Oct 17, 2018 at 13:01
-
\$\begingroup\$ Is it better now? \$\endgroup\$Stephen Richardson– Stephen Richardson2018年10月17日 13:52:18 +00:00Commented 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\$Toby Speight– Toby Speight2018年10月17日 14:10:30 +00:00Commented Oct 17, 2018 at 14:10
-
\$\begingroup\$ I updated it again, i don't know how to make it more obvious \$\endgroup\$Stephen Richardson– Stephen Richardson2018年10月17日 15:00:14 +00:00Commented Oct 17, 2018 at 15:00
3 Answers 3
- Temporary result holder,
eventListGrouped
, is not necessary if you are going to return it directly. Eachcase
can justreturn ...
instead of storing value (eventListGrouped = ...
. And, in thedefault
case, you can justreturn 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 ofIGrouping<TKey,TValue>
is different for each item. While it is possible use theIEnumerable<TValue>
interface, but that would mean losing type inference onTKey
. Re-accessingTKey
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
) ofEventListGroup
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 theTKey
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;
}
}
-
\$\begingroup\$ Is the performance of your solution the same as the original? \$\endgroup\$Stephen Richardson– Stephen Richardson2018年10月17日 23:32:00 +00:00Commented 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\$Xiaoy312– Xiaoy3122018年10月18日 01:02:26 +00:00Commented Oct 18, 2018 at 1:02
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);
}
-
\$\begingroup\$ As I've said before: While it is possible use the
IEnumerable<TValue>
interface, but that would mean losing type inference onTKey
. Re-accessingTKey
would require a runtime casting, which could be error prone if you are not careful. \$\endgroup\$Xiaoy312– Xiaoy3122018年10月18日 18:33:34 +00:00Commented 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\$Xiaoy312– Xiaoy3122018年10月18日 18:37:50 +00:00Commented 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\$user73941– user739412018年10月18日 19:51:15 +00:00Commented Oct 18, 2018 at 19:51
You can write a factory to construct EventGroupList:
var groupFunc = new Func<IGrouping<EventListRow>, EventListGroup>(row => ...);
Then just select using the factory:
.Select(groupFunc);
-
5\$\begingroup\$ Can you expand on your example a bit or link me to an example? \$\endgroup\$Stephen Richardson– Stephen Richardson2018年10月17日 15:03:45 +00:00Commented Oct 17, 2018 at 15:03