2

Lets say I have a generic list of the the following objects:

public class Supermarket
{
    public string Brand { get; set; }
    public string Suburb { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
}

So using a List<Supermarket> which is populated with many of these objects with different values I am trying to:

  1. Select the distinct Suburb properties from a superset of Supermarket objects contained in a List<Supermarket> (say this superset contains 20 distinct Suburbs).

  2. Join the Distinct List of Suburbs above to another set of aggregated and counted Suburbs obtained by a LINQ query to a different, smaller list of List<Supermarket>

The distinct items in my superset are:

"Blackheath"
"Ramsgate"
"Penrith"
"Vaucluse"
"Newtown"

And the results of my aggregate query are:

"Blackheath", 50
"Ramsgate", 30
"Penrith", 10

I want to join them to get

"Blackheath", 50
"Ramsgate", 30
"Penrith", 10
"Vaucluse", 0
"Newtown", 0

Here is what I have tried so far:

var results = from distinctSuburb in AllSupermarkets.Select(x => x.Suburb).Distinct()
                select new
                {
                    Suburb = distinctSuburb,
                    Count = (from item in SomeSupermarkets
                            group item by item.Suburb into aggr
                            select new
                            {
                                Suburb = aggr.Key,
                                Count = aggr.Count()
                            } into merge
                            where distinctSuburb == merge.Suburb
                            select merge.Count).DefaultIfEmpty(0)
                } into final
                select final;

This is the first time I have had to post on Stack Overflow as its such a great resource, but I can't seem to cobble together a solution for this.

Thanks for your time

EDIT: OK So I solved this a short while after the initial post. The only thing I was missing was chaining a call to .ElementAtOrDefault(0) after the call to .DefaultIfEmpty(0). I also verifed that using .First() instead of .DefaultIfEmpty(0) as Ani pointed out worked, The correct query is as follows:

var results = from distinctSuburb in AllSupermarkets.Select(x => x.Suburb).Distinct()
                select new
                {
                    Suburb = distinctSuburb,
                    Count = (from item in SomeSupermarkets
                            group item by item.Suburb into aggr
                            select new
                            {
                                Suburb = aggr.Key,
                                Count = aggr.Count()
                            } into merge
                            where distinctSuburb == merge.Suburb
                            select merge.Count).DefaultIfEmpty(0).ElementAtOrDefault(0)
                } into final
                select final;

LASTLY: I ran Ani's code snippet and confirmed that it ran successfully, so both approaches work and solve the original question.

dcarson
  • 2,853
  • 1
  • 25
  • 34

1 Answers1

2

I don't really understand the assumed equivalence between State and Suburb (where distinctSuburb == merge.State), but you can fix your query adding a .First() after the DefaultIfEmpty(0) call.

But here's how I would write your query: using a GroupJoin:

var results = from distinctSuburb in AllSupermarkets.Select(x => x.Suburb).Distinct()
              join item in SomeSupermarkets 
                        on distinctSuburb equals item.Suburb
                        into suburbGroup
              select new
              {
                    Suburb = distinctSuburb,
                    Count = suburbGroup.Count()
              };
dcarson
  • 2,853
  • 1
  • 25
  • 34
Ani
  • 111,048
  • 26
  • 262
  • 307
  • Thanks Ani. I solved the problem with my original approach and fixed the issue you pointed out where I mistakenly confused the State and Suburb properties. – dcarson Jun 06 '12 at 13:43