【问题标题】:Merging two Lists and a dictionary to create a single dictionary合并两个列表和一个字典以创建一个字典
【发布时间】:2012-02-18 14:40:03
【问题描述】:

我有两个具有类对象的通用列表:

class Subject
 {
   public string Code { get; set; }
   public string Name { get; set; }
 }

还有一个类如下:

class Student
{
    public string RollNo { get; set; }
    public string Name { get; set; 
}

还有一个类型列表,其中卷号和主题代码为学生的 KeyValuePair,如下所示:

List<KeyValuePair<string,string>>  RoleList=
           new  List<KeyValuePair<string,string>> ();

Student st1=new Student();          
Subject sb1=new Subject();
Subject sb2=new Subject();
Subject sb3=new Subject();

RoleList.Add(new KeyValuePair<string,string>(st1.RollNo,sb1.Code));
RoleList.Add(new KeyValuePair<string,string>(st1.RollNo,sb2.Code));
RoleList.Add(new KeyValuePair<string,string>(st1.RollNo,sb3.Code));

我需要的是另一个类型的字典

Dictionary<string, List<Subject>> StSbList =
    new Dictionary<string, List<Subject>>();

其中 StSbList 应该有一个给定学生的科目列表,使用字典 RoleList 具有给定卷号的所有科目,在 C# 4.0 中使用 LINQ,类似于

 StSbList(st1.RollNo, {sb1,sb2,sb3});

我尝试并能够合并到类似的集合,但对不同类型的集合感到震惊。有关任何其他更好方法的任何建议。

正如 Douglas 和 Martin 正确指出的那样,这两种解决方案都运行良好,但在我的测试数据上 ToLookup 运行速度更快,因此使用相同。

解决方案 1

Dictionary<string, List<Subject>> stSbList =
RoleList.GroupBy(kvp => kvp.Key)
        .ToDictionary(
            grouping => grouping.Key,
            grouping => grouping.Select(kvp => (from sub in listSub
                                                where sub.Code.Equals(kvp.Value)
                                                select sub).First()).ToList(),
            EqualityComparer<string>.Default);

解决方案 2

var tSbList = RoleList.ToLookup(kvp => kvp.Key, kvp => 
             (from sub in listSub
              where sub.Code.Equals(kvp.Value)
              select sub).First());

【问题讨论】:

  • 更清楚地解释您的需求,并向我们展示您的尝试。
  • 我已尽力解释我需要什么,但如果可能的话,您可以指出我需要更具体的地方,我很乐意编辑我的问题以及我尝试过的内容我合并到列表和两个字典仅使用此处的一些以前的问题。:)
  • 您希望StSbList 持有什么。更清楚地描述这一点。另外,我们不是来做你的工作的,所以请展示你到目前为止所做的尝试。
  • 顺便说一句,有几个RoleList.Add(st1.RollNo, ...);,但这是一个字典,那里ArgumentException会被抛出(“一个具有相同键的项目已经被添加。”)。
  • 嗨史蒂文,我没有要求任何人做我的工作,没有冒犯的意思,但我没有发布任何我已经尝试使用 Stack-overflow 中的其他问题的代码,只是保持问题清晰。如果你愿意,我可以再次发布相同的代码。

标签: c# .net linq c#-4.0


【解决方案1】:

你的问题不是很清楚。如果您要获取有关如何填充字典的示例,请参考以下示例:

Dictionary<string, List<Subject>> stSbList = new Dictionary<string, List<Subject>>();
stSbList.Add(st1.RollNo, new List<Subject> { sb1, sb2, sb3 });
stSbList.Add(st2.RollNo, new List<Subject> { sb1, sb3 });
stSbList.Add(st3.RollNo, new List<Subject> { sb1, sb2 });

更简洁地使用嵌套集合初始化器:

Dictionary<string, List<Subject>> stSbList = new Dictionary<string, List<Subject>>
{
    { st1.RollNo, new List<Subject> { sb1, sb2, sb3 } },
    { st2.RollNo, new List<Subject> { sb1, sb3 } },
    { st3.RollNo, new List<Subject> { sb1, sb2 } },
};

更新:正如其他人所指出的,您将RoleList 定义为字典可能是错误的,因为它不允许您为同一个学生定义多个科目。你需要的是一个支持多对多关系表示的数据结构;例如,List&lt;T&gt;Tuple&lt;string,string&gt;,其中每个元组的Item1 包含Student.RollNo,而Item2 包含Subject.Code

var roleList = new List<Tuple<string, string>>();
roleList.Add(Tuple.Create(st1.RollNo, sb1.Code));
roleList.Add(Tuple.Create(st1.RollNo, sb2.Code));
roleList.Add(Tuple.Create(st1.RollNo, sb3.Code));
roleList.Add(Tuple.Create(st2.RollNo, sb1.Code));
roleList.Add(Tuple.Create(st2.RollNo, sb3.Code));
roleList.Add(Tuple.Create(st3.RollNo, sb1.Code));
roleList.Add(Tuple.Create(st3.RollNo, sb2.Code));

要将其转换为字典,您可以先使用GroupBy 运算符,然后使用ToDictionary

var subjects = new[] { sb1, sb2, sb3 }.ToDictionary(sb => sb.Code);

Dictionary<string, List<Subject>> stSbList =
    roleList.GroupBy(tuple => tuple.Item1)
            .ToDictionary(
                grouping => grouping.Key,
                grouping => grouping.Select(tuple => subjects[tuple.Item2]).ToList(),
                EqualityComparer<string>.Default);

