3

I have an IEnumerable<RuleSelection> with these properties:

public class RuleSelection{
  public int RuleId { get; set;}
  public int? CriteriaId { get; set; }
  public int? CriteriaSourceId{ get; set; }
}

RuleId in RuleSelection is not unique.

Can I write a linq query to normalize these into IEnumerable<Rule> which would be:

public class Rule{
  public int RuleId { get; set; }
  public IEnumerable<int> Criteria { get; set; }
  public IEnumerable<int> CriteriaSource { get; set; }
}

Rule.RuleId would be unique and the properties Criteria and CriteriaSource would include all the CriteriaId's and CriteriaSourceId's for the RuleId respectively.

adam0101
  • 23,476
  • 18
  • 75
  • 147

3 Answers3

10

It sounds like you want something like:

var rules = selections.GroupBy(rs => rs.RuleId)
                      .Select(g => new Rule {
                                  RuleId = g.Key,
                                  Criteria = g.Select(rs => rs.CriteriaId)
                                              .Where(c => c != null)
                                              .Select(c => c.Value)
                                              .ToList(),
                                  CriteriaSource = g.Select(rs => rs.CriteriaSourceId)
                                                    .Where(c => c != null)
                                                    .Select(c => c.Value)
                                                    .ToList(),
                              });
Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
0

Using my FullOuterGroupJoin extension method

you could:

theRules.FullOuterGroupJoin(theRules,
        r => r.RuleId,
        r => r.RuleId,
        (crit, critSource, id) => new Rule { 
            RuleId = id, 
            Criteria = crit
                .Where(r => r.CriteriaId.HasValue)
                .Select(r => r.CriteriaId.Value),
            CriteriaSource = critSource
                .Where(r => r.CriteriaSourceId.HasValue)
                .Select(r => r.CriteriaSourceId.Value),
        }
    );
Community
  • 1
  • 1
sehe
  • 328,274
  • 43
  • 416
  • 565
0

To write this:

var rules =
    from sel in selections
    group sel by sel.RuleId into rg
    select new Rule {
        RuleId = rg.Key,
        Criteria = rg.Select(r => r.CriteriaId).FilterValues(),
        CriteriaSource = rg.Select(r => r.CriteriaSourceId).FilterValues(),
    };

I created the following FilterValues extension (to eliminate duplication):

public static IEnumerable<T> FilterValues<T>(
    this IEnumerable<T?> items)
    where T : struct
{
    // omitting argument validation
    return 
        from item in items 
        where item.HasValue
        select item.Value;
}

I set out to provide essentially a pure query-syntax version of JonSkeet's answer. I gave up on that in effort to remove duplication for the property assignments and wound up with this combination extension & query-syntax approach.

devgeezer
  • 3,749
  • 1
  • 17
  • 25