views:

126

answers:

1

Problem

I have a collection of XDocument instances. Each document has a repeated element that can take a different value. I want to group them by this value but each element can specify a different value.

<sampledoc>
  <value>a</value>
  <value>b</value>
  <value>c</value>
</sampledoc>

Example

  • Document A has values a, b, c
  • Document B has values b, c, d
  • Document C has values a, b

I want a grouping that is:

  • group a
    • Document A
    • Document C
  • group b
    • Document A
    • Document B
    • Document C
  • group c
    • Document A
    • Document B
  • group d
    • Document B

Question

I'm sure I must be able to do this but I can't see the wood for the trees right now.

docs.GroupBy... won't work on it's own (as far as I can tell) because the expression it takes should return a single value, and each document can contain multiple values. My head says a single LINQ query should be possible, but it can't fathom what it would be.

Can this be done using the GroupBy or AsLookup LINQ methods? Is there a way to do this?

I'd prefer examples in C# if anyone would be willing to provide one.

Update

Thanks to the answer from Pavel Minaev and a little inspiration, I solved this as follows:

// Collate all the different values
docs.SelectMany(doc => doc.Elements("Value")
                          .Select(el => el.Value))
    // Remove duplicate values
    .Distinct()
    // Generate a lookup of specific value to all
    // documents that contain that value
    .ToLookup(v => v, v => docs.Where(doc => doc.Elements("Value")
                                                .Any(el=>el.Value == v)));
+2  A: 

GroupBy won't help you here anyway, because it assigns every element in the sequence to only one group.

var docs = new XDocument[] { docA, docB, docC } ;
var result = docs
   .SelectMany(doc => doc.Root.Elements("Value"))
   .Select(el => el.Value)
   .Distinct()
   .Select(key => new {
        Key = key,
        Documents = docs.Where(doc =>
            doc.Root.Elements("Value").Any(el => el.Value == key))
   });
Pavel Minaev
Thanks. I quickly came to that realisation about GroupBy but I was struggling. I've just come up with the exact same thing as the first part of your query, but that second part was proving elusive as I was trying to get the whole IGrouping thing working. Perhaps I can create an ILookup where you have a Select call.
Jeff Yates