c# - 如何根据属性合并两个对象列表并将重复项合并到新对象中
问题描述
我正在努力为我的问题找到一个简单的解决方案:我有两个对象列表,并希望基于一个属性(串行)比较它们并创建一个包含两个列表中的对象的新列表。如果对象仅在列表一中,我想将其标记为已删除(状态),如果它仅在列表二中,则应将其标记为新(状态)。如果它在两者中我想将其标记为已更改(状态)并存储旧值和新值(金额/新金额)。
所以它看起来像这样:
清单一:
[
{
serial: 63245-8,
amount: 10
},
{
serial: 08657-5,
amount: 100
}
,
{
serial: 29995-0,
amount: 500
}
]
清单二:
[
{
serial: 63245-8,
amount: 100
},
{
serial: 67455-1,
amount: 100
}
,
{
serial: 44187-10,
amount: 50
}
]
输出:
[
{
serial: 63245-8,
amount: 10,
newAmount: 100
status: "changed"
},
{
serial: 08657-5,
amount: 100
status: "deleted"
},
{
serial: 29995-0,
amount: 500,
status: "deleted"
}
{
serial: 67455-1,
amount: 100
status: "new"
}
,
{
serial: 44187-10,
amount: 50
status: "new"
}
]
除了遍历两个列表并与另一个列表进行比较之外,我想不出任何好的解决方案,构建三个不同的列表并将它们合并到最后,甚至最终对它们进行排序。我很确定有更好的解决方案,甚至可能使用 AutoMapper ?谁能帮我 ?
谢谢!
编辑:因为问题出现在评论中。如果项目在两个列表中,则状态可以是“已更改”或“未更改”。这对实现并不重要,因为我显示对象的新旧数量,只需要特别标记已删除和新对象。不过,状态“未更改”将是一个很好的参考。
解决方案
这是列表的双向比较,您可以通过使用 LinqIEnumerable.Except()
和IEnumerable.Intersect()
.
您应该做的第一件事是编写一个类来保存数据项:
sealed class Data
{
public string Serial { get; }
public int Amount { get; }
public Data(string serial, int amount)
{
Serial = serial;
Amount = amount;
}
}
接下来你需要做的是写一个IEqualityComparer<T>
你可以用来比较项目的(你需要这个来使用Intersect()
和Except()
:
sealed class DataComparer : IEqualityComparer<Data>
{
public bool Equals(Data x, Data y)
{
return x.Serial.Equals(y.Serial);
}
public int GetHashCode(Data obj)
{
return obj.Serial.GetHashCode();
}
}
现在编写一个类来接收比较数据:
enum ComparisonState
{
Unchanged,
Changed,
New,
Deleted
}
sealed class ComparedData
{
public Data Data { get; }
public int PreviousAmount { get; }
public ComparisonState ComparisonState { get; }
public ComparedData(Data data, ComparisonState comparisonState, int previousAmount)
{
Data = data;
ComparisonState = comparisonState;
PreviousAmount = previousAmount;
}
public override string ToString()
{
if (ComparisonState == ComparisonState.Changed)
return $"Serial: {Data.Serial}, Amount: {PreviousAmount}, New amount: {Data.Amount}, Status: Changed";
else
return $"Serial: {Data.Serial}, Amount: {Data.Amount}, Status: {ComparisonState}";
}
}
(为方便起见,我在ToString()
该类中添加了一个。)
现在您可以按如下方式使用 Linq。阅读评论以了解其工作原理:
class Program
{
public static void Main()
{
var list1 = new List<Data>
{
new Data("63245-8", 10),
new Data("08657-5", 100),
new Data("29995-0", 500),
new Data("12345-0", 42)
};
var list2 = new List<Data>
{
new Data("63245-8", 100),
new Data("12345-0", 42),
new Data("67455-1", 100),
new Data("44187-10", 50),
};
var comparer = new DataComparer();
var newItems = list2.Except(list1, comparer); // The second list without items from the first list = new items.
var deletedItems = list1.Except(list2, comparer); // The first list without items from the second list = deleted items.
var keptItems = list2.Intersect(list1, comparer); // Items in both lists = kept items (but note: Amount may have changed).
List<ComparedData> result = new List<ComparedData>();
result.AddRange(newItems .Select(item => new ComparedData(item, ComparisonState.New, 0)));
result.AddRange(deletedItems.Select(item => new ComparedData(item, ComparisonState.Deleted, 0)));
// For each item in the kept list, determine if it changed by comparing it to the first list.
// Note that the "list1.Find()` is an O(N) operation making this quite slow.
// You could speed it up for large collections by putting list1 into a dictionary and looking items up in it -
// but this is unlikely to be needed for smaller collections.
result.AddRange(keptItems.Select(item =>
{
var previous = list1.Find(other => other.Serial == item.Serial);
return new ComparedData(item, item.Amount == previous.Amount ? ComparisonState.Unchanged : ComparisonState.Changed, previous.Amount);
}));
// Print the result, for illustration.
foreach (var item in result)
Console.WriteLine(item);
}
}
其输出如下:
Serial: 67455-1, Amount: 100, Status: New
Serial: 44187-10, Amount: 50, Status: New
Serial: 08657-5, Amount: 100, Status: Deleted
Serial: 29995-0, Amount: 500, Status: Deleted
Serial: 63245-8, Amount: 10, New amount: 100, Status: Changed
Serial: 12345-0, Amount: 42, Status: Unchanged
推荐阅读
- android - Android 崩溃 - kotlin.UninitializedPropertyAccessException MainActivity.getAdapterGallery
- facebook - Facebook Conversions API 警告缺少事件 ID?
- r - 如何在加载阶段禁用 UI 元素
- windows - Windows“开始”菜单启动文件夹中的自复制脚本
- javascript - cURL JSON 数据 Node.js
- xml - perl 的 XML::SemanticDiff 可以硬塞到两个 XML 文件的顺序不可知比较中吗?
- javascript - 我有一个关于 js Async Await 行为结构的问题
- vb.net - 如何查找包含具有给定扩展名的文件的文件夹
- javascript - 输入和退格后显示 Google 脚本 HTML 数据列表
- bash - 无法通过 Bash 创建文件