开发者

Filtering out duplicate XElements based on an attribute value from a Linq query

开发者 https://www.devze.com 2023-01-17 17:16 出处:网络
I\'m using Linq to try to filter out any duplicate XElements that have the same value for the \"name\" attribute.

I'm using Linq to try to filter out any duplicate XElements that have the same value for the "name" attribute.

Original xml:

<foo>
<property name="John" value="Doe" id="1" />
<property name="Paul" value="Lee" id="1" />
<property name="Ken" value="Flow" id="1" />
<property name="Jane" value="Horace" id="1" />
<property name="Paul" value="Lee" id="1" />
... other xml properties with different id's
</foo>

// project elements in group into a new XElement
// (this is for another part of the code)
var props = group.data.Select( f => new XElement("property", 
    new XAttribute("name", f.Attribute("name").Value), f.Attribute("value"));

// filter out duplicates
props = props.Where(f => f.ElementsBeforeSelf()
                          .Where(g => g.Attribute("name").Va开发者_如何学运维lue ==
                                      f.Attribute("name").Value)
                          .Count() == 0);

Unfortunately, the filter step isnt working. I would think that Where() filter would check for any element before the current one that has the same property name and then include that in a set that was more than zero, thereby excluding the current element (called 'f'), but thats not happening. Thoughts ?


You could just create an IEqualityComparer to use with the Distinct(), that should get you what you need.

class Program
{
    static void Main(string[] args)
    {
        string xml = "<foo><property name=\"John\" value=\"Doe\" id=\"1\"/><property name=\"Paul\" value=\"Lee\" id=\"1\"/><property name=\"Ken\" value=\"Flow\" id=\"1\"/><property name=\"Jane\" value=\"Horace\" id=\"1\"/><property name=\"Paul\" value=\"Lee\" id=\"1\"/></foo>";

        XElement x = XElement.Parse(xml);
        var a = x.Elements().Distinct(new MyComparer()).ToList();
    }
}

class MyComparer : IEqualityComparer<XElement>
{
    public bool Equals(XElement x, XElement y)
    {
        return x.Attribute("name").Value == y.Attribute("name").Value;
    }

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


Your appoach is a bit weird, e.g., You don't need to project elements into new elements; it just works(tm) when you add existing elements to a new document.

I would simply group the <property> elements by the name attribute and then select the first element from each group:

var doc = XDocument.Parse(@"<foo>...</foo>");

var result = new XDocument(new XElement("foo",
    from property in doc.Root
    group property by (string)property.Attribute("name") into g
    select g.First()));


I think you should remove the duplicates first, and then do your projection. For example:

var uniqueProps = from property in doc.Root
                  group property by (string)property.Attribute("name") into g
                  select g.First() into f
                  select new XElement("property", 
                      new XAttribute("name", f.Attribute("name").Value),
                      f.Attribute("value"));

or, if you prefer method syntax,

var uniqueProps = doc.Root
    .GroupBy(property => (string)property.Attribute("name"))
    .Select(g => g.First())
    .Select(f => new XElement("property", 
                     new XAttribute("name", f.Attribute("name").Value),
                     f.Attribute("value")));
0

精彩评论

暂无评论...
验证码 换一张
取 消