比较两个列表的差异

发布于 2024-07-16 11:15:14 字数 2061 浏览 6 评论 0原文

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

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

例如,假设我们有一个简单的“Employee”类,它具有三个属性:Name、ID、Department。 我们想要报告 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 是不可能的。

I would like some feedback on how we can best write a generic function that will enable two Lists to be compared. The Lists contain class objects and we would like to iterate through one list, looking for the same item in a second List and report any differences.

We already have a method to compare classes, so we need feedback on how we can feed the method (shown below) from two Lists.

For example, say we have a simple "Employee" class that has three properties, Name, ID, Department. We want to report the differences between List and another List.

Note:
Both lists will always contain the same number of items.

As mentioned above, we have a generic method that we use to compare two classes, how can we incorporate this method to cater for Lists, i.e. from another method, loop through the List and feed the classes to the generic method .... but how do we find the equivalent class in the second List to pass to the method below;

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;
}

Any suggestions or ideas appreciated?

Edit: Targeting .NET 2.0 so LINQ is out of the question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

踏月而来 2024-07-23 11:15:14

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

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

This solution produces a result list, that contains all differences from both input lists.
You can compare your objects by any property, in my example it is ID.
The only restriction is that the lists should be of the same type:

var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
            .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));
濫情▎り 2024-07-23 11:15:14

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

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

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

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

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

现在我很困惑......这有什么问题吗? 为什么不只是以下内容?

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

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


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

.... but how do we find the equivalent class in the second List to pass to the method
below;

This is your actual problem; you must have at least one immutable property, a id or something like that, to identify corresponding objects in both lists. If you do not have such a property you, cannot solve the problem without errors. You can just try to guess corresponding objects by searching for minimal or logical changes.

If you have such an property, the solution becomes really simple.

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

thanks to you both danbruc and Noldorin for your feedback. both Lists will be the same
length and in the same order. so the method above is close, but can you modify this
method to pass the enum.Current to the method i posted above?

Now I am confused ... what is the problem with that? Why not just the following?

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

The Math.Min() call may even be left out if equal length is guaranted.


Noldorin's implementation is of course smarter because of the delegate and the use of enumerators instead of using ICollection.

白昼 2024-07-23 11:15:14

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

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());
}

I think you're looking for a method like this:

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);
    }
}

It's untested, but it should do the job nonetheless. Note that what's particularly useful about this method is that it's full generic, i.e. it can take two sequences of arbitrary (and different) types and return objects of any type.

This solution of course assumes that you want to compare the nth item of seq1 with the nth item in seq2. If you want to do match the elements in the two sequences based on a particular property/comparison, then you'll want to perform some sort of join operation (as suggested by danbruc using Enumerable.Join. Do let me know if it neither of these approaches is quite what I'm after and maybe I can suggest something else.

Edit:
Here's an example of how you might use the CompareSequences method with the comparer function you originally posted.

// 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());
}
ら栖息 2024-07-23 11:15:14

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

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

This approach from Microsoft works very well and provides the option to compare one list to another and switch them to get the difference in each. If you are comparing classes simply add your objects to two separate lists and then run the comparison.

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

征﹌骨岁月お 2024-07-23 11:15:14

我希望我正确理解了您的问题,但是您可以使用 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);
        }

I hope that I am understing your question correctly, but you can do this very quickly with Linq. I'm assuming that universally you will always have an Id property. Just create an interface to ensure this.

If how you identify an object to be the same changes from class to class, I would recommend passing in a delegate that returns true if the two objects have the same persistent id.

Here is how to do it in 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);
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文