如何使用linq表达式

时间:2018-01-18 23:30:36

标签: c# .net vb.net linq data-structures

这是How to flatten nested objects with linq expression的内容,因为我没有足够的声誉来为讨论添加评论。

我试图将不同类型的树结构拼合成一个列表。

以下是样本模型:

public class Book
{
    public string Name { get; set; }
    public IList<Chapter> Chapters { get; set; }
}

public class Chapter
{
    public string Name { get; set; }
    public IList<Page> Pages { get; set; }
}


public class Page
{
    public string Name { get; set; }
}

样本数据:

Book: Pro Linq 
{ 
   Chapter 1: Hello Linq 
   {
      Page 1, 
      Page 2, 
      Page 3
   },
   Chapter 2: C# Language enhancements
   {
      Page 4
   },
   Chapter 3: Glossary
   {
   }
}
Book: Pro Linq II
{
}

所需的平面输出:

"Pro Linq", "Hello Linq", "Page 1"
"Pro Linq", "Hello Linq", "Page 2"
"Pro Linq", "Hello Linq", "Page 3"
"Pro Linq", "C# Language enhancements", "Page 4"
"Pro Linq", "Glossary", null
"Pro Linq II", null, null

如果没有concat并且没有处理过两次这个集合,有没有办法做到这一点?

4 个答案:

答案 0 :(得分:1)

我猜有一种更简洁的方法可以实现这一点,但以下内容将根据输入获得所需的输出。我使用null合并运算符将空值转换为字符串"null"以便于打印,您可以轻松地将它们保留为空。

var result =
from book in books
from chapter in (book?.Chapters?.DefaultIfEmpty(new Chapter()) ?? Enumerable.Repeat<Chapter>(new Chapter(),1))
from page in (chapter?.Pages?.DefaultIfEmpty(new Page()) ?? Enumerable.Repeat<Page>(new Page(),1))
select new {Book = book?.Name ?? "null", Chapter = chapter?.Name ?? "null", Page = page?.Name ?? "null"};

输出

Pro Linq, Hello Linq, Page 1
Pro Linq, Hello Linq, Page 2
Pro Linq, Hello Linq, Page 3
Pro Linq, C# Language Enhancements, Page 4
Pro Linq, Glossary, null
Pro Linq II, null, null

此外,如果其他用户想要在不自行构建的情况下进行实验,请使用问题中提供的类型输入。

var books = new List<Book> {
    new Book{
        Name = "Pro Linq",
        Chapters = new List<Chapter>{
            new Chapter {
                Name = "Hello Linq",
                Pages = new List<Page> {
                    new Page {Name = "Page 1"},
                    new Page {Name = "Page 2"},
                    new Page {Name = "Page 3"}
                }
            },
            new Chapter {
                Name = "C# Language Enhancements",
                Pages = new List<Page> {
                    new Page {Name = "Page 4"}
                }
            },
            new Chapter {
                Name = "Glossary"
            }
        }
    },
    new Book {
        Name = "Pro Linq II"
    }
};

答案 1 :(得分:0)

虽然不漂亮但确实产生了预期的结果。

使用SelectMany和一些内联if语句来检查null。

var flattenedList = books.SelectMany(book =>
    book.Chapters != null && book.Chapters.Count > 0
     ? book.Chapters.SelectMany(chapter =>
         chapter.Pages != null && chapter.Pages.Count > 0
            ? chapter.Pages.Select(page => string.Join(", ", book.Name, chapter.Name, page.Name))
            : new[] { string.Join(", ", book.Name, chapter.Name, "null") }
    )
    : new[] { string.Join(", ", book.Name, "null", "null") }
).ToList();

答案 2 :(得分:0)

这个答案基于Nkosi的答案,并进行了一些非常小的调整。

  • 使用Any()
  • 否ToList()
  • Func添加并用于用引号
  • 包围文本

代码:

Func<string, string> Quote = (s) => "\"" + s + "\"";
var result = books.SelectMany(book =>
    book.Chapters != null && book.Chapters.Any()
     ? book.Chapters.SelectMany(chapter =>
         chapter.Pages != null && chapter.Pages.Any()
            ? chapter.Pages.Select(page => string.Join(", ", Quote(book.Name), Quote(chapter.Name), Quote(page.Name)))
            : new[] { string.Join(", ", Quote(book.Name), Quote(chapter.Name), "null") }
    )
    : new[] { string.Join(", ", Quote(book.Name), "null", "null") }
);

这会产生:

"Pro Linq", "Hello Linq", "Page 1"
"Pro Linq", "Hello Linq", "Page 2"
"Pro Linq", "Hello Linq", "Page 3"
"Pro Linq", "C# Language Enhancements", "Page 4"
"Pro Linq", "Glossary", null
"Pro Linq II", null, null

答案 3 :(得分:0)

使用字符串扩展名进行引用会使其更清晰:

public static class Ext {
    public static string Quoted(this string text) => $"\"{text}\"";
}

您需要将null项替换为返回单个空项的IEnumerable<>,如下所示:

var ans = myBooks.SelectMany(b => (b.Chapters == null ? new[] { (Chapter)null } : b.Chapters)
                                      .SelectMany(c => (c?.Pages == null ? new[] { (Page)null } : c.Pages)
                                                           .Select(p => $"{b.Name.Quoted()}, {c?.Name?.Quoted() ?? "null"}, {p?.Name?.Quoted() ?? "null"}")
                                                 )
                            );