【问题标题】:Compare two Lists for differences比较两个列表的差异
【发布时间】:2013-10-20 10:48:25
【问题描述】:

我想要一些关于我们如何最好地编写一个通用函数来比较两个列表的反馈。 Lists 包含类对象,我们希望遍历一个列表,在第二个 List 中查找相同的项目并报告任何差异。

我们已经有了一个比较类的方法,所以我们需要关于如何从两个列表中提供方法(如下所示)的反馈。

例如,假设我们有一个简单的“Employee”类,它具有三个属性:Name、ID、Department。我们要报告 List 和另一个 List 之间的差异。

注意:
两个列表将始终包含相同数量的项目。

如上所述,我们有一个泛型方法用于比较两个类,我们如何结合这个方法来满足列表的需求,即从另一个方法循环遍历列表并将类提供给泛型方法.. .. 但是我们如何在第二个 List 中找到等效的类以传递给下面的方法;

public static string CompareTwoClass_ReturnDifferences<T1, T2>(T1 Orig, T2 Dest)
    where T1 : class
    where T2 : class
{
    // Instantiate if necessary
    if (Dest == null) throw new ArgumentNullException("Dest", "Destination class must first be instantiated.");

    var Differences = CoreFormat.StringNoCharacters;

    // Loop through each property in the destination  
    foreach (var DestProp in Dest.GetType().GetProperties())
    {
        // Find the matching property in the Orig class and compare
        foreach (var OrigProp in Orig.GetType().GetProperties())
        {

            if (OrigProp.Name != DestProp.Name || OrigProp.PropertyType != DestProp.PropertyType) continue;
            if (OrigProp.GetValue(Orig, null).ToString() != DestProp.GetValue(Dest, null).ToString())
                Differences = Differences == CoreFormat.StringNoCharacters 
                    ? string.Format("{0}: {1} -> {2}", OrigProp.Name,
                                                       OrigProp.GetValue(Orig, null),
                                                       DestProp.GetValue(Dest, null)) 
                    : string.Format("{0} {1}{2}: {3} -> {4}", Differences,
                                                              Environment.NewLine,
                                                              OrigProp.Name,
                                                              OrigProp.GetValue(Orig, null),
                                                              DestProp.GetValue(Dest, null));
        }
    }
    return Differences;
}

任何建议或想法表示赞赏?

编辑:以 .NET 2.0 为目标,因此 LINQ 是不可能的。

【问题讨论】:

  • 大声笑...不,一个顶级的系统关键应用程序 :-) 认真地尝试将此功能实现到一个小型爱好应用程序中...它所有的学习。
  • 列表的长度是否相等?
  • 等于 ??我很高兴能把它当作一个学习练习。你能扩展吗?
  • 糟糕,我删除了评论。我的意思是将对象与 ID 属性等同起来。并为相同 ID 的对象添加 GetDiffereces() 方法。然后使用 LINQ。
  • @pedro:LINQ 可以在 .NET 2.0 上运行。见code.google.com/p/linqbridge

标签: c# .net list .net-2.0


【解决方案1】:

此解决方案生成一个结果列表,其中包含两个输入列表的所有差异。 您可以通过任何属性来比较您的对象,在我的示例中是 ID。 唯一的限制是列表应该是相同的类型:

var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
            .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));

【讨论】:

  • 这很棒。社区确实可以让您获得其他人对问题的看法。谢谢!
  • 完美运行!谢谢
  • 我相信这是假设两个列表中都没有重复项?
【解决方案2】:

.... 但是我们如何在第二个 List 中找到等效的类以传递给方法 下面;

这是您的实际问题;你必须至少有一个不可变的属性,一个 id 或类似的东西,来识别两个列表中的对应对象。如果你没有这样的属性你,就不能不出错地解决问题。您可以通过搜索最小或逻辑变化来尝试猜测相应的对象。

如果你有这样的属性,解决方案就变得非常简单。

Enumerable.Join(
   listA, listB,
   a => a.Id, b => b.Id,
   (a, b) => CompareTwoClass_ReturnDifferences(a, b))

感谢 danbruc 和 Noldorin 的反馈。两个列表将相同 长度和顺序相同。所以上面的方法很接近,但是你能修改一下吗 将 enum.Current 传递给我上面发布的方法的方法?

现在我很困惑……这有什么问题?为什么不只是以下?

