سؤال

I need a generic class to compare two list instances. I have the code below, but it is quite messy and inefficient. How can I make it better and definitely faster?

CallByName is an extension that uses reflection to get the property.

public class ListObjectComparator
{ 
    #region PROPRIETES

    public List<object> NewList {get; private set;}
    public List<object> RemovedList { get; private set; }
    public List<object> CommunList { get; private set; }

    #endregion


    #region METHODES PUBLICS

    public bool Run<T>(string iCommunTextPropertyName, List<T> iOriginalList, List<T> iNewList)
    {
        return Run(iCommunTextPropertyName, iOriginalList, iCommunTextPropertyName, iNewList);
    }

    public bool Run<T>(string iOriginalPropertyName, List<T> iOriginalList,string iNewPropertyName, List<T> iNewList)
    {
        if (iOriginalPropertyName.IsNotNull() &&
            iNewPropertyName.IsNotNull() &&
            iOriginalList != null &&
            iNewList != null)
        {
            NewList = new List<object>();
            RemovedList = new List<object>();
            CommunList = new List<object>();

            foreach (object originalItem in iOriginalList)
            {
                object research = iNewList.Where(a => a.CallByName(iNewPropertyName).ToString() == originalItem.CallByName(iOriginalPropertyName).ToString()).FirstOrDefault();
                if (research == null)
                {
                    RemovedList.Add(originalItem);
                }
                else
                {
                    CommunList.Add(research);
                }
            }

            foreach (object newItem in iNewList)
            {
                object research = iOriginalList.Where(a => a.CallByName(iOriginalPropertyName).ToString() == newItem.CallByName(iNewPropertyName).ToString()).FirstOrDefault();
                if (research == null) { NewList.Add(newItem); };

            }
            return true;
        }
        return false;
    }
}

Answer:

public class ListComparator<TOriginal,TNew>
{

    public ListComparator(IEnumerable<TOriginal> iOriginalList, Func<TOriginal, IComparable> iOriginalProperty, IEnumerable<TNew> iNewList, Func<TNew, IComparable> iNewProperty)
    {
        if (iOriginalProperty == null ||
            iNewProperty == null) { throw new ArgumentNullException(); };

            if (iOriginalList.IsNullOrEmpty() )
            {
                NewList = (iNewList != null) ? iNewList.ToList() : null;
                return;
            }

            if (iNewList.IsNullOrEmpty())
            {
                RemovedList = (iOriginalList != null) ? iOriginalList.ToList() : null;
                return;
            }


            NewList = (from tnew in iNewList.ToList()
                       join toriginal in iOriginalList.ToList()
                       on iNewProperty(tnew)
                       equals iOriginalProperty(toriginal) into gj
                       from item in gj.DefaultIfEmpty()
                       where item == null
                       select tnew).ToList();

            CommunList = (from tnew in iNewList.ToList()
                          join toriginal in iOriginalList.ToList()
                          on iNewProperty(tnew)
                          equals iOriginalProperty(toriginal) into gj
                          from item in gj.DefaultIfEmpty()
                          where item != null
                          select tnew).ToList();

            CommunPairList = (from tnew in iNewList.ToList()
                              join toriginal in iOriginalList.ToList()
                              on iNewProperty(tnew)
                              equals iOriginalProperty(toriginal) into gj
                              from item in gj.DefaultIfEmpty()
                              where item != null
                              select new KeyValuePair<TOriginal, TNew>(item, tnew)).ToList();

            RemovedList = (from toriginal in iOriginalList.ToList()
                           join tnew in iNewList.ToList()
                           on iOriginalProperty(toriginal)
                           equals iNewProperty(tnew) into gj
                           from item in gj.DefaultIfEmpty()
                           where item == null
                           select toriginal).ToList();
            return;

    }

    #region PROPRIETES

    public List<TNew> NewList { get; private set; }
    public List<TOriginal> RemovedList { get; private set; }
    public List<TNew> CommunList { get; private set; }
    /// <summary>
    /// Obtient la liste de pair avec l'original en key et le nouveau en value
    /// </summary>
    public List<KeyValuePair<TOriginal,TNew>> CommunPairList { get; private set; }

    #endregion

}

use:

List<Tuple<string, string>> list1 = new List<Tuple<string, string>>();
        List<Tuple<string, string>> list2 = new List<Tuple<string, string>>();

        list1.Add(new Tuple<string, string>("AA", "zefzef"));
        list1.Add(new Tuple<string, string>("A1", "iulyu"));

        list2.Add(new Tuple<string, string>("Abb", "szefez"));
        list2.Add(new Tuple<string, string>("A1", "zevzez"));

        ListComparator<Tuple<string, string>, Tuple<string, string>> comp = new ListComparator<Tuple<string, string>, Tuple<string, string>>(list1, x => x.Item1, list2, a => a.Item1);

OUTPUT :

1 commun, 1 removed, 1 new

thanks

هل كانت مفيدة؟

المحلول

To get the list of items that the two lists share in common, look at Enumerable.Intersect. That is:

var same = OriginalList.Intersect(NewList, comparer);

If you want to know which items in NewList aren't in OriginalList (i.e. the list of added items), you'd use Enumerable.Except:

var added = NewList.Except(OriginalList, comparer);

If you want to know which items were deleted, you use Except again, but switch the order:

var deleted = OriginalList.Except(NewList, comparer);

If you're comparing different properties, then probably your best bet is to create intermediate lists that contain the properties and references to the objects. For example: (There might be typos in the code, but this shows the general idea.)

class TempObj: IEquatable<TempObj>
{
    public string Key { get; set; }
    public object Item { get; set; }
    public bool Equals(TempObj other)
    {
        return Key.Equals(other.Key);
    }
    public override int GetHashCode()
    {
        return Key.GetHashCode();
    }
}

var listOrig = OriginalList.Select(x => new TempObj(
    x..CallByName(iOriginalPropertyName).ToString());
var listNew = NewList.Select(x => new TempObj(
    x.CallByName(iNewPropertyName).ToString());

Now you can do Intersect or Except on listNew and listOrig, which will compare the property names. You can then pull the Item values from the resulting list.

نصائح أخرى

Your class actually isn't generic here (although it should be), only your methods are.

Try this:

public class ListObjectComparator<T>
{

    #region Public Methods

    public static IEnumerable<T> Run(IEnumerable<T> originalItems, IEnumerable<T> newItems, Func<T,T,bool> comparer)
    {
        foreach(var originalItem in originalItems)
        {
            bool found = false;
            foreach (var newItem in newItems)
            {
                if (comparer(originalItem,newItem))
                {
                    found = true;
                    itemToRemove = newItem;
                    break;
                }
            }
            if (found)
            {
                newItems.Remove(itemToRemove);
                yield return originalItem;
            }
        }
    }

    #endregion
}

Run compares originalItems with newItems and returns a list of items that are the same using the ComparePredicate you supply.

An example of use is:

List<int> firstList;
List<int> secondList;

List<int> common = ListObjectComparator<int>.Run(firstList,secondList, ((f,s) => return f< s));

The compare predicate here will return all ints in list1 where list 1 was less than the equivalent int in list 2.

EDIT: I should add that you shouldn't reinvent the wheel, you could always use an IEqualityComparer class of your own.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top