C#如何使用LINQ将List拆分为两个

Joh*_*ens 6 c# linq performance split list

我试图使用LINQ将List拆分为两个列表,而不是两次迭代'master'列表.一个List应包含LINQ条件为true的元素,另一个应包含所有其他元素.这是可能吗?

现在我只使用两个LINQ查询,因此两次迭代(巨大的)主列表.

这是我现在使用的(伪)代码:

List<EventModel> events = GetAllEvents();

List<EventModel> openEvents = events.Where(e => e.Closer_User_ID == null);
List<EventModel> closedEvents = events.Where(e => e.Closer_User_ID != null);
Run Code Online (Sandbox Code Playgroud)

是否可以在不重复原始List两次的情况下产生相同的结果?

小智 9

您可以使用ToLookup扩展方法如下:

 List<Foo> items = new List<Foo> { new Foo { Name="A",Condition=true},new Foo { Name = "B", Condition = true },new Foo { Name = "C", Condition = false } };

  var lookupItems = items.ToLookup(item => item.Condition);
        var lstTrueItems = lookupItems[true];
        var lstFalseItems = lookupItems[false];
Run Code Online (Sandbox Code Playgroud)


Nei*_*eil 5

GroupBy并且Single应该完成你正在寻找的东西:

var groups = events.GroupBy(e => e.Closer_User_ID == null).ToList(); // As others mentioned this needs to be materialized to prevent `events` from being iterated twice.
var openEvents = groups.SingleOrDefault(grp => grp.Key == true)?.ToList() ?? new List<EventModel>();
var closedEvents = groups.SingleOrDefault(grp => grp.Key == false)?.ToList() ?? new List<EventModel>();
Run Code Online (Sandbox Code Playgroud)

  • 实际上这仍然会迭代`events`两次,因为`groups`是懒惰的初始化,所以你最终做了两次分组.在它之后添加一个`ToList`或甚至一个`ToDictionary`来实现值. (2认同)

Har*_*lse 5

您可以通过将其转换为查找表来在一个语句中执行此操作:

var splitTables = events.Tolookup(event => event.Closer_User_ID == null);
Run Code Online (Sandbox Code Playgroud)

这将返回一个由两个元素组成的序列,其中每个元素都是一个IGrouping<bool, EventModel>. 该Key说的顺序是否与空Closer_User_Id,或者不是序列。

然而,这看起来相当神秘。我的建议是使用新函数扩展 LINQ。

此函数采用任何类型的序列,以及将序列分为两组的谓词:与谓词匹配的组和与谓词不匹配的组。

这样您就可以使用该函数将各种IEnumerable序列分成两个序列。

请参阅揭开扩展方法的神秘面纱

public static IEnumerable<IGrouping<bool, TSource>> Split<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource,bool> predicate)
{
    return source.ToLookup(item => item.Condition);
}
Run Code Online (Sandbox Code Playgroud)

用法:

IEnumerable<Person> persons = ...
// divide the persons into adults and non-adults:
var result = persons.Split(person => person.IsAdult);
Run Code Online (Sandbox Code Playgroud)

Result 有两个元素:一个 Key 为 true 的元素包含所有 Adults。

虽然用法现在变得更容易阅读,但您仍然存在处理完整序列的问题,而实际上您可能只想使用其中的几个结果项

让我们返回 an IEnumerable<KeyValuePair<bool, TSource>>,其中布尔值指示项目匹配还是不匹配:

public static IEnumerable<KeyValuePair<bool, TSource>> Audit<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource,bool> predicate)
{
    foreach (var sourceItem in source)
    {
        yield return new KeyValuePair<bool, TSource>(predicate(sourceItem, sourceItem));
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你得到一个序列,其中每个元素都说明它是否匹配。如果您只需要其中的几个,则不会处理序列的其余部分:

IEnumerable<EventModel> eventModels = ...
EventModel firstOpenEvent = eventModels.Audit(event => event.Closer_User_ID == null)
    .Where(splitEvent => splitEvent.Key)
    .FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)

where 说你只想要那些通过审计的审计项目(关键是真的)。

因为您只需要第一个元素,所以不再审计序列的其余部分


归档时间:

查看次数:

623 次

最近记录:

6 年,9 月 前