Фильтрация дублирующихся элементов XElements на основе значения атрибута из запроса Linq - PullRequest
2 голосов
/ 22 сентября 2010

Я использую Linq, чтобы попытаться отфильтровать дубликаты XElements, имеющие одинаковое значение для атрибута "name".

Оригинал 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").Value ==
                                      f.Attribute("name").Value)
                          .Count() == 0);

К сожалению, шаг фильтра не работает. Я бы подумал, что фильтр Where () проверит любой элемент перед текущим, имеющий то же имя свойства, и затем включит его в набор, который был больше нуля, тем самым исключая текущий элемент (называемый 'f'), но это не происходит. Мысли?

Ответы [ 3 ]

1 голос
/ 22 сентября 2010

Я думаю, вы должны сначала удалить дубликаты, а , а затем сделать вашу проекцию. Например:

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"));

или, если вы предпочитаете синтаксис метода,

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")));
1 голос
/ 22 сентября 2010

Вы можете просто создать IEqualityComparer для использования с Distinct (), который должен получить то, что вам нужно.

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();
    }
}
1 голос
/ 22 сентября 2010

Ваш подход немного странный, например, вам не нужно проецировать элементы в новые элементы; это просто работает (тм), когда вы добавляете существующие элементы в новый документ.

Я бы просто сгруппировал элементы <property> по атрибуту name, а затем выбрал первый элемент из каждой группы:

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()));
...