Сравнение двух списков объектов класса, аналогичных 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 как о списке после внесения некоторых изменений. Я хотел бы сравнить их и разделить на три категории:
- Добавленные элементы - элемент с новыми "данными", добавленными в список.
- Удаленные элементы - элемент был удален из списка.
- Различные элементы - "Данные" находятся в списках 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);