改进通用列表比较器

时间:2014-04-23 13:11:06

标签: c# list generics comparator

我需要一个泛型类来比较两个列表实例。我有下面的代码,但它非常混乱和低效。我怎样才能让它更好,更快?

CallByName是一个使用反射获取属性的扩展。

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

答案:

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

}

使用:

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

输出:

1个通讯,1个删除,1个新

感谢

2 个答案:

答案 0 :(得分:2)

要获取两个列表共有的项目列表,请查看Enumerable.Intersect。那就是:

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

如果您想了解NewListOriginalList中的哪些项目(即添加的项目列表),您可以使用Enumerable.Except

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

如果您想知道哪些项目已被删除,请再次使用Except,但切换顺序:

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

如果您要比较不同的属性,那么最好的办法是创建包含属性和对象引用的中间列表。例如:(代码中可能存在拼写错误,但这显示了一般的想法。)

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

现在,您可以在IntersectExcept上执行listNewlistOrig,这将比较属性名称。然后,您可以从结果列表中提取Item值。

答案 1 :(得分:1)

你的班级实际上并不是通用的(尽管它应该是),只有你的方法是。

试试这个:

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
}

运行将originalItems与newItems进行比较,并使用您提供的ComparePredicate返回相同的项目列表。

使用的一个例子是:

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

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

此处的比较谓词将返回list1中的所有整数,其中列表1小于列表2中的等效int。

编辑:我应该补充一点,你不应该重新发明轮子,你总是可以使用你自己的IEqualityComparer类。