문제

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