Convert List of names to Dictionary, where each entry corresponds to names that begin with a certain letter

I created some method that returns a Dictionary<string, List<Employee>> in this method i loop through a List<Employee> and look for the firstname and add this alphabetical to my Dictionary

Look at the following example:

  • Employees
    • Eijk, Jordy
    • Doe, John
    • Doe, Jane
    • etc.

where the first part is the Lastname and the second part is the Firstname

My method creates a Dictionary like this

  • "D", List ({Doe, John}, {Doe, Jane})
  • "E", List ({Eijk, Jordy})

The method:

public async Task<Dictionary<string, List<Employee>>> GetAllOrderdedByNameAsync()
    {
        var dbList = await _employeeRepository.ListAsync();
        var employees = dbList.Select(FromDb).ToList();
        var empDict = new Dictionary<string, List<Employee>>();
        for (char c = 'A'; c <= 'Z'; c++)
        {
            var list = employees.Where(employee => employee.LastName.StartsWith(c.ToString(), StringComparison.CurrentCultureIgnoreCase)).ToList();
            if (list.Count > 0)
            {
                empDict.Add(c.ToString(), list);
            }
        }
        return empDict;
    }

Now my question... Is there a better way to do this? I will keep the List<Employee> as input and need the Dictionary<string,List<Employee>> as output, so please don't say i need to return something else.

Jon Skeet
people
quotationmark

It sounds like you really want an ILookup<string, Employee> which is precisely a dictionary where each key maps to potentially multiple values. Yes, you can create a Dictionary<string, List<Employee>> but I would strongly advise you not to. You say you "need" the Dictionary<string,List<Employee>> - why?

The lookup code would be:

var lookup = list.ToLookup(x => x.Substring(x.LastName(0, 1)),
                           StringComparer.CurrentCultureIgnoreCase);

To get a dictionary, if you really have a good reason, you can use:

var dictionary = list.GroupBy(x => x.Substring(x.LastName(0, 1)))
                     .ToDictionary(g => g.Key, g => g.ToList(), 
                                   StringComparer.CurrentCultureIgnoreCase);

... but as I say, I would strongly recommend using a type which already represents exactly what you're looking for, in a more concise fashion. (It also has the nice feature of allowing you to look up by any key, and just returning an empty sequence if there are no entries.)

You might also want to consider using char as your key instead of string, given that you're only dealing with a single character - and also consider what you want to happen to last names such as "de Havilland" or ones beginning with an accented character. (In other words, you may want to perform some normalization.)

people

See more on this question at Stackoverflow