2010-01-12 8 views
2

Quelle est la meilleure façon de transformer un IEnumerable en une structure de type recherche ou dictionnaire, mais avec plusieurs clés par valeur?
Ce que je suis à la recherche est quelque chose qui fait à peu près la même chose que cela, et d'une manière générique:Conversion d'un IEnumerable en une recherche avec plusieurs clés par valeur

var wordsByLetter = new Dictionary<char, HashSet<string>>(); 
foreach (string word in words) 
{ 
    foreach (char letter in word.Distinct()) 
    { 
     if (!wordsByLetter.ContainsKey(letter)) 
     { 
      wordsByLetter.Add(letter, new HashSet<string>()); 
     } 
     wordsByLetter[letter].Add(word); 
    } 
} 

Ainsi, le résultat est une application dictionnaire chaque lettre utilisée pour l'ensemble des mots qui contiennent que lettre.
Par exemple, si words contenait {"foo", "faz", "zoo"} alors le dictionnaire résultant contiendrait:

'a' -> {"faz"} 
'f' -> {"foo", "faz"} 
'o' -> {"foo", "zoo"} 
'z' -> {"faz", "zoo"} 

je pourrais tourner mon exemple de code dans une méthode d'extension, mais est-il une fonction intégrée ou meilleur algorithme à utiliser?

+0

Comment comptez-vous utiliser cette structure de données? Que voulez-vous dire par plusieurs clés par valeur dans ce cas? –

+0

@lpthnc: J'ai modifié la question pour essayer de clarifier. – Whatsit

+0

Je modifie ma réponse après l'avoir acceptée, vous pourriez être intéressé par la nouvelle version ... –

Répondre

5

Voici une solution en utilisant ToDictionary:

var wordsByLetter = 
    words.SelectMany(word => word.ToCharArray()) 
     .Distinct() 
     .ToDictionary(
      letter => letter, 
      letter => words.Where(word => word.Contains(letter))); 

Notez qu'il est certainement moins efficace que votre code, puisque la collection des mots est recensée une fois pour obtenir les lettres distinctes, puis une fois pour chaque lettre ...


Mise à jour: en fait j'ai un muc h suggestion plus efficace:

var wordsByLetter = 
    (from word in words 
    from letter in word 
    group word by letter into grp 
    select new 
    { 
     Letter = grp.Key, 
     Words = new HashSet<string>(grp) 
    }) 
    .ToDictionary(x => x.Letter, x => x.Words); 

Il doit donner exactement le même résultat que votre code

+0

+1 Quand puis-je acheter votre livre sur Linq, merci, – BillW

5

ToLookup est la méthode d'extension dont vous avez besoin. Par exemple:

var lookup = (from word in words 
       from c in word 
       select new { Word = word, Character = c }).ToLookup(x => x.Character, x => x.Word); 
0
// { foo, faz } -> { f|foo, o|foo, f|faz, a|faz, z|faz } 
var pairs = words.SelectMany(w => 
    w.Distinct().Select(l => new { Word = w, Letter = l })); 

var map = pairs.ToLookup(p => p.Letter, p => p.Word);