从列表中删除项目的最佳方法
Best method to remove items from a list
我有一个 MyClass
的 500.000 到 1.000.000 个实例的列表,它具有以下属性:
class MyClass
{
string ParentId;
string Name;
DateTime StartDate;
DateTime EndDate;
}
数据可能如下所示:
ParentId | Name | StartDate | EndDate
----------------------------------------------
parent1 | alpha | 01-01-2011 | 02-02-2015
parent1 | beta | 01-01-2011 | 02-02-2014
parent2 | gamma | 01-01-2012 | 02-02-2011
我需要过滤列表,使其包含 "alpha" 和 "gamma" 对象。 "beta" 对象应排除在外,因为它与 alpha 具有相同的父对象,但 EndDate 更早。
即结果列表应该只包含每个 ParentId 的一个实例(具有最新 EndDate 的实例)。
过滤需要良好的表现。
您可以使用 Linq.Where
轻松过滤 List<T>
var result = myList
.Where(item => item.Name == "gamma" || item.Name == "alfa")
.ToList();
如果你想区分某个字段的输出,你可以使用MoreLinq's DistinctBy
或GroupBy
:
var result = myList
.Where(item => item.Name == "gamma" || item.Name == "alfa")
.GroupBy(item => item.ParentId)
.Select(g => g.First()) //Selection logic
.ToList();
您可以使用 GroupBy
和 Select
:
var filtered = list
.GroupBy(mc=>mc.ParentId)
.Select(g=>g.OrderByDescending(mc=>mc.EndDate).First())
.ToList();
我假设您想过滤掉 beta 是出于解释的原因,而不是因为它的裸名。您可以使用以下方法来获得这样的结果:
myClasses.GroupBy(i => i.ParentId)
.Select(i => i.OrderByDescending(i2 => i2.EndDate).First());
你可以使用它,这个方法对大数组工作得很好而且很快:
var groupesList = yourList.GroupBy(x => x.ParentId,
(y, set) => new {Key = y, Value = set.First(s => s.EndDate == set.Max(r => r.EndDate))}).Select(x => x.Value).ToList();
虽然目前接受的答案(来自@Kobi)是正确的并且可能是最简单的解决方案,但它可能不是 "best" 解决方案。
特别是,由于您提到列表中可能有很多项目并且解决方案应该表现良好,我想我会检查没有 LINQ 的解决方案如何执行。
这是我的解决方案:
var tempDict = new Dictionary<string, MyClass>();
foreach (var data in list) // list is the List<MyClass>
{
MyClass existing;
if (!tempDict.TryGetValue(data.ParentId, out existing))
{
// Put item into temp dictionary (use ParentId as key)
tempDict[data.ParentId] = data;
}
else
{
// Check if the instance in the temp dictionary has an
// earlier EndDate. If yes, replace it.
if (existing.EndDate < data.EndDate) // replace
tempDict[data.ParentId] = data;
}
}
var result = tempDict.Values.ToList();
快速比较(使用 500.000 个项目)显示此解决方案比 LINQ 版本快大约 3 到 4 倍(取决于唯一 ParentId 值的数量)。
我有一个 MyClass
的 500.000 到 1.000.000 个实例的列表,它具有以下属性:
class MyClass
{
string ParentId;
string Name;
DateTime StartDate;
DateTime EndDate;
}
数据可能如下所示:
ParentId | Name | StartDate | EndDate
----------------------------------------------
parent1 | alpha | 01-01-2011 | 02-02-2015
parent1 | beta | 01-01-2011 | 02-02-2014
parent2 | gamma | 01-01-2012 | 02-02-2011
我需要过滤列表,使其包含 "alpha" 和 "gamma" 对象。 "beta" 对象应排除在外,因为它与 alpha 具有相同的父对象,但 EndDate 更早。
即结果列表应该只包含每个 ParentId 的一个实例(具有最新 EndDate 的实例)。
过滤需要良好的表现。
您可以使用 Linq.Where
List<T>
var result = myList
.Where(item => item.Name == "gamma" || item.Name == "alfa")
.ToList();
如果你想区分某个字段的输出,你可以使用MoreLinq's DistinctBy
或GroupBy
:
var result = myList
.Where(item => item.Name == "gamma" || item.Name == "alfa")
.GroupBy(item => item.ParentId)
.Select(g => g.First()) //Selection logic
.ToList();
您可以使用 GroupBy
和 Select
:
var filtered = list
.GroupBy(mc=>mc.ParentId)
.Select(g=>g.OrderByDescending(mc=>mc.EndDate).First())
.ToList();
我假设您想过滤掉 beta 是出于解释的原因,而不是因为它的裸名。您可以使用以下方法来获得这样的结果:
myClasses.GroupBy(i => i.ParentId)
.Select(i => i.OrderByDescending(i2 => i2.EndDate).First());
你可以使用它,这个方法对大数组工作得很好而且很快:
var groupesList = yourList.GroupBy(x => x.ParentId,
(y, set) => new {Key = y, Value = set.First(s => s.EndDate == set.Max(r => r.EndDate))}).Select(x => x.Value).ToList();
虽然目前接受的答案(来自@Kobi)是正确的并且可能是最简单的解决方案,但它可能不是 "best" 解决方案。
特别是,由于您提到列表中可能有很多项目并且解决方案应该表现良好,我想我会检查没有 LINQ 的解决方案如何执行。
这是我的解决方案:
var tempDict = new Dictionary<string, MyClass>();
foreach (var data in list) // list is the List<MyClass>
{
MyClass existing;
if (!tempDict.TryGetValue(data.ParentId, out existing))
{
// Put item into temp dictionary (use ParentId as key)
tempDict[data.ParentId] = data;
}
else
{
// Check if the instance in the temp dictionary has an
// earlier EndDate. If yes, replace it.
if (existing.EndDate < data.EndDate) // replace
tempDict[data.ParentId] = data;
}
}
var result = tempDict.Values.ToList();
快速比较(使用 500.000 个项目)显示此解决方案比 LINQ 版本快大约 3 到 4 倍(取决于唯一 ParentId 值的数量)。