2010-09-22 10 views
2

J'utilise Linq pour essayer de filtrer les XElements en double qui ont la même valeur pour l'attribut "name".Filtrage de XElements en double basé sur une valeur d'attribut d'une requête Linq

xml d'origine:

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

Malheureusement, l'étape filtre isnt de travail. Je penserais que le filtre de Where() vérifierait n'importe quel élément avant le courant qui a le même nom de propriété et l'inclut alors dans un ensemble qui était plus que zéro, excluant ainsi l'élément actuel (appelé «f»), mais c'est n'arrive pas. Pensées ?

Répondre

1

Vous pouvez simplement créer un IEqualityComparer à utiliser avec le Distinct(), que vous devriez obtenir ce dont vous avez besoin.

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(); 
    } 
} 
+0

Merci, j'aime cette méthode (et je l'ai utilisée) car elle est plus facile à implémenter sans changer ma logique de regroupement. Je suis curieux de savoir pourquoi ma méthode originale n'a pas fonctionné bien. –

1

Votre app est un peu étrange, par exemple, vous n'avez pas besoin de projeter des éléments dans de nouveaux éléments; cela fonctionne juste (tm) quand vous ajoutez des éléments existants à un nouveau document.

Je voudrais simplement regrouper les éléments <property> par l'attribut name puis sélectionnez le premier élément de chaque groupe:

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())); 
+0

La projection est pour une autre partie du code nécessaire. Je dois avoir ce rôle là-dedans. –

1

Je pense que vous devez supprimer les doublons d'abord, et puis faire votre projection. Par exemple:

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

ou, si vous préférez la syntaxe de la méthode,

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

Intéressant. Simple et élégant! –