[英]How can I filter a list using generics and Linq extension methods?
[英]How can I iterate over a collection and change values with LINQ extension methods?
假设我有一组消息,其中包含“UserID”(int)和“Unread”(bool)属性。
对于UserID = 5的集合中的任何Message,如何使用LINQ扩展方法设置Unread = false?
所以,我知道我可以这样做:
messages.Any(m => m.UserID == 5);
但是,如何使用扩展方法设置每个的Unread属性呢?
注意:我知道我不应该在生产代码中这样做。 我只是想学习更多LINQ-fu。
实际上,这可以仅使用没有ToList
的内置LINQ扩展方法。
我相信这将与常规for
循环非常相似。 (我还没检查过)
你不敢在实际代码中这样做。
messages.Where(m => m.UserID == 5)
.Aggregate(0, (m, r) => { m.Unread = false; return r + 1; });
作为额外的奖励,这将返回其修改的用户数量。
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
然后提交更改。
标准LINQ扩展方法不包括针对方法的副作用。 但是,您可以自己实现它,也可以使用Reactive Extensions for .NET(Rx),如下所示:
messages.Where(m => m.UserID == 5).Run(m => m.Unread = false);
由于没有明确的扩展方法可以执行ForEach
,因此您无论是使用辅助库还是自己编写foreach语句。
foreach (Message msg in messages.Where(m => m.UserID == 5))
{
msg.Unread = false;
}
如果您确实想使用Linq语句来完成此操作,请使用ToList()
方法创建集合的副本,访问List
类型的ForEach()
方法:
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
或将副作用放在Where()
语句中:
messages.Where(m =>
{
if (m.UserID == 5) { m.Unread = false; return true; }
return false;
});
在任何一种情况下,我更喜欢使用显式的foreach
循环,因为它不会产生不必要的副本,并且比Where
hack更清晰。
使用LINQ你不能,因为LINQ是一种查询语言/扩展。 然而,有一个名为MoreLinq的项目,它定义了一个名为ForEach的扩展方法,它允许您传递将在每个元素上执行的操作。
所以,你可以使用MoreLinq:
messages.Where(m => m.UserID == 5).ForEach(m => m.Unread = false);
最好的祝福,
奥利弗哈纳皮
这个答案是本着提供解决方案的精神 。 On可以创建一个扩展,它既可以执行谓词( Where
扩展),也可以执行这些项目所需的操作。
下面是一个名为OperateOn
的扩展,它很容易编写:
public static void OperateOn<TSource>(this List<TSource> items,
Func<TSource, bool> predicate,
Action<TSource> operation)
{
if ((items != null) && (items.Any()))
{
items.All (itm =>
{
if (predicate(itm))
operation(itm);
return true;
});
}
}
这是它的实际应用:
var myList = new List<Item>
{ new Item() { UserId = 5, Name = "Alpha" },
new Item() { UserId = 5, Name = "Beta", UnRead = true },
new Item() { UserId = 6, Name = "Gamma", UnRead = false }
};
myList.OperateOn(itm => itm.UserId == 5, itm => itm.UnRead = true);
Console.WriteLine (string.Join(" ",
myList.Select (itm => string.Format("({0} : {1})",
itm.Name,
itm.UnRead ))));
/* Outputs this to the screen
(Alpha : True) (Beta : True) (Gamma : False)
*/
...
public class Item
{
public bool UnRead { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
}
您应该能够在Select()中执行此操作,记住lambda是函数的快捷方式,因此您可以根据需要放置尽可能多的逻辑,然后返回枚举的当前项。 并且......为什么你不能在生产代码中这样做?
messages = messages
.Select(m =>
{
if (m.UserId == 5)
m.Unread = true;
return m;
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.