Archive
Nested LINQ Aggregate queries vs Nested Foreach statements example
At Trafilm project’s Metadata (http://github.com/zoomicon/Trafilm.Metadata) I have a linked data structure where a Film contains multiple Conversations which contain multiple L3STinstances, which in turn contain multiple L3TTinstances.
I had the need to calculate a list of all unique L2language values of L3TTinstance grand-grand-children for a Film (to keep as calculated metadata for display at the Films PivotViewer collection available at http://gallery.trafilm.net/collection/films.cxml (that is a Collection XML file), which you can explore via HTML5 PivotViewer (seajax) at http://gallery.trafilm.net/film).
BTW, other objects mentioned have their own separate PivotViewer galleries (shown at http://gallery.trafilm.net/conversation, http://gallery.trafilm.net/L3STinstance, http://gallery.trafilm.net/L3TTinstance respectively). Note that the metadata for the respective collections is still being populated at the time of writing, but some sample data is included to try out.
Initially I did it with nested LINQ Aggregate expressions:
L2dubbedLanguages = value.Aggregate(new List<string>(), (total, conversation) =>
(conversation == null) ? total :
conversation.L3STinstances.Aggregate(total, (totalST, l3STinstance) =>
(l3STinstance == null) ? totalST :
l3STinstance.L3TTinstances.Aggregate(total, (totalTT, l3TTinstance) =>
{
if ( (l3TTinstance != null) &&
!string.IsNullOrEmpty(l3TTinstance.L2language) &&
(l3TTinstance.L2mode == "Dubbed") &&
!totalTT.Contains(l3TTinstance.L2language) )
totalTT.Add(l3TTinstance.L2language);
return totalTT; //note that totalTT == totalST == total
}
))).ToArray();
However, since I also had to repeat again all the code block shown above for the “Subtitled” L2languages (just replacing the “Dubbed” string comparison in it), it was more optimal to combine the two in one code block using nested foreach statements that host one switch statement:
//Calculated from Conversations.L3STinstances.L3TTinstances//
IList<string> dubbed = new List<string>();
IList<string> subtitled = new List<string>();
foreach (Conversation conversation in value)
if ((conversation != null) && (conversation.L3STinstances != null))
foreach (L3STinstance l3STinstance in conversation.L3STinstances)
if ((l3STinstance != null) && (l3STinstance.L3TTinstances != null))
foreach (L3TTinstance l3TTinstance in l3STinstance.L3TTinstances)
if (l3TTinstance != null)
{
string l2language = l3TTinstance.L2language;
if (!string.IsNullOrEmpty(l2language))
switch (l3TTinstance.L2mode)
{
case "Dubbed":
if (!dubbed.Contains(l2language))
dubbed.Add(l2language);
break;
case "Subtitled":
if (!subtitled.Contains(l2language))
subtitled.Add(language);
break;
}
}
L2dubbedLanguages = dubbed.ToArray();
L2subtitledLanguages = subtitled.ToArray();
Having done this refactoring, I decided to also change some of the Conversation object’s calculated fields with a LINQ Aggregate:
L3STlanguages = value.Aggregate(new List<string>(),
(total, l3STinstance) => {
if ((next != null) &&
!string.IsNullOrEmpty(l3STinstance.L3STlanguage) &&
!total.Contains(l3STinstance.L3STlanguage))
total.Add(l3STinstance.L3STlanguage);
return total;
}).ToArray();
and which had to be repeated again for the L3STlanguageType field (that is by just changing L3STlanguage to L3STlanguageType in the code block above), to the following nested foreach statements’ code block:
IList<string> languages = new List<string>();
IList<string> languageTypes = new List<string>();
foreach(L3STinstance l3STinstance in L3STinstances)
if (l3STinstance != null)
{
string language = l3STinstance.L3STlanguage;
if (!string.IsNullOrEmpty(language) &&
!languages.Contains(language))
languages.Add(language);string languageType = l3STinstance.L3STlanguageType;
if (!string.IsNullOrEmpty(languageType) &&
!languageTypes.Contains(languageType))
languageTypes.Add(languageType);
}
L3STlanguages = languages.ToArray();
L3STlanguageTypes = languageTypes.ToArray();
That should also avoid any intermediate memory objects overhead that comes with usage of LINQ for Aggregation.