更新2:为了完整起见,这是根据您对RoleList 的定义构建字典的方法(尽管这可能不是您想要的):

var roleList = new Dictionary<string, string>();
roleList.Add(st1.RollNo, sb1.Code);
roleList.Add(st2.RollNo, sb2.Code);
roleList.Add(st3.RollNo, sb2.Code);

var subjects = new[] { sb1, sb2, sb3 }.ToDictionary(sb => sb.Code);

Dictionary<string, List<Subject>> stSbList = roleList.ToDictionary(
    kvp => kvp.Key,
    kvp => new List<Subject> { subjects[kvp.Value] });

更新3:适用于最新版本的代码:

var subjects = new[] { sb1, sb2, sb3 }.ToDictionary(sb => sb.Code);

Dictionary<string, List<Subject>> stSbList =
    RoleList.GroupBy(kvp => kvp.Key)
            .ToDictionary(
                grouping => grouping.Key,
                grouping => grouping.Select(kvp => subjects[kvp.Value]).ToList());

【讨论】:

  • 我不是在问如何初始化字典。但问题是使用两个列表和一个字典,根据某些标准将它们合并在一起,以获取另一个字典,该字典由初始列表之一中的对象组成。
  • @Douglas- 感谢更新,我刚刚编辑了我的问题以消除混乱。
  • @Douglas- var subject = new[] { sb1, sb2, sb3 }.ToDictionary(sb => sb.Code);对于这一部分,我有主题列表,我直接需要使用它。
  • @PanditA:如果您希望人们帮助您,您真的应该更加努力地正确地提出您的问题。您至少更改了 4 次问题的内容(通常给出明显不正确的代码),并且您说它仍然不完整?您的示例中的主题列表在哪里?
  • @PanditA,那么您可能应该从该列表中创建字典。在这样的列表中查找会很慢。
【解决方案2】:

要获得所需的字典,有必要为主题创建关联存储。 请看代码。

Student st1 = new Student
    {
        Name = "John",
        RollNo = "Roll 1"
    };

Subject sb1 = new Subject
    {
        Code = "Subject 1"
    };
Subject sb2 = new Subject
    {
        Code = "Subject 2"
    };
Subject sb3 = new Subject
    {
        Code = "Subject 3"
    };

// Create the associative dictionary for subjects.
var subjectsByCode = new [] { sb1, sb2, sb3 }.ToDictionary(subject => subject.Code, subject => subject);

// Many-to-many relationship: Student (RollNo) <=> Subject (Code).
List<KeyValuePair<string,string>> roleList = new List<KeyValuePair<string,string>>
    {
        new KeyValuePair<string, string>(st1.RollNo, sb1.Code),
        new KeyValuePair<string, string>(st1.RollNo, sb2.Code),
        new KeyValuePair<string, string>(st1.RollNo, sb3.Code)
    };

// Create Student RollNo => List of Subjects dictionary.
Dictionary<string, List<Subject>> studentRollNoToSubjects = roleList
    .GroupBy(pair => pair.Key) // Group by RollNo
    .ToDictionary(group => group.Key, // Key of the dictionary is RollNo.
        group => group.Select(pair => subjectsByCode[pair.Value]).ToList()); // List of subjects is created using subjects-by-code dictionary.

【讨论】:

    【解决方案3】:

    要获得所需的字典,您可以使用 ToLookup 扩展方法。

    您想保留 Subject 对象,但在您的问题中,您似乎将它们扔掉,只将它们的代码存储在字典中。我冒昧地稍微修改了您创建RoleList 的方式:

    var subject1 = new Subject { "Code 1", "Subject 1" };
    var subject2 = new Subject { "Code 2", "Subject 2" };
    var subject3 = new Subject { "Code 3", "Subject 3" };
    var RoleList = new  List<KeyValuePair<string, Subject>() {
      "Roll 1", subject1,
      "Roll 1", subject2,
      "Roll 1", subject3,
      "Roll 2", subject2,
      "Roll 2", subject3
    }
    
    StSbList = RoleList.ToLookup(kvp => kvp.Key, kvp => kvp.Value);
    

    假设RoleList有以下数据:

    钥匙 |价值 --------+--------- 卷 1 |主题1 卷 1 |主题2 卷 1 |主题3 卷 2 |主题2 卷 2 |主题3

    StSbList 将包含以下数据:

    钥匙 |价值 --------+----------------- 卷 1 |主题1,主题2,主题3 卷 2 |主题2,主题3

    【讨论】:

    • 我认为这错过了一步:结果应该包含Subjects,而不是他们的代码。
    • @Martin Liversage :我需要 Subject 类的对象,而不仅仅是主题代码。
    • @Martin :我使用了下面的代码,但在对任何给定代码进行分组时,我的元素计数为 4,而我只有三个主题。最后一个元素显示为 NULL。我的代码是: var tSbList = RoleList.ToLookup(kvp => kvp.Key, kvp => (from sub in listSub where sub.Code.Equals(kvp.Value) select sub).First());
    • @PanditA:有几个答案建议您使用.GroupBy(...).ToDictionary(...),但.ToLookup(...) 确实是一种更简单的方法。通过将Subject 对象存储在源字典中,可以避免.ToLookup()/.ToDictionary() 的值选择器内令人困惑的线性搜索。
    • @Martin :查找是要走的路...整洁的解决方案..:) 感谢您的回复。就绕道而言,有一些旧藏品无法更改,因此绕道。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    • 2021-03-13
    • 2017-07-23
    • 1970-01-01
    • 2019-05-04
    相关资源
    最近更新 更多