【问题标题】:C#: Creating objects that does not exist in a different listC#:创建不存在于不同列表中的对象
【发布时间】:2009-06-05 13:48:08
【问题描述】:

我有两个不同类型的对象的两个列表,它们表示来自两个 sql 查询的数据行。第一个列表包含数据,第二个包含更详细的数据。举个例子:

List1:        List2:
 1   Alice     1   15
 2   Bob       1   19
 3   Carol     2    5
 4   Dave      2    7
               2   20
               4   16

我想在 List2 中插入行,以便 List1 中的每个人在 List2 中至少有一行。因此,当 List2 中不存在某个人的行时,我想插入一个具有默认值的行。在示例情况下,我必须为 Carol 插入一行,所以我最终会得到:

List1:        List2:
 1   Alice     1   15
 2   Bob       1   19
 3   Carol     2    5
 4   Dave      2    7
               2   20
               3    0
               4   16

有没有人有一个聪明、干净和有效的方法来做到这一点?

我知道要将这些表连接在一起,我必须使用外连接,例如在这个Outer Join Sample 中。但我不想要一个新的结果集。我只想将那些缺失的行插入到 List2 中。

注意: 是的,我知道问题\标题有点……等等……但我不知道如何更好地表述它。如果可以的话,请有人修复它。

注意 2: 我不能使用 SQL。我无法在原始表中插入这些行。我正在报告数据,这意味着我不接触任何数据。我刚读出来。数据将用于主从报表,我的问题是,当某个主行不存在详细信息时,您最终会得到一个空白空间。这不好。因此,我想插入包含合理信息的行,以便用户可以看到这里没有可显示的内容。

