WARNING: It turns out that although functional, this example does not scale well, performance is O(n²)… as the list size doubles, execution time quadruples… back to the drawing board! For detail read the Performance section at the bottom of the post.

Given a basic set of values (in this case some pets recorded by pet type and name) …

Dog   Cocker Spaniel
Dog   Boxer
Dog   Great Dane
Cat   Persian
Cat   Tabby
Cat   Russian Blue

We want to get the following result …

Cat   Persian,Russian Blue,Tabby
Dog   Boxer,Cocker Spaniel,Great Dane

Here’s one approach using Linq and string.Join.

public class Pet
{
    public string PetType { get; set; }
    public string Name { get; set; }
}

class Program
{
    static void Main()
    {
        var pets = new List<Pet>();

        pets.AddRange(
            new Pet[] {
                new Pet { PetType = "Dog", Name = "Cocker Spaniel" },
                new Pet { PetType = "Dog", Name = "Boxer" },
                new Pet { PetType = "Dog", Name = "Great Dane" },
                new Pet { PetType = "Cat", Name = "Persian" },
                new Pet { PetType = "Cat", Name = "Tabby" },
                new Pet { PetType = "Cat", Name = "Russian Blue" },
            });

        var petQuery = 
            from pet in pets
            group pet by pet.PetType into petGroup
            orderby petGroup.Key
            select new
            {
                PetType = petGroup.Key,
                PetNames = string.Join(",", 
                    pets.Where(p => p.PetType == petGroup.Key)
                        .OrderBy(p => p.Name)
                        .Select(p => p.Name)
                        )
            };

        var result = petQuery.ToList();
    }
}

Performance

The performance of this approach depends on the data. With a small fixed number of PetTypes, but increasing the number of Names, performance is linear (doubling the number of Names doubles execution time). But when you start increasing the number of PetTypes (keeping the number of Names fixed), performance is quadratic (execution time quadruples each time the number of different PetTypes is doubles).

I guess that this makes sense as the complexity of the Where join increases incrementally.