[英]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.