Miguel Moura
Miguel Moura

Reputation: 39484

Convert to Dictionary and Fill missing items

I have a query as follows:

IDictionary<ClassificationLevel, Int32> stats = context.Exams
  .GroupBy(x => x.Classification)
  .Select(x => new { Key = x.Key, Count = x.Count() })
  // ... 

The dictionary ClassificationLevel is has follows:

public enum ClassificationLevel { L1 = 1, L2 = 2, L3 = 3, L4 = 4 }

My problems are:

  1. How to convert the result of the query to IDictionary

  2. The items with Count 0 will not appear in the dictionary. How to make sure those items appear with value 0.

UPDATED

To get the best performance I think the following should be made:

IDictionary<ClassificationLevel, Int32> stats = context.Exams
  .GroupBy(x => x.Classification)
  .ToDictionary(x => new { Key = x.Key, Count = x.Count() });

This would close the EF query ...

Then I would find which keys are missing, e.g. which ClassificationLevel items are missing, and add those keys with value 0.

How should I do this?

Upvotes: 1

Views: 443

Answers (5)

Yuliam Chandra
Yuliam Chandra

Reputation: 14640

With a single linq expression.

var stats = context.Exams
    .GroupBy(x => x.Classification)
    .ToDictionary(x => x.Key, g => g.Count()) // execute the query
    .Union(Enum.GetValues(typeof(ClassificationLevel))
        .OfType<ClassificationLevel>()
        .ToDictionary(x => x, x => 0)) // default empty count
    .GroupBy(x => x.Key) // group both
    .ToDictionary(x => x.Key, x => x.Sum(y => y.Value)); // and sum

Upvotes: 2

dbc
dbc

Reputation: 117230

use Enumerable.ToDictionary() and then Enum.GetValues() to fill in the missing values:

        IDictionary<ClassificationLevel, Int32> dict = context.Exams
            .GroupBy(x => x.Classification)
            .ToDictionary(g => g.Key, g => g.Count());
        foreach (ClassificationLevel level in Enum.GetValues(typeof(ClassificationLevel)))
            if (!dict.ContainsKey(level))
                dict[level] = 0;

Or, if Entity Framework balks at the ToDictionary(), I believe you can do the following:

        IDictionary<ClassificationLevel, Int32> dict = context.Exams
            .GroupBy(x => x.Classification)
            .Select(x => new { Key = x.Key, Count = x.Count() })
            .AsEnumerable()
            .ToDictionary(g => g.Key, g => g.Count);
        foreach (ClassificationLevel level in Enum.GetValues(typeof(ClassificationLevel)))
            if (!dict.ContainsKey(level))
                dict[level] = 0;

Upvotes: 1

Pheonyx
Pheonyx

Reputation: 851

This code allows you to loop around your enum. foreach (ClassificationLevel level in (ClassificationLevel[]) Enum.GetValues(typeof(ClassificationLevel))) { }

You could then put something like the following in the middle of the loop:

if(!stats.KeyExists(level))
{
   stats.Add(level, 0);
}

Upvotes: 0

Tim Schmelter
Tim Schmelter

Reputation: 460278

You could use a "Left Outer Join" in LINQ, after that you can use GroupBy + ToDictionary:

var query = from classification in Enum.GetValues(typeof(ClassificationLevel)).Cast<ClassificationLevel>()
            join exam in context.Exams on classification equals exam.Classification into gj
            from subExam in gj.DefaultIfEmpty()
            select new { classification, exam = subExam };
IDictionary<ClassificationLevel, Int32> stats = query
    .GroupBy(x => x.classification)
    .ToDictionary(g => g.Key, g => g.Count());

Upvotes: 0

thekip
thekip

Reputation: 3768

You could solve it like this

var enumValues = Enum.GetValues(typeof (EnumType)).Cast<EnumType>().ToArray();
Enumerable.Range((int) enumValues.Min(), (int) enumValues.Max()).ToDictionary(
                        x => x.Key,
                        x => context.Exams.Count(e => e.Classification == x)
                        );

Upvotes: 0

Related Questions