One To Many LINQ Joins Across Nested Collections
I have a development scenario where I am joining two collections with
Linq; a single list of column header objects which contain presentation
metadata, and an enumeration of kv dictionaries which result from a web
service call. I can currently iterate (for) through the dictionary
enumeration, and join the single header list to the current kv dictionary
without issue. After joining, I emit a curated array of dictionary values
for each iteration.
What I would like to do is eliminate the for loop, and join the single
header list directly to the entire enumeration. I understand the 1-to-1
collection join pretty well, but the 1-to-N syntax is eluding me.
Details
I have the following working method:
public void GetQueryResults(DataTable outputTable)
{
var odClient = new ODataClient(UrlBase);
var odResponse = odClient.FindEntries(CommandText);
foreach (var row in odResponse)
{
var rowValues = OutputFields
.Join(row, h => h.Key, r => r.Key,
(h, r) => new { Header = h, ResultRow = r })
.Select(r => r.ResultRow.Value);
outputTable.Rows.Add(rowValues.ToArray());
}
}
odResponse contains IEnumerable<IDictionary<string, object>>; OutputFields
contains IList<QueryField>; the .Join produces an enumeration of anons
containing matched field metadata (.Header) and response kv pairs
(.ResultRow); finally, the .Select emits the matched response values for
row consumption. The OutputField collection looks like this:
class QueryField
{
public string Key { get; set; }
public string Label { get; set; }
public int Order { get; set; }
}
Which is declared as:
public IList<QueryField> OutputFields { get; private set; }
By joining the collection of field headers to the response rows, I can
pluck just the columns I need from the response. If the header keys
contain { "size", "shape", "color" } and the response keys contain {
"size", "mass", "color", "longitude", "latitude" }, I will get an array of
values for { "size", "shape", "color" }, where shape is null, and the
mass, longitude, and latitude values are ignored. For the purposes of this
scenario, I am not concerned with ordering. This all works a treat.
Problem
What I'd like to do is refactor this method to return an enumeration of
value array rows, and let the caller manage the consumption of the data:
public IEnumerable<string[]> GetQueryResults()
{
var odClient = new ODataClient(UrlBase);
var odResponse = odClient.FindEntries(CommandText);
var responseRows = //join OutputFields to each row in odResponse by .Key
return responseRows;
}
Followup Question
Would a Linq-implemented solution for this refactor require an immediate
scan of the enumeration, or can it pass back a lazy result? The purpose of
the refactor is to improve encapsulation without causing redundant
collection scans. I can always build imperative loops to reformat the
response data the hard way, but what I'd like from Linq is something like
a closure.
Thanks heaps for spending the time to read this; any suggestions are
appreciated!
No comments:
Post a Comment