marynarz
marynarz

Reputation: 43

How to distinct groups of values?

I am trying to build a query that select unique groups of values. I know how to group values and select distinct values, but I don't see how to select distinct groups of values regarless of the order of the elements considered. More precisely:

My XML is something like this:

<A>
    <B type="1">value1</prop>
    <B type="2">value2</prop>
    <B type="3">value3</prop>
    <B type="4">value4</prop>
<C>
    <D>XXX</D>
</C>
<C>
    <D>YYY</D>
</C>
</A>

<A>
    <B type="3">value3</prop>
    <B type="4">value4</prop>
    <B type="2">value2</prop>
<C>
    <D>XXX</D>
</C>
<C>
    <D>YYY</D>
</C>
</A>

Here I would like to make groups of <B> values from all <A> nodes and select only unique sets of values. For instance, here:

value1+value2+value3+value4

and

value2+value3+value4+value1

These two sets of values should be only unique if some or all of the values are different.

Thank you very much in advance for your help.

Upvotes: 3

Views: 784

Answers (2)

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174329

The following works:

var doc = XDocument.Parse(xml);

var comparer = new ElementsComparer();

var result = doc.Descendants("B")
                .GroupBy(x => x.Parent)
                .Distinct(comparer);

class ElementsComparer : IEqualityComparer<IGrouping<XElement, XElement>>,
                         IEqualityComparer<XElement>
{
    public bool Equals(XElement lhs, XElement rhs)
    {
        return lhs.Value.Equals(rhs.Value);
    }

    public bool Equals(IGrouping<XElement, XElement> lhs,
                       IGrouping<XElement, XElement> rhs)
    {
        var x = lhs.OrderBy(a => a.Value);
        var y = rhs.OrderBy(a => a.Value);
        return x.SequenceEqual(y, this);
    }

    public int GetHashCode(XElement obj)
    {
        return obj.Value.GetHashCode();
    }

    public int GetHashCode(IGrouping<XElement, XElement> obj)
    {
        return 0;
    }
}

This returns distinct groups. A group is seen as the same as another group if the elements in the group have the same values as the elements in the other group.
Unfortunatelly, we need a full blown IEqualityComparer implementation to achieve this.

Upvotes: 2

Jan P.
Jan P.

Reputation: 3297

Try

var xDocument = XDocument.Parse(/*Your XML here*/);
var vals =
    //Find all A-Elements
    xDocument.Root.Elements("A")
        //Select all their B-Elements to one collection
        .SelectMany(a => a.Elements("B"))
        //Group the B-Items by their value
        .GroupBy(element => element.Value)
        //Select the keys of the IGrouping<TValue, XElement>
        .Select(element => element.Key)
        //Distinct them
        .Distinct();

Upvotes: 0

Related Questions