2010-06-18 3 views
11

Quel est le moyen le plus facile et peu efficace pour convertir une structure plate:Linq: Conversion de la structure à plat hiérarchique

object[][] rawData = new object[][] 
{ 
    { "A1", "B1", "C1" }, 
    { "A1", "B1", "C2" }, 
    { "A2", "B2", "C3" }, 
    { "A2", "B2", "C4" } 
    // .. more 
}; 

dans une structure hiérarchique:

class X 
{ 
    public X() 
    { 
    Cs = new List<string>(); 
    } 
    public string A { get; set; } 
    public string B { get; set; } 
    public List<string> Cs { get; private set; } 
} 

le résultat devrait ressembler à ceci

// pseudo code which describes structure: 
result = 
{ 
    new X() { A = "A1", B = "B1", Cs = { "C1", "C2" } }, 
    new X() { A = "A2", B = "B2", Cs = { "C3", "C4" } } 
} 

De préférence en utilisant des méthodes d'extension de Linq. La classe cible X peut être modifiée (par exemple, un paramètre public pour la liste), uniquement si elle n'est pas possible/utile telle qu'elle est actuellement.

+0

vous besoin d'une sorte de groupe par – Andrey

+0

@Andrey, oui, c'est un groupe par 'A' et' B' et l'agrégation de la 'Cs' dans une liste. –

Répondre

7

pour ce cas particulier:

.GroupBy(x => new { a = x[0], b = x[1] }) 
    .Select(x => new { A = x.Key.a, B = x.Key.b, C = x.Select(c => c[2]) }) 
+0

pour son exemple de hiérarchie: '.Sélectionner (x => nouveau X {A = x.Key.a, B = x.Key.b, Cs = x.Sélectionner (c => c [2]) .ToListe ()}) ' –

+1

Je suis confus à cause du C = x.Select ... Est-ce que ça fonctionne vraiment comme ça? Dois essayer. –

+0

Oui, cela fonctionne, c'est en fait l'utilisation de base de 'GroupBy'. Merci beaucoup. –

2

Quelque chose comme cela devrait fonctionner si la profondeur de votre hiérarchie est limitée (comme dans votre exemple où vous avez seulement trois niveaux A, B et C). J'ai simplifié votre X un peu:

class X { 
    public string A { get; set; } 
    public string B { get; set; } 
    public List<string> Cs { get; set; } 
} 

Ensuite, vous pouvez utiliser GroupBy imbriqués autant de fois que vous avez besoin (en fonction de la profondeur de la hiérarchie). Il serait relativement facile de réécrire ceci dans une méthode récursive (qui fonctionne pour les hiérarchies arbitrairement profondes):

// Group by 'A' 
rawData.GroupBy(aels => aels[0]).Select(a => 
    // Group by 'B' 
    a.GroupBy(bels => bels[1]).Select(b => 
    // Generate result of type 'X' for the current grouping 
    new X { A = a.Key, B = b.Key, 
      // Take the third element 
      Cs = b.Select(c => c[2]).ToList() })); 

Ceci est plus explicite que les autres solutions ici, mais peut-être il sera plus facile à lire comme il est encodage plus simple de l'idée ...

1

Avec les membres de X étant des chaînes et Cs étant un ensemble privé, et rawData étant un tableau de tableaux d'objets, j'ajouter un constructeur à X public X(string a, string b, List<string> cs) puis effectuez ce code

var query = from row in rawData 
      group row by new { A = row[0], B = row[1] } into rowgroup 
      select new X((string)rowgroup.Key.A, (string)rowgroup.Key.B, rowgroup.Select(r => (string)r[2]).ToList()); 

Ceci est sur les données brutes suivantes

object[][] rawData = new object[][] 
    { 
     new object[] { "A1", "B1", "C1" }, 
     new object[] { "A1", "B1", "C2" }, 
     new object[] { "A2", "B2", "C3" }, 
     new object[] { "A2", "B2", "C4" } 
     // .. more 
    }; 
+0

Merci. J'ai implémenté un constructeur comme celui-ci (mais avec un IEnumerable au lieu de la List, en le copiant dans un tableau en interne). La requête semble également bonne, je suis juste plus familier avec les méthodes d'extension. J'ai accepté la réponse d'Andrey parce qu'il était le premier avec la réponse (fondamentalement) correcte. –

0

Je voulais voir si je pouvais écrire ceci sans instances anonymes. Il est pas trop mal:

IEnumerable<X> myList = 
    from raw0 in rawData 
    group raw0 by raw0[0] into g0 
    let g1s = 
    (
    from raw1 in g0 
    group raw1 by raw1[1] 
) 
    from g1 in g1s 
    select new X() 
    { 
    A = g0.Key, 
    B = g1.Key, 
    C = g1.Select(raw2 => raw2[2]).ToList() 
    } 
+0

Solution intéressante. Vous pouvez également utiliser un KeyValuePair pour seulement deux clés. –

+0

@David B: 'g0.Key' ne fonctionne pas. S'il vous plaît aider. – FMFF

Questions connexes