简体   繁体   English

LINQ:按列表字段分组

[英]LINQ: group by a field that is a list

I have a list of Publication objects:我有一个 Publication 对象列表:

public class Publication 
{
    public List<string> Authors { get; set; }
}

I would like to group my publication objects by their authors using LINQ.我想使用 LINQ 按作者对我的发布对象进行分组。 My solution uses a dictionary and looks like this:我的解决方案使用字典,如下所示:

var dict = new Dictionary<string, List<Publication>>();

foreach (var publication in list)
{
    foreach(var author in publication.Authors)
    {
        if (dict.ContainsKey(author))
        {
            dict[author].Add(publication);
        }
        else
        {
            dict.Add(author, new List<Publication> { publication });
        }
    }
}

However this seems ugly to me.然而,这对我来说似乎很丑陋。 There must be a simple and elegant one-liner for this problem, but I can't get it straight.这个问题一定有一个简单而优雅的单线,但我无法直截了当。 I also tried to flatten the list with selectmany on Authors property and group them afterwards but it strips out only the author strings and the publication objects are gone.我还尝试在 Authors 属性上使用 selectmany 将列表展平,然后将它们分组,但它只去除了作者字符串,并且发布对象消失了。

With LINQ you can do like this:使用 LINQ,您可以这样做:

var groupedByAuthor = list
    .SelectMany(publication => 
        publication.Authors.Select(author => new { author, publication }))
    .GroupBy(arg => arg.author, keyValue => keyValue.publication)
    .ToArray();

But this will be a little bit slower.但这会慢一点。

You can use Linq and some anonymous classes:您可以使用 Linq 和一些匿名类:

public class Publication
{
    public List<string> Authors { get; set; }

    public string Title { get; set; }
}

class Program
{
    static void Main()
    {
        IEnumerable<Publication> publications; // YOUR DATA HERE!

        var groupsByAuthor = publications.SelectMany(publication => publication.Authors.Select(author => new { Publication = publication, Author = author })).GroupBy(x => x.Author);

        foreach (var gba in groupsByAuthor)
        {
            Console.WriteLine(gba.Key);
            foreach (var pub in gba)
            {
                Console.WriteLine(pub.Publication.Title);
            }
        }

        Console.ReadLine();
    }
}

If you more concerned about clean and expressive code you may want to try creating an extension method instead brute forcing an ugly linq query.如果您更关心干净和富有表现力的代码,您可能想尝试创建一个扩展方法,而不是强制执行丑陋的 linq 查询。 Code should look something like the following.代码应如下所示。

class Program
{
    static void Main(string[] args)
    {
        List<Publication> test = new List<Publication>();
        Dictionary<string, List<Publication>> publicationsByAuther = test.GroupByAuthor();
    }
}


public static class Extensions
{

    public static Dictionary<string, List<Publication>> GroupByAuther(this List<Publication> publications)
    {
        Dictionary<string, List<Publication>> dict = new Dictionary<string, List<Publication>>();
        foreach (var publication in publications)
        {
            foreach (var author in publication.Authors)
            {
                if (dict.ContainsKey(author))
                {
                    dict[author].Add(publication);
                }
                else
                {
                    dict.Add(author, new List<Publication>
                {
                    publication
                });
                }
            }
        }
        return dict;
    }
}

You might think about separating the case of adding a new author from adding a publication.您可能会考虑将添加新作者与添加出版物的情况分开。 Turning the code into something like that:把代码变成这样:

var dict = new Dictionary<string, List<Publication>>();
foreach (var publication in list)
{
    foreach (var author in publication.Authors)
    {
        if (!dict.ContainsKey(author))
            dict.Add(author, new List<Publication>());
        dict[author].Add(publication);
    }
}

It is not LINQ and not a new solution, but maybe it can help about the elegancy它不是 LINQ 也不是一个新的解决方案,但也许它有助于优雅

You can also do it like this :你也可以这样做:

var dict = pubs.SelectMany(p => p.Authors)
               .ToDictionary(
                   a => a, 
                   a => pubs.Where(p => p.Contains(a)).ToList())
               );

Not tested but you get the idea.没有经过测试,但你明白了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM