Сравнение двух списков объектов класса, аналогичных Diff Tool

Вопрос перенесен сюда.

Мое требование - написать программу, которая имитирует различные инструменты. Да, есть довольно много библиотек и открытого исходного кода, который выполняет эту задачу, но я хотел бы написать свой собственный компаратор.

Вот отправная точка. У меня есть класс с именем DataItem, который выглядит следующим образом:

public class DataItem
{
    public DataItem() { }
    public DataItem(string d, string v) { Data = d; Value = v; }

    public string Data { get; set; }
    public string Value { get; set; }
}

У меня есть два списка этих объектов класса, давайте назовем их PRE и POST и возьмем несколько примеров значений следующим образом. Часть "Данные" будет уникальной в списке.

preList: (Data,Value)
AAA,0
BBB,1
CCC,3
DDD,4
FFF,0
GGG,3

postList: (Data,Value)
AAA,0
BBB,2
DDD,4
EEE,9
FFF,3

Думайте о PRE как о первоначальном списке, а POST как о списке после внесения некоторых изменений. Я хотел бы сравнить их и разделить на три категории:

  1. Добавленные элементы - элемент с новыми "данными", добавленными в список.
  2. Удаленные элементы - элемент был удален из списка.
  3. Различные элементы - "Данные" находятся в списках PRE и POST, но их соответствующие "Значения" отличаются.

Таким образом, при категоризации они должны выглядеть так:

Added Items:
EEE,9

Removed Items:
CCC,3
GGG,3

Diff Items:
BBB
FFF

У меня есть другой класс DiffItem, для объектов которого я хотел бы поставить окончательные результаты. DiffItem выглядит так:

public class DiffItem
{
    public DiffItem() { }
    public DiffItem(string data, string type, string pre, string post) { Data = data; DiffType = type; PreVal = pre; PostVal = post; }

    public string Data { get; set; }
    public string DiffType { get; set; } // DiffType = Add/Remove/Diff
    public string PreVal { get; set; } // preList value corresponding to Data item
    public string PostVal { get; set; } // postList value corresponding to Data item
}

Для этого я сначала расширил IEqualityComparer и написал пару сравнителей:

public class DataItemComparer : IEqualityComparer<DataItem>
{
    public bool Equals(DataItem x, DataItem y)
    {
        return (string.Equals(x.Data, y.Data) && string.Equals(x.Value, y.Value));
    }

    public int GetHashCode(DataItem obj)
    {
        return obj.Data.GetHashCode();
    }
}

public class DataItemDataComparer : IEqualityComparer<DataItem>
{
    public bool Equals(DataItem x, DataItem y)
    {
        return string.Equals(x.Data, y.Data);
    }

    public int GetHashCode(DataItem obj)
    {
        return obj.Data.GetHashCode();
    }
}

Затем использовали методы Except() и Intersect() следующим образом:

    static void DoDiff()
    {
        diffList = new List<DiffItem>();

        IEnumerable<DataItem> preOnly = preList.Except(postList, new DataItemComparer());
        IEnumerable<DataItem> postOnly = postList.Except(preList, new DataItemComparer());
        IEnumerable<DataItem> common = postList.Intersect(preList, new DataItemComparer());

        IEnumerable<DataItem> added = postOnly.Except(preOnly, new DataItemDataComparer());
        IEnumerable<DataItem> removed = preOnly.Except(postOnly, new DataItemDataComparer());
        IEnumerable<DataItem> diffPre = preOnly.Intersect(postOnly, new DataItemDataComparer());
        IEnumerable<DataItem> diffPost = postOnly.Intersect(preOnly, new DataItemDataComparer());

        foreach (DataItem add in added)
        {
            diffList.Add(new DiffItem(add.Data, "Add", null, add.Value));
        }
        foreach (DataItem rem in removed)
        {
            diffList.Add(new DiffItem(rem.Data, "Remove", rem.Value, null));
        }
        foreach (DataItem pre in diffPre)
        {
            DataItem post = diffPost.First(x => x.Data == pre.Data);
            diffList.Add(new DiffItem(pre.Data, "Diff", pre.Value, post.Value));
        }
    }

Это работает и делает работу. Но мне интересно, есть ли "лучший" способ сделать это. Обратите внимание, что я ставлю кавычки вокруг слова "лучше", потому что у меня нет правильного определения того, что могло бы сделать это лучше. Возможно, есть способ сделать это без такого количества циклов 'foreach' и использования Except() и Intersetc(), так как я думаю, что за Linq происходит довольно много итераций.

Проще говоря, есть ли более чистый код, который я могу написать для этого? Я спрашиваю в основном из академического интереса и расширить свои знания.

1 ответ

Я не думаю, что вам нужен ваш IEqualityComparer:

var added = from a in postList
            where !preList.Any(b => b.Data == a.Data)
            select new DiffItem(a.Data, "Add", null, a.Value);
var removed = from b in preList
              where !postList.Any(a => a.Data == b.Data)
              select new DiffItem(b.Data, "Remove", b.Value, null);
var diff = from b in preList
           join a in postList on b.Data equals a.Data
           where b.Value != a.Value
           select new DiffItem(b.Data, "Diff", b.Data, a.Data);
var diffList = added.ToList();
diffList.AddRange(removed);
diffList.AddRange(diff);
Другие вопросы по тегам