在列表中查找对象的并集(复合对象/嵌套列表)
本文关键字:对象 列表 复合 嵌套 查找 | 更新日期: 2023-09-27 17:58:53
(我重写了这个问题,试图更好地提供我要做的事情的例子。如果这仍然是一个令人困惑的问题,我很抱歉…)
以什么方式可以找到作为其他对象的属性包含的n个对象集的并集?
以以下为例:
class Author
{
public string FirstName;
public string LastName;
}
class Magazine
{
public string Title;
public string ISBN;
public List<Article> TOC;
}
class Article
{
public string Title;
public string Header;
public string Body;
public int PageNumber;
public Magazine Source;
public Author Author;
}
- 我得到了一个包含
List<Article>
的List<Magazine>
- 文章对象有一个杂志和作者对象作为属性(文章可能以1:N的比例存在于杂志中——作为副本或再版)
- 事实上,我把每一篇文章都当作一个独立的对象。我不在乎给我的
List<Magazine>
。我被要求提供List<Author>
- 在收到这份名单之前,我对可能的文章或作者有NO的了解
我得到的数据是这样的(在树状布局中)。
Magazine01
Article01 (Title, Header, ... Magazine01, Author01)
Article02 (Title, Header, ... Magazine01, Author02)
Article03 (Title, Header, ... Magazine01, Author03)
Magazine02
Article04 (Title, Header, ... Magazine02, Author04)
Article05 (Title, Header, ... Magazine02, Author05)
Article06 (Title, Header, ... Magazine02, Author04)
Magazine03
Article07 (Title, Header, ... Magazine03, Author03)
Article08 (Title, Header, ... Magazine03, Author02)
Article09 (Title, Header, ... Magazine03, Author01)
我被要求提供的数据看起来是这样的(在树视图布局中)。
Author01
Article01 (Title, Header, ... Magazine01, Author01)
Article09 (Title, Header, ... Magazine03, Author01)
Author02
Article02 (Title, Header, ... Magazine01, Author02)
Article08 (Title, Header, ... Magazine03, Author02)
Author03
Article03 (Title, Header, ... Magazine01, Author03)
Article07 (Title, Header, ... Magazine03, Author03)
Author04
Article04 (Title, Header, ... Magazine02, Author04)
Article06 (Title, Header, ... Magazine02, Author04)
Author05
Article05 (Title, Header, ... Magazine02, Author05)
- 我得到的
List<Magazine>
没有特别的顺序 - 我还没有尝试对输出进行排序,但这将是很好的
- 可能有n本杂志,每一期都有n本文章
- 因此,一个作者可能有n篇文章
- 当我收到杂志的对象时,我正在将文章聚合到
List<Article>
中(但我并没有下定决心继续这样做,这似乎是个好主意…)
除了下面我的(可耻的)"减员"方法之外,我还能通过什么方式减少文章列表中的循环:
List<Magazine> magazineList; // Magazines contain a list of Articles which contain an Author.
List<Article> articleList; // As each Magazine is created, a summary list of the Articles is recorded here (I realize that this is probably unnecessary, but that is an aside to my problem)
Bag<Author> authorBag;
for (i = 0; i < (Magazines.Count - 1); i++)
{
BuildAuthorBag();
}
ParseArticleList();
//...
BuildAuthorBag()
{
// Finds occurrences of Author in the Article list of each Magazine.
// Limits the inclusion of Author based on preferences ( i > 1, 2, etc. )
}
ParseArticleList()
{
// I clone the articleList (i.e. tmpArticleList)
// I create two lists of Article type. One will be the new leaf nodes. The second is a swap list.
// Using the authorBag...
// In a loop...
// I create the new parent node from the current Author in the bag.
// Then, find occurrences of the Author in the tmpArticleList adding that Article as a child node to the parent node.
// or...
// I place them into the second list to be swapped for the tmpArticleList before the next loop.
}
据我所知,您需要这样的东西。(我使用的是VS2010和C#4,但任何支持LINQ的版本都应该这样做)。
using System;
using System.Collections.Generic;
using System.Linq;
namespace SO6299404
{
class Author
{
public string FirstName;
public string LastName;
}
class Magazine
{
public string Title;
public string ISBN;
public List<Article> TOC;
// Code Until the end of the class is for testing only
public void Dump()
{
Dump(0);
}
public void Dump(int indent)
{
Console.WriteLine("".PadLeft(indent) + Title);
if (TOC != null)
{
foreach (Article article in TOC)
{
article.Dump(indent + 2);
}
}
}
}
class Article
{
public string Title;
public string Header;
public string Body;
public int PageNumber;
public Magazine Source;
public Author Author;
// Code Until the end of the class is for testing only
public void Dump()
{
Dump(0);
}
public void Dump(int indent)
{
string author = Author == null ? "" : Author.FirstName;
Console.WriteLine("".PadLeft(indent) + Title + ", " + author);
}
}
// A foreach extension. Not strictly nescessary, but having this extension
// makes it a bit easier to write foreach loops
public static class EnumerableExtension
{
public static void Foreach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
if (enumerable == null) throw new ArgumentNullException("enumerable");
if (action == null) throw new ArgumentNullException("action");
foreach (T value in enumerable)
action(value);
}
}
class Program
{
static void Main()
{
List<Magazine> magazines = SetupTestCase();
// Let's print out our test case
Console.WriteLine("==[Setup]===========================");
magazines.Foreach(x => x.Dump());
// So now we need to extraxct all Authors and list articles belonging to them
// First we get Authorl; Article pair and then we group by Authors
var temp = magazines.SelectMany(m => m.TOC, (a, b) => new {b.Author, Article = b}).GroupBy(x => x.Author);
// Ok, we are done. Let's print out the results
Console.WriteLine("==[REsult]===========================");
temp.Foreach(x =>
{
Console.WriteLine(x.Key.FirstName);
x.Foreach( y => y.Article.Dump(2));
});
}
// The code from here until the end of the class is for generating a test case only
private static List<Magazine> SetupTestCase()
{
// Let's set up a test case similar to the example in the question
// We generate 5 Authors
Author[] authors = Seed(1, 5).Select(x => GenerateTestAuthor(x.ToString())).ToArray();
// And 9 Articles
Article[] articles = Seed(1,9).Select(x => GenerateTestArticle(x.ToString())).ToArray();
// This defines how articles are connected to authors
int[] articleToAuthor = new[] {0,1,2,3,4,3,2,1,0};
// Let's connect articles and authors as per definition abbove
Seed(9).Foreach(x=> {articles[x].Author = authors[articleToAuthor[x]];});
// Now 3 Magazines
Magazine[] magazines = Seed(1,3).Select(x => GenerateTestMagazine(x.ToString())).ToArray();
// This deines which articles go in which magazine
int[] articleToMagazine = new[] {0,0,0,1,1,1,2,2,2};
// Let's add artices to the Magazines
Seed(9).Foreach(x=> magazines[articleToMagazine[x]].TOC.Add(articles[x]));
// And now let us link back from articles to Magazines
magazines.Foreach(x => x.TOC.Foreach(z => z.Source = x));
return magazines.ToList();
}
static IEnumerable<int> Seed(int start, int count)
{
return Enumerable.Range(start, count);
}
static IEnumerable<int> Seed(int n)
{
return Seed(0, n);
}
static Article GenerateTestArticle(string id)
{
return new Article
{
Title = "Title" + id,
Header = "Title" + id,
Body = "Title" + id,
PageNumber = 1,
};
}
static Author GenerateTestAuthor(string id)
{
return new Author
{
FirstName = "Author" + id,
LastName = "Author" + id,
};
}
static Magazine GenerateTestMagazine(string id)
{
return new Magazine
{
Title = "Magazine" + id,
ISBN = "Magazine" + id,
TOC = new List<Article>()
};
}
}
}
当我运行时,这就是我在屏幕上看到的
==[Setup]===========================
Magazine1
Title1, Author1
Title2, Author2
Title3, Author3
Magazine2
Title4, Author4
Title5, Author5
Title6, Author4
Magazine3
Title7, Author3
Title8, Author2
Title9, Author1
==[REsult]===========================
Author1
Title1, Author1
Title9, Author1
Author2
Title2, Author2
Title8, Author2
Author3
Title3, Author3
Title7, Author3
Author4
Title4, Author4
Title6, Author4
Author5
Title5, Author5
这是否适合您的需求?
public class Student
{
public List<Course> Courses { get; set; }
public List<Grade> Grades { get; set; }
public Dictionary<Course, Grade> CourseGrades { get; set; }
}
public class Course
{
public int Id { get; set; }
}
public class Grade
{
public double Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var c0 = new Course() { Id = 0 };
var c1 = new Course() { Id = 1 };
var students = new List<Student>()
{
new Student() { CourseGrades = new Dictionary<Course,Grade>()
{
{ c0, new Grade() { Value = 2 } },
{ c1 , new Grade() { Value = 1 } }
}},
new Student() { CourseGrades = new Dictionary<Course,Grade>()
{
{ c1 , new Grade() { Value = 3 } },
{ c0 , new Grade() { Value = 4 } }
}},
};
Dictionary<Course, List<Grade>> courseGrades = SelectUnion(students.SelectMany(s => s.CourseGrades), cg => cg.Key, cg => cg.Value);
}
private static Dictionary<TKey, List<TValue>> SelectUnion<TSource, TKey, TValue>(IEnumerable<TSource> set, Func<TSource, TKey> keyGen, Func<TSource, TValue> valueGen)
{
var result = new Dictionary<TKey, List<TValue>>();
foreach (var src in set)
{
var key = keyGen(src);
if (!result.ContainsKey(key))
{
result[key] = new List<TValue>();
}
result[key].Add(valueGen(src));
}
return result;
}
我知道这很可能只适合你举的例子。因此,如果这还不够,也许你可以提供一个你想要的最终结果的示例结构,这样我们就知道该为什么拍摄了。