这是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并且没有处理过两次这个集合,有没有办法做到这一点?
答案 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的答案,并进行了一些非常小的调整。
代码:
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"}")
)
);