English 中文(简体)
Converting an IEnumerable to a lookup with multiple keys per value
原标题:

What s the best way to transform an IEnumerable into a lookup- or dictionary-like structure, but with multiple keys per value?
What I m looking for is something that does roughly the same thing as this, and in a generic way:

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

So the result is a dictionary mapping each letter used to the set of words that contain that letter.
For example, if words contained {"foo", "faz", "zoo"} then the resulting dictionary would contain:

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

I could turn my code example into an extension method, but is there a built-in function or better algorithm to use?

最佳回答

Here s a solution using ToDictionary :

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

Note that it s certainly less efficient than your code, since the words collection is enumerated once to get the distinct letters, then once for each letter...


Update: actually I have a much more efficient suggestion :

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

It should give exactly the same result as your code

问题回答

ToLookup is the extension method you need. For example:

var lookup = (from word in words
              from c in word
              select new { Word = word, Character = c }).ToLookup(x => x.Character, x => x.Word);

Have you considered using a Trie instead?

C# implementation of a Trie

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




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签