David Keaveny
David Keaveny

Reputation: 4079

Linq order by sub-sub-collection

I have a collection that starts out as XML returned from an API like this:

<table>
   <row id="1">
      <field id="date">2017-01-02</field>
      <field id="salary">125000</field>
   </row>
   <row id="2">
      <field id="date">2016-01-02</field>
      <field id="salary">130000</field>
   </row>
</table>

The XML is deserialised to typical POCOs:

public class Table
{
   public Row[] Rows { get;set; }
}

public class Row
{
   public Field[] Fields { get;set; }
}

public class Field
{
    public string Id { get;set; }
    public string Value { get;set; }
}

The API makes no promises regarding the order in which rows are returned, and I want to retrieve the row with the most recent date value. If the date was an actual property on a Row object then I could use the technique shown in https://stackoverflow.com/a/27686805/319980, (using the Min LINQ operator to sort the Table.Rows collection by the Date property) but that doesn't appear to be an option here, since date is a value on a child collection.

Assuming that the date field will always be present, can this be done in pure LINQ?

Upvotes: 2

Views: 613

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

You can use OrderByDescending and First operators to obtain the result. Assuming the date field is always present and that it is a valid date you could write it like this:

table.Rows
    .OrderByDescending(r => DateTime.ParseExact(r.Fields.First(f => f.Id == "date").Value, "yyyy-MM-dd", CultureInfo.InvariantCulture))
    .First();

If you can add the morelinq package, you can use the MaxBy operator:

table.Rows
    .MaxBy(r => DateTime.ParseExact(r.Fields.First(f => f.Id == "date").Value, "yyyy-MM-dd", CultureInfo.InvariantCulture));

Upvotes: 2

Gilad Green
Gilad Green

Reputation: 37299

What you can do is order the Rows by the date and then take the first row. To order by the date you should parse the string:

var result = table.Rows.OrderByDescending(r => DateTime.ParseExact(
    r.Fields.FirstOrDefault(f => f.Id == "date")?.Value ?? DateTime.MinValue.ToString("yyyy-MM-dd"),
    "yyyy-MM-dd", 
    CultureInfo.InvariantCulture)).FirstOrDefault();

A more robust version can be to use TryParse for the date instead of just parsing, and in the case of failure returning DateTime.MinValue so row is not chosen

Upvotes: 4

Related Questions