【问题讨论】:

    标签: c# linq list


    【解决方案1】:

    假设您的列表按示例中的 Key 值排序(在本例中为整数),这样的事情应该可以工作:

    int i = 0;
    
    foreach (var item in List1)
    {
        // Skip any items in List2 that don't exist in List1
        // (not sure this is needed in your case), or that we've
        // already passed in List1
        while (List2[i].Key < item.Key)
            i++;
    
        if (List2[i].Key > item.Key)
        {
            // Create new item in List2
            List2.Add(new List2Item(item.Key, 0));
        }
    }
    
    // TODO: resort List2
    

    根据您希望丢失的项目数量,您可能希望插入 到 List2 中,从而消除了求助的需要。但是,如果您预计会丢失很多项目,则此方法会更快。或者,您可以为 List2 使用链表。

    请注意,如果 List1 中有重复的 Key 条目,这将失败。您需要单独检查以防止在 List2 中创建多个新项目。

    【讨论】:

      【解决方案2】:
      var lst1 = new List<int>() { 1, 2, 3, 4 };
      var lst2 = new List<int>() { 1, 1, 2, 2, 2, 4 };
      
      lst2.AddRange(lst1.Except(lst2));
      

      【讨论】:

        【解决方案3】:

        LINQ:从您在链接中提供的示例中,只需将代码更改为:

        foreach (var i in q) {
            Console.WriteLine("Customer: {0}  Order Number: {1}", 
                i.Name.PadRight(11, ' '), i.OrderNumber);
        }
        

        foreach (var i in q) {
            if (i.OrderNumber == "(no orders)")
                order.Add(new Order {Key = i.ID /* add your default values here*/});
        }
        

        当然你也可以在此处保存一些代码行以及之前的代码。

        【讨论】:

          【解决方案4】:

          好的,下面是: 1. 创建一个类型来表示列表中的项目:

           struct ListType
                  {
                      public object Id;
                      public object Name;
                  }
          

          或者,您当然可以采用更适合您的其他方式构建。

          1. 通过 LINQ 查询将 List2 创建为 IEnumerable

          2. 我假设 List1 与 List2 具有相同的结构,具有 Id 和 Name 字段(您可以对列表项使用相同的 ListType 类型)

          3. 有了上面的假设,下面是解决初始问题的代码:

            列表 newLst2 = list2.ToList();

            Array.ForEach(list1.ToArray(), list1It =>
            {
               var isInList2 = from list2it in newLst2.ToArray()
                               where (string)list2it.Id == list1It.Id
                               select list2it;
            
               if (isInList2.Count() == 0)
                   newLst2.Add(new ListType { Id = list1It.Id, Name = list1It.Name });
            
            });     
            

          注释:对于 List1 中的每个元素,在 List2 中进行查询并检查 Id 是否存在。如果它不存在,请添加一个新项目。

          可能有更有效的方法可以做到这一点,但这应该能让你开始。

          【讨论】:

            【解决方案5】:

            这是一个使用 LINQ 的解决方案。

            public class List1
            {
                public int ID { get; set; }
                public string Person { get; set; }
            }
            
            public class List2
            {
                public int ID { get; set; }
                public int Value { get; set; }
            }
            
            var lList1 = new List<List1>
                         {
                            new List1 {ID = 1, Person = "Alice"},
                            new List1 {ID = 2, Person = "Bob"},
                            new List1 {ID = 3, Person = "Carol"},
                            new List1 {ID = 4, Person = "Dave"}
                         };
            
            
            var lList2 = new List<List2>
                         {
                           new List2 {ID = 1, Value = 15},
                           new List2 {ID = 1, Value = 19},
                           new List2 {ID = 2, Value = 5},
                           new List2 {ID = 2, Value = 7},
                           new List2 {ID = 2, Value = 20},
                           new List2 {ID = 4, Value = 16}
                         };
            
            var lOutput = lList1.SelectMany(pArg => 
                                         lList2.Where(pArg1 => pArg1.ID == pArg.ID)
                                               .DefaultIfEmpty(new List2 { ID = pArg.ID, Value = 0})
                                               .Select(pArg1 => pArg1));
            

            【讨论】:

              【解决方案6】:

              呃...看起来只使用 Contains 会很简单,不是吗?

              foreach (Key key in List1.Keys)
              {
                 if (!List2.Keys.Contains(key)) List2.Add(key, "0");
              }
              

              这对于 List1 中的重复键没有问题。

              【讨论】:

                【解决方案7】:

                LINQ 实现

                public class Master
                {
                    public int ID;
                }
                
                public class Detail
                {
                    public int ID;
                    public int Count;
                }
                
                public static void AddMissingDetails(IEnumerable<Master> masters, List<Detail> details)
                {
                    AddMissingDetails(masters, details, x => new Detail
                        {
                            ID = x,
                            Count = 0
                        });
                }
                
                public static void AddMissingDetails(IEnumerable<Master> masters, List<Detail> details, Func<int, Detail> createDefault)
                {
                    details.AddRange(
                        masters
                            .Select(x => x.ID)
                            .Except(details.Select(x => x.ID).Distinct())
                            .Select(createDefault));
                }
                

                【讨论】:

                  【解决方案8】:

                  您可能不喜欢我的解决方案。但我要感谢你的这篇文章。 它让我有机会使用 linq 做一些有用的事情。

                  我正在使用 Extension 方法和 Linq 在您的目标列表 (List2) 中添加缺失的项目。 我不确定你是否正在使用 3.0/3.5 框架,如果你这样做了,那么这个解决方案对你有用,而且它也是“一种聪明、干净和有效的方法”:)。

                   public static void MergeLists() {
                        var listOne=new List<List1> {
                          new List1 {ID=1, Person="Alice"},
                          new List1 {ID=2, Person="Bob"},
                          new List1 {ID=3, Person="Carol"},
                          new List1 {ID=4, Person="Dave"},
                          new List1 {ID=5, Person="Dave2"},
                          new List1 {ID=6, Person="Dave3"},
                        };
                        var listTwo=new List<List2> {
                          new List2 {ID=1, Value=15},
                          new List2 {ID=1, Value=19},
                          new List2 {ID=2, Value=5},
                          new List2 {ID=2, Value=7},
                          new List2 {ID=2, Value=20},
                          new List2 {ID=4, Value=16}
                        };
                  
                        var listTwoWithAddedItems=listOne.AddMissingItems(listTwo, (item1, item2) => item1.ID==item2.ID,
                                                                      item2 => new List2 { ID=item2.ID, Value=-1 }).ToList();//For this value, you can put whatever default value you want to set for the missing items.
                  
                        Console.Read();
                      }
                  
                       public static class AmbyExtends {
                  
                          public static List<Target> AddMissingItems<Source, Target>(this IEnumerable<Source> source, List<Target> target, Func<Source, Target, bool> selector, Func<Source, Target> creator) {
                            foreach(var item in source) {
                              if(!target.Any(x=>selector(item,x))) {
                                target.Add(creator(item));
                              }         
                            }
                            return target;
                          }
                        }
                  

                  【讨论】:

                    【解决方案9】:
                    INSERT INTO List2(ID, Value)
                    SELECT      ID, 0
                    FROM        List1
                    WHERE       NOT EXISTS (SELECT NULL FROM List2 WHERE List2.ID=List1.ID)
                    

                    【讨论】:

                    • T-SQL 是最合适的语言,而不是 LINQ。
                    • 我无法向数据库中插入任何内容。而且我不能使用 SQL =/
                    【解决方案10】:

                    在数据库上使用 SQL,解决方案是:

                    INSERT INTO List2 (ID)
                    SELECT      l1.ID
                    FROM        List1 l1
                    LEFT JOIN   List2 l2
                            ON  l1.ID = l2.ID
                    WHERE       l2.ID IS NULL
                    

                    这里假设 List2 表中的其他列要么不是 NULL,要么具有 DEFAULT 值约束。

                    【讨论】:

                    • 答案需要在 LINQ 而不是 SQL 中,抱歉。
                    • 如果你需要LINQ,那么问题出在哪里?:你可以得到你在问题中提到的结果。然后使用那些没有匹配的对象来创建新对象(或将它们添加到列表中)
                    • 这正是我要问的问题:P 你将如何干净高效地做到这一点?
                    猜你喜欢
                    • 2012-07-16
                    • 2012-06-19
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多