The more I use LINQ the more I like it.  However I’m starting to wonder when it’s too much.

Check out this code below.  My concern is that the other guy on the project won’t have a clue what it’s doing.  See if you can tell what it’s doing before reading the explanation that follows.  I’m no LINQ expert, so any advice on making this simpler and easier to comprehend is appreciated…

   1: public IEnumerabe<DocumentField> Adapt(IEnumerable<ScanMark> marks )
   2: {
   3:     var fieldGroupMarkValues = (from mark in marks
   4:                                 let monikerParts = mark.Name.Split(new char[] {':'}, 2)
   5:                                 let groupName = monikerParts[0]
   6:                                 let markValue = mark.IsMarked ? monikerParts[1] : null
   7:                                 select new
   8:                                            {
   9:                                                FieldGroup = groupName,
  10:                                                Value = markValue
  11:                                            });
  12:     var fieldGroupValues = from markGroup in fieldGroupMarkValues
  13:                      group markGroup by markGroup.FieldGroup
  14:                      into fields
  15:                      let monikerParts = fields.Key.Split(new char[] {'!'}, 2)
  16:                      let fieldName = monikerParts[0]
  17:                      let groupName = monikerParts[1]
  18:                      let rawValue = ( from f in fields where null != f.Value select f.Value ).ToArray()
  19:                      let zeroValue = ( ! rawValue.Any() ? " " : null )
  20:                      let strayValue = 1 < rawValue.Count() ? "*" : null
  21:                      let fieldValue = rawValue.FirstOrDefault()
  22:                          select new
  23:                                     {
  24:                                         FieldName = fieldName,
  25:                                         GroupName = groupName,
  26:                                         Value = zeroValue ?? strayValue ?? fieldValue
  27:                                     };
  28:  
  29:     return from fieldGroup in fieldGroupValues
  30:                       group fieldGroup by fieldGroup.FieldName into fields
  31:                       let fieldName = fields.Key
  32:                       let value = from g in fields select g.Value
  33:                       select new DocumentField()
  34:                                      {
  35:                                          Name = fieldName,
  36:                                          Value = String.Join( "", value.ToArray() )
  37:                                      };
  38:     
  39:  
  40: }        

In a nutshell, the Adapt method transforms a set of ScanMark objects into a set of DocumentField objects.  The tricky part is that there is a hierarchy encoded into the ScanMark.Name property values, which appear similar to this:

FIELDNAME!GROUPNAME:MARKNAME

Each ScanMark belongs to a named group of marks – GROUPNAME in the example above.  The collection of marks within a group determine a single string value for the group.  There can be three valid states:

  1. The group contains no marks (every ScanMark.IsMarked value is false ).  In this case, the group value is a space (“ ”).
  2. The group contains one mark (only one ScanMark.IsMarked value is true ).  In this case, the group value is the name of the selected ScanMark.
  3. The group contains more than one mark (two or more ScanMark.IsMarked values are true).  In this case, the group value is an asterisk (“*”).

Moreover, the value across a set of groups is concatenated into a single named field value (the name being FIELDNAME from the ScanMark.Name property example).

So my thought process is set-based:

  1. Collate marks across all field groups;
  2. Transform field group mark values into a single field group value;
  3. Collate field group values into a single value for the field.

I’m just not convinced the code says this clearly.  Thoughts?