How to efficiently compare two objects and count the number of equal properties?

Viewed 658

I am working with two list that contain many properties. I am attempting to loop through the lists and return a comparison of the two objects in the form of a count (int). The count is defined as the number of properties that are equal.

Below is an example of this:

    class Object{
     public string prop1 {get;set;} //x5  Avg Length (20-30 Char)
     public double prop2 {get;set;} //x3

    }
    private int CompareProps(Object a, Object b)
    {
        int matchedElements = 0;
        if (a.Prop1 == b.Pro1)
            matchedElements++; ;
        if (a.Prop2 == b.Prop2)
            matchedElements++;

        // More Property Comparisons...

        return matchedElements;
    }
    ///Loops in another method with two Lists of Object, where each list.count = 300
        List1 = getList1Objects();//300 Objects
        List2 = getList2Objects();//300 Objects
        int l1 = List1.Count;
        int l2 = List2.Count;

        Parallel.For(0, l1, i =>
        {
            for (int j = 0; j < l2; j++)
            {
                int k =  CompareProps(List1[i], List2[j]);
            }
        });

This is very inefficient though. Is there are better way to do this in C#? The properties can be strings, doubles, etc.

Thanks!

3 Answers

If performance is really important, I think you need Intersect since it uses HashSets.

private static int CompareProps(MyObject a, MyObject b)
{
     var aValues = a.GetType().GetProperties().Select(x => x.GetValue(a, null));
     var bValues = b.GetType().GetProperties().Select(x => x.GetValue(b, null));

     return aValues.Intersect(bValues).Count();
}

And here's the sample usage..

var a = new MyObject
{
   prop1 = "abc", // same value
   prop2 = "def",
   prop3 = 123,
   prop4 = 456 // same value
};

var b = new MyObject
{
   prop1 = "abc", // same value
   prop2 = "jkl",
   prop3 = 789,
   prop4 = 456 // same value
};

Console.WriteLine(CompareProps(a, b)); // output 2

EDIT:

Tested my solution by running 300X300 list loop.

private static void Run()
{
  var alist = new List<MyObject>();
  for (var i = 0; i < 300; i++)
  {
    alist.Add(new MyObject
    {
        prop1 = "abc",
        prop2 = RandomString(),
        prop3 = random.Next(),
        prop4 = 123
    });
  }

  var blist = new List<MyObject>();
  for (var i = 0; i < 300; i++)
  {
     blist.Add(new MyObject
     {
        prop1 = "abc",
        prop2 = RandomString(),
        prop3 = random.Next(),
        prop4 = 123
     });
  }

  var watch = new Stopwatch();
  watch.Start();

  Parallel.For(0, alist.Count, i =>
  {
     for (var j = 0; j < blist.Count; j++)
     {
        Console.WriteLine("Result: " + CompareProps(alist[i], blist[j]));
     }
  });

  Console.WriteLine(watch.Elapsed.TotalSeconds + "  seconds..");
}

Result: 9.1703053 seconds..

enter image description here

You can compare them like this:

private int CompareProps(Object a, Object b)
{
    int matchedElements = 0;
    var props = a.GetType().GetProperties();
    foreach(var prop in props)
    {
        var vala = prop.GetValue(a);
        var valb = prop.GetValue(b);
        if(vala == valb)
            matchedElements++;

    // More Property Comparisons...
    return matchedElements;
}

It's easier to write and maintain for future changes in class properties. But be ware that it definitely takes more time. So it you are intending to compare hundreds of instances of objects this method is not recommended.

You can compare a to b either by reference or hash for a quick short-circuit if there's a possibility of the same object occurring in both lists.

If the inner list is slow to create (e.g. a query from a DB) you can take a snapshot .ToList() outside of the loop if size/memory permits.

That's about it though. Sometimes though, work is work.

Related