Filtering out duplicate XElements based on an attribute value from a Linq query
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")));
精彩评论