Lets say I have a collection of Messages which has the properties "UserID" (int) and "Unread" (bool).
How can I use LINQ extension methods to set Unread = false, for any Message in the collection in whose UserID = 5?
So, I know I can do something like:
messages.Any(m => m.UserID == 5);
But, how do I set the Unread property of each of those with an extension method as well?
Note: I know I should not do this in production code. I'm simply trying to learn some more LINQ-fu.
Actually, this is possible using only the built-in LINQ extension methods without ToList
.
I believe that this will perform very similarly to a regular for
loop. (I haven't checked)
Don't you dare do this in real code.
messages.Where(m => m.UserID == 5)
.Aggregate(0, (m, r) => { m.Unread = false; return r + 1; });
As an added bonus, this will return the number of users that it modified.
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
然后提交更改。
Standard LINQ extension methods doesn't include side effects aimed methods. However you can either implement it yourself or use from Reactive Extensions for .NET (Rx) like this:
messages.Where(m => m.UserID == 5).Run(m => m.Unread = false);
As there is no explicit extension method that does a ForEach
, you are stuck with either using a secondary library, or writing the foreach statement on your own.
foreach (Message msg in messages.Where(m => m.UserID == 5))
{
msg.Unread = false;
}
If you really want to use a Linq statement to accomplish this, create a copy the collection using the ToList()
method, accessing the ForEach()
method of the List
type:
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
or place the side-effect in a Where()
statement:
messages.Where(m =>
{
if (m.UserID == 5) { m.Unread = false; return true; }
return false;
});
In either case, I prefer to use the explicit foreach
loop as it doesn't make unnecessary copies and is clearer than the Where
hack.
With LINQ you can't because LINQ is a query language/extension. There is however a project called MoreLinq , which defines an extension method called ForEach which allows you to pass an action which will be performed on every element.
So, you could do with MoreLinq:
messages.Where(m => m.UserID == 5).ForEach(m => m.Unread = false);
Best Regards,
Oliver Hanappi
This answer is in the spirit of providing a solution. On could create an extension which does both the predicate ( Where
extension) to weed out the items and the action needed upon those items.
Below is an extension named OperateOn
which is quite easy to write:
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;
});
}
}
Here is it in action:
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; }
}
You should be able to just do it in a Select(), remember the lambda is a shortcut for a function, so you can put as much logic in there as you want, then return the current item being enumerated. And... why exactly wouldn't you do this in production code?
messages = messages
.Select(m =>
{
if (m.UserId == 5)
m.Unread = true;
return m;
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.