for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
    yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}

如果保证长度相等,甚至可以省略 Math.Min() 调用。


Noldorin 的实现当然更智能,因为委托和使用枚举器而不是使用 ICollection。

【讨论】:

  • 抱歉,我们的目标是 .NET 2.0。我应该指出这一点。
【解决方案3】:

我认为您正在寻找这样的方法:

public static IEnumerable<TResult> CompareSequences<T1, T2, TResult>(IEnumerable<T1> seq1,
    IEnumerable<T2> seq2, Func<T1, T2, TResult> comparer)
{
    var enum1 = seq1.GetEnumerator();
    var enum2 = seq2.GetEnumerator();

    while (enum1.MoveNext() && enum2.MoveNext())
    {
        yield return comparer(enum1.Current, enum2.Current);
    }
}

它未经测试,但它应该可以完成这项工作。请注意,此方法特别有用的是它是完全通用的,即它可以采用任意(和不同)类型的两个序列并返回任何类型的对象。

此解决方案当然假设您要将seq1 的第n 项与seq2 中的第n 项进行比较。如果您想根据特定的属性/比较来匹配两个序列中的元素,那么您将需要执行某种join 操作(如 danbruc 使用Enumerable.Join 所建议的那样。如果有,请告诉我这些方法都不是我所追求的,也许我可以提出其他建议。

编辑: 下面是一个示例,说明如何将 CompareSequences 方法与您最初发布的比较器函数一起使用。

// Prints out to the console all the results returned by the comparer function (CompareTwoClass_ReturnDifferences in this case).
var results = CompareSequences(list1, list2, CompareTwoClass_ReturnDifferences);
int index;    

foreach(var element in results)
{
    Console.WriteLine("{0:#000} {1}", index++, element.ToString());
}

【讨论】:

  • 这将同步遍历两个列表,但对象在两个列表中的排序方式可能不同。
  • 是的,当然。我无法完全从这个问题中收集到是否是这种情况,但无论如何我只是在阅读您的评论之前编辑了这篇文章,所以它现在是属性合格的......
  • 感谢 danbruc 和 Noldorin 的反馈。两个列表的长度和顺序相同。所以上面的方法很接近,但是你可以修改这个方法以将 enum.Current 传递给我上面发布的方法吗?
  • 没问题...两个列表长度相等且顺序相同的事实非常有帮助,所以我认为这个解决方案可以很好地完成工作(danbruc 值得注意的是更一般案例)。
  • (续)。现在,我不太确定您对传递 enum.Current 的含义是什么(上面的代码必须同时传递 enum1.Current 和 enum2.Current)。我编辑了这篇文章以阐明您如何使用 CompareSequences 函数。
【解决方案4】:

Microsoft 的这种方法效果很好,并且提供了将一个列表与另一个列表进行比较并切换它们以获得每个列表的差异的选项。如果您要比较类,只需将您的对象添加到两个单独的列表中,然后运行比较。

http://msdn.microsoft.com/en-us/library/bb397894.aspx

【讨论】:

    【解决方案5】:

    我希望我正确理解了您的问题,但是您可以使用 Linq 快速完成此操作。我假设您将始终拥有 Id 属性。只需创建一个接口来确保这一点。

    如果您将一个对象识别为相同的方式因类而异,我建议传入一个委托,如果这两个对象具有相同的持久 id,则该委托返回 true。

    这是在 Linq 中的操作方法:

    List<Employee> listA = new List<Employee>();
            List<Employee> listB = new List<Employee>();
    
            listA.Add(new Employee() { Id = 1, Name = "Bill" });
            listA.Add(new Employee() { Id = 2, Name = "Ted" });
    
            listB.Add(new Employee() { Id = 1, Name = "Bill Sr." });
            listB.Add(new Employee() { Id = 3, Name = "Jim" });
    
            var identicalQuery = from employeeA in listA
                                 join employeeB in listB on employeeA.Id equals employeeB.Id
                                 select new { EmployeeA = employeeA, EmployeeB = employeeB };
    
            foreach (var queryResult in identicalQuery)
            {
                Console.WriteLine(queryResult.EmployeeA.Name);
                Console.WriteLine(queryResult.EmployeeB.Name);
            }
    

    【讨论】:

      猜你喜欢
      • 2014-02-26
      • 1970-01-01
      • 1970-01-01
      • 2013-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多