linq to XML: unique attribute value count per group

1.4k views Asked by At

I have got XML nodes as below.

...
<ParentNode>
    <Node id="2343" name="some name" mode="Some Mode">
         //Some child nodes here
    </Node>
    <Node id="2344" name="some other name" mode="Some Mode">
         //Some child nodes here
    </Node>
    ...
</ParentNode>
<ParentNode>
    <Node id="2343" name="some name" mode="Some Other Mode">
         //Some child nodes here
    </Node>
    <Node id="2344" name="some other name" mode="Some Mode">
         //Some child nodes here
    </Node>
</ParentNode>
....

What I need is

id      name             distinct-mode-count
--------------------------------------------
2343    some name        2
2344    some other name  1

I have tried below to get this.

XElement myXML = XElement.Load(filePath);

IEnumerable<XElement> parentNodes = myXML.Descendants("ParentNode");

var nodeAttributes = parentNodes.Select(le => le.Descendants("Node")
    .GroupBy(x => new { 
                          id = x.Attribute("id").Value, 
                          name = x.Attribute("name").Value 
                      }).Select(g => new { 
                          id = g.Key.id, 
                          name = g.Key.name, 
                          distinct_mode_count = // This is where I am stuck
                      }));

I am not sure how to get distinct_mode_count in the above query.

Edit

I need distinct attribute value count for attribute "mode", regardless of which ParentNode they are in.

1

There are 1 answers

4
Jon Skeet On BEST ANSWER

Assuming you want the count of the distinct "mode" attribute values within the nodes with the same ID/name, you just need to project from each element in the group to the mode, then take the distinct sequence of those modes, then count it:

You just need to take the count of the group, and also use SelectMany to "flatten" your parent nodes. (Or just use myXml.Descendants("Node") to start with.)

Short but complete example which gives your desired results:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        XElement myXML = XElement.Load("test.xml");

        IEnumerable<XElement> parentNodes = myXML.Descendants("ParentNode");
        var nodeAttributes = parentNodes
            .SelectMany(le => le.Descendants("Node"))
            .GroupBy(x => new { 
                Id = x.Attribute("id").Value, 
                Name = x.Attribute("name").Value 
            })
            .Select(g => new {
                g.Key.Id,
                g.Key.Name,
                DistinctModeCount = g.Select(x => x.Attribute("mode").Value)
                                     .Distinct()
                                     .Count()
            });
        foreach (var item in nodeAttributes)
        {
            Console.WriteLine(item);
        }
    }
}

Alternatively:

XElement myXML = XElement.Load("test.xml");

var nodeAttributes = myXML
    .Descendants("Node")
    .GroupBy(...)
    // Remaining code as before