Change a list's property using linq - c#

How to make the following code shorter, perhaps using anonymous method or extensions and LINQ.
Since I have to repeat this code several times and I want to make it as succinct as possible.
var imagesToUnlock = App.ImageListVM.Items.Where(img => img.Category == key);
foreach (var image in imagesToUnlock)
{
image.IsLocked = false;
}

The other solutions here feel dirty because they mutate objects in a collection via the use of LINQ.
I would instead, put the code and the filter condition into an extension method and call that:
public static IEnumerable<Item> UnlockWhere(this IEnumerable<Item> list, Func<Item, bool> condition) {
foreach (var image in list)
if (condition(image)) {
image.IsLocked = false;
yield return image;
}
}
The keeps the immutability-concerns of LINQ intact and still produces the expected result.
The call becomes:
var unlockedItems = App.ImageListVM.Items.UnlockWhere(img => img.Category == key);
EDIT
Re-written to completely remove LINQ. Instead, this new method iterates only once and returns a new, mutated collection.

Not the most efficient way to do it, but I believe you can do
var imagesToUnlock = App.ImageListVM.Items.Where(img => img.Category == key).ToList().Foreach(f => f.IsLocked = false);
Check out the Foreach method on List<T> for more info.
I would also like to note (as some have pointed out in the comments) that this is not considered best practice by some people. You should take a look at this article by Eric Lippert, who explains the issue in better detail.

Here's a stab as an extension method
Code
public static IEnumerable<T> SetPropertyValues<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var item in items)
{
action(item);
yield return item;
}
}
Usage
private class Foo
{
public string Bar { get; set; }
}
[TestMethod]
public void SetPropertyValuesForMiscTests()
{
var foos = new[] { new Foo { Bar = "hi" }, new Foo { Bar = "hello" } };
var newList = foos.SetPropertyValues(f => f.Bar = "bye");
Assert.AreEqual("bye", newList.ElementAt(0).Bar);
Assert.AreEqual("bye", newList.ElementAt(1).Bar);
}
I tested it and it works fine.

Yeah you can do this. Adapted from this answer.
imagesToUnlock.Select(i => {i.IsLocked = false; return i;}).ToList();
Edit: A lot of people are saying this is bad practice. I agree with dasblinkenlight here.. Exploring the limits of LINQ and C# is our duty as programmers. It isn't unreasonable to change the objects type from the DTO to the view model or domain object, I know its not the best, but if encapsulated and commented it isn't the end of the world to use select to do this. But please be conscious of the best practices explained by Eric.

Related

IEnumerable performs differently on Array vs List

This question is more of a "is my understanding accurate", and if not, please help me get my head around it. I have this bit of code to explain my question:
class Example
{
public string MyString { get; set; }
}
var wtf = new[] { "string1", "string2"};
IEnumerable<Example> transformed = wtf.Select(s => new Example { MyString = s });
IEnumerable<Example> transformedList = wtf.Select(s => new Example { MyString = s }).ToList();
foreach (var i in transformed)
i.MyString = "somethingDifferent";
foreach (var i in transformedList)
i.MyString = "somethingDifferent";
foreach(var i in transformed)
Console.WriteLine(i.MyString);
foreach (var i in transformedList)
Console.WriteLine(i.MyString);
It outputs:
string1
string2
somethingDifferent
somethingDifferent
Both Select() methods at first glance return IEnumerable< Example>. However, underlying types are WhereSelectArrayIterator< string, Example> and List< Example >.
This is where my sanity started to come into question. From my understanding the difference in output above is because of the way both underlying types implement the GetEnumerator() method.
Using this handy website, I was able to (I think) track down the bit of code that was causing the difference.
class WhereSelectArrayIterator<TSource, TResult> : Iterator<TResult>
{ }
Looking at that on line 169 points me to Iterator< TResult>, since that's where it appears GetEnumerator() is called.
Starting on line 90 I see:
public IEnumerator<TSource> GetEnumerator() {
if (threadId == Thread.CurrentThread.ManagedThreadId && state == 0) {
state = 1;
return this;
}
Iterator<TSource> duplicate = Clone();
duplicate.state = 1;
return duplicate;
}
What I gather from that is when you enumerate over it, you're actually enumerating over a cloned source (as written in the WhereSelectArrayIterator class' Clone() method).
This will satisfy my need to understand for now, but as a bonus, if someone could help me figure out why this isn't returned the first time I enumerate over the data. From what I can tell, the state should = 0 the first pass. Unless, perhaps there is magic happening under the hood that is calling the same method from different threads.
Update
At this point I'm thinking my 'findings' were a bit misleading (damn Clone method taking me down the wrong rabbit hole) and it was indeed due to deferred execution. I mistakenly thought that even though I deferred execution, once it was enumerated the first time it would store those values in my variable. I should have known better; after all I was using the new keyword in the Select. That said, it still did open my eyes to the idea that a particular class' GetEnumerator() implementation could still return a clone which would present a very similar problem. It just so happened that my problem was different.
Update2
This is an example of what I thought my problem was. Thanks everyone for the information.
IEnumerable<Example> friendly = new FriendlyExamples();
IEnumerable<Example> notFriendly = new MeanExamples();
foreach (var example in friendly)
example.MyString = "somethingDifferent";
foreach (var example in notFriendly)
example.MyString = "somethingDifferent";
foreach (var example in friendly)
Console.WriteLine(example.MyString);
foreach (var example in notFriendly)
Console.WriteLine(example.MyString);
// somethingDifferent
// somethingDifferent
// string1
// string2
Supporting classes:
class Example
{
public string MyString { get; set; }
public Example(Example example)
{
MyString = example.MyString;
}
public Example(string s)
{
MyString = s;
}
}
class FriendlyExamples : IEnumerable<Example>
{
Example[] wtf = new[] { new Example("string1"), new Example("string2") };
public IEnumerator<Example> GetEnumerator()
{
return wtf.Cast<Example>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return wtf.GetEnumerator();
}
}
class MeanExamples : IEnumerable<Example>
{
Example[] wtf = new[] { new Example("string1"), new Example("string2") };
public IEnumerator<Example> GetEnumerator()
{
return wtf.Select(e => new Example(e)).Cast<Example>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return wtf.Select(e => new Example(e)).GetEnumerator();
}
}
Linq works by making each function return another IEnumerable that is typically a deferred processor. No actual execution occurs until an enumeration of the finally returned Ienumerable occurs. This allows for the create of efficient pipelines.
When you do
var transformed = wtf.Select(s => new Example { MyString = s });
The select code has not actually executed yet. Only when you finally enumerate transformed will the select be done. ie here
foreach (var i in transformed)
i.MyString = "somethingDifferent";
Note that if you do
foreach (var i in transformed)
i.MyString = "somethingDifferent";
the pipeline will be executed again. Here thats is not a big deal but it can be huge if IO is involved.
this line
var transformedList = wtf.Select(s => new Example { MyString = s }).ToList();
Is the same as
var transformedList = transformed.ToList();
The real eyeopener is to place debug statements or breakpoints inside a where or select to actually see the deferred pipeline execution
reading the implementation of linq is useful. here is select https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,5c652c53e80df013,references

C#: Update Item value in List<of T>

I'm looking for a way to update an Element in a List without enumerating it on my own.
I got the Class MyProjects which hold a List named Projects.
I want to find the MyProjects.Projects-Class, where a member property of Class1 (Name) equals the Value "Overhead".
What works:
foreach (Project prj in MyProjects.Projects) {
if (prj.Name == "Overhead")
prj.IsActive = true;
};
I, however, try to do the same by using Linq, but failed in writing it as one line. Is this even possible? The reason why I don't like to iterate in the way above is that I already iterate the whole list in this codeblock and think, that there might be a more beautiful way :)
You shouldn't try to get everything down to one line - just as brief as is readable. In this case, you can use:
foreach (var project in MyProjects.Projects.Where(p => p.Name == "Overhead"))
{
project.IsActive = true;
}
That's using LINQ for the querying part, which is appropriate as that's what the Q of LINQ stands for. I'd strongly urge you not to mutate items within LINQ calls in the way that Mayank's answer does though. It's error-prone (as evidenced by the original answer not working) and against the spirit of LINQ.
That's about as readable as it gets, IMO. It does exactly the same thing as the original code, mind you - you can't avoid something iterating over every item in the list, if every item might be one you want to update.
EDIT: Just for laughs, if you really, really wanted to do it in pretty minimal code, you could use:
// DON'T USE THIS!
MyProjects.Project.Count(p => p.Name == "Overhead" && (p.IsActive = true));
Here we use the fact that && is short-circuiting to avoid evaluating the assignment (p.IsActive = true) unless the condition is matched. It's handy that we're assigning a bool value to a property, as that means we don't need to do anything else to make it a valid second operand for the && operator. We use Count() to fully evaluate the result without creating any additional lists etc - and we use the version with a predicate to avoid even needing a Where call, which a previous version did. (LastOrDefault would work too.) But it's all a horrible abuse, and should never appear in any real code.
I've come up with a way to get it down to one line, without abusing LINQ, since I'm only using it for the querying part (filter), and using a custom extension method to perform the property setting action. You're still going to enumerate the items (you have to) but you can hide that away in the extension method. I suspect that you didn't really care whether you enumerated the item or not, you just didn't like the amount of visible space a foreach loop would take up in your main code.
Use this extension method:
public static IEnumerable<T> SetProperty<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (var item in list)
{
action.Invoke(item);
}
return list;
}
This allows you to get it down to one readable line.
Projects.Where(p => p.Name == "Overhead").SetProperty(p => p.IsActive = true);
Complete test program:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var Projects = new List<Project>() {
new Project() { Name="Overhead", IsActive=false },
new Project() { Name="Nadfadfs", IsActive=false },
new Project() { Name="Overhead", IsActive=false },
new Project() { Name="dasfasdf", IsActive=false }
};
PrintProjectList(Projects);
Console.WriteLine("--Setting property--");
Projects.Where(p => p.Name == "Overhead").SetProperty(p => p.IsActive = true);
PrintProjectList(Projects);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
static void PrintProjectList(IEnumerable<Project> projects)
{
foreach(var p in projects)
{
Console.WriteLine($"Name: {p.Name} IsActive: {p.IsActive}");
}
}
}
class Project
{
public string Name { get; set; }
public bool IsActive { get; set; }
}
public static class Extensions
{
public static IEnumerable<T> SetProperty<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (var item in list)
{
action.Invoke(item);
}
return list;
}
}
}
Output:
Name: Overhead IsActive: False
Name: Nadfadfs IsActive: False
Name: Overhead IsActive: False
Name: dasfasdf IsActive: False
--Setting Property--
Name: Overhead IsActive: True
Name: Overhead IsActive: False
Name: Overhead IsActive: True
Name: Overhead IsActive: False
It turns out that my SetProperty function is very similar to the ForEach that's already built into the framework. The main difference being that mine can operate on any IEnumerable<T>. That syntax is loved by some, and hated by others, for reasons that Eric Lippert pointed out on his blog (Thanks to Jon Skeet for pointing this out). Also, see this discussion by the Microsoft team. I'll leave it to you to draw your own conclusion.
On a side note, calling it SetProperty is kind of inaccurate, because you could do any action on the items in the collection. You could call it ForEach, but that clashes with the framework. Not positive what I'd call it, but perhaps PerformAction.
Following Linq statement should work
MyProjects.Projects.Where(p => p.Name == "Overhead")
.Select(x => {x.IsActive = true; return x;})
.ToList();
As per comments from #JonSkeet, #TimSchmelter and #LeandroSoares, above code is really bad idea. Here are some reasons
Calling .ToList() causes whole lazy collection to be executed and loaded in the memory.
Code above isn't very readable and hard to maintain.
Code above is abusing the API as it is forcing the API to do things which it is not designed for.

What is the best way to trim a list?

I have a List of strings. Its being generated elsewhere but i will generate it below to help describe this simplified example
var list = new List<string>();
list.Add("Joe");
list.Add("");
list.Add("Bill");
list.Add("Bill");
list.Add("");
list.Add("Scott");
list.Add("Joe");
list.Add("");
list.Add("");
list = TrimList(list);
I would like a function that "trims" this list and by trim I want to remove all items at the end of the array that are blank strings (the final two in this case).
NOTE: I still want to keep the blank one that is the second item in the array (or any other one that is just not at the end) so I can't do a .Where(r=> String.isNullOrEmpty(r))
I would just write it without any LINQ, to be honest- after all, you're modifying a collection rather than just querying it:
void TrimList(List<string> list)
{
int lastNonEmpty = list.FindLastIndex(x => !string.IsNullOrEmpty(x));
int firstToRemove = lastNonEmpty + 1;
list.RemoveRange(firstToRemove, list.Count - firstToRemove);
}
If you actually want to create a new list, then the LINQ-based solutions are okay... although potentially somewhat inefficient (as Reverse has to buffer everything).
Take advantage of Reverse and SkipWhile.
list = list.Reverse().SkipWhile(s => String.IsNullOrEmpty(s)).Reverse().ToList();
List<T> (not the interface) has a FindLastIndex method. Therefore you can wrap that in a method:
static IList<string> TrimList(List<string> input) {
return input.Take(input.FindLastIndex(x => !string.IsNullOrEmpty(x)) + 1)
.ToList();
}
This produces a copy, whereas Jon's modifies the list.
The only solution I can think of is to code a loop that starts at the end of the list and searches for an element that is not an empty string. Don't know of any library functions that would help. Once you know the last good element, you know which ones to remove.
Be careful not to modify the collection while you are iterating over it. Tends to break the iterator.
I always like to come up with the most generic solution possible. Why restrict yourself with lists and strings? Let's make an algorithm for generic enumerable!
public static class EnumerableExtensions
{
public static IEnumerable<T> TrimEnd<T>(this IEnumerable<T> enumerable, Predicate<T> predicate)
{
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
var accumulator = new LinkedList<T>();
foreach (var item in enumerable)
{
if (predicate(item))
{
accumulator.AddLast(item);
}
else
{
foreach (var accumulated in accumulator)
{
yield return accumulated;
}
accumulator.Clear();
yield return item;
}
}
}
}
Use it like this:
var list = new[]
{
"Joe",
"",
"Bill",
"Bill",
"",
"Scott",
"Joe",
"",
""
};
foreach (var item in list.TrimEnd(string.IsNullOrEmpty))
{
Console.WriteLine(item);
}

C# List<t>.ForEach(delegate(type t) and how to remove items as the list is iterated through

This may be considered bad programming, but prior to .net 4, I used to heavily use code similar to this:
enemyList.ForEach(delegate(Enemy e)
{
e.Update();
if (someCondition)
enemyList.Remove(e);
});
Now, I'm going through an updating some old projects, and there are a LOT of code thats going to have to be changed since ForEach was removed.. Now, I do have an extension to allow me to use the ForEach :
public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action)
{
if (sequence == null) throw new ArgumentNullException("sequence");
if (action == null) throw new ArgumentNullException("action");
foreach (T item in sequence)
action(item);
}
I know I can do this:
var index = 0;
while(index < enemyList.Count)
{
if(condition)
enemyList.RemoveAt(index);
else
index++;
}
But some of those would be a pain to rewrite like that.. Is there any way to add that functionality back so that I can iterate through that list, remove the items I need without having to go back and rewrite and edit all of those functions ? I still consider myself a newbie to coding, and I just can't figure this one out.. Any help would be appreciated!
********* EDIT *********
So I guess it boils down to rewriting a lot of code.. I have a lot of code such as this that I just pulled out of a project:
GameplayScreen.gameWorld.shipList.ForEach(delegate(Ship s)
{
if (eS.originalShipsID == s.shipID)
{
if (!eS.Alive || eS.health <= 0)
{
// this one sunk...
string log = "0" + s.shipName + " was sunk in battle.. All crew and cargo were lost.";
AddLogEntry(log);
totalCrewLost += s.currentCrew;
GameplayScreen.gameWorld.shipList.Remove(s);
}
}
});
I was just hoping there was a way to not have to rewrite all of that.. So time to update and change the way I code apparently. Thanks!
Use the list's RemoveAll method.
You can refactor the code to:
enemyList.RemoveAll(enemy => enemy.SomeCondition);
Not only is it better than the while loop, I'd argue it's quite a bit better than the Foreach method.
You can't. The only way would be to add the items to remove to another list and then iterate over that list and remove them after the initial iteration.
A better option would be to use a reverse for loop to iterate over the values. You can then safely remove the items during the initial iteration:
for (var i = enemyList.Count() - 1; i >= 0; i--) {
{
if(condition) enemyList.RemoveAt(i);
}
Since you said you do it a lot, why not do something like this:
public static void RemoveIfTrue<T>(this ICollection<T> list, Func<T, bool> condition)
{
List<T> itemsToRemove = list.Where(condition).ToList();
foreach (var item in itemsToRemove)
{
list.Remove(item);
}
}
Then you could use it like:
myList.RemoveIfTrue(x => YourConditionIsTrue)
That way you don't have a bunch of duplication of logic.
If you're using a List<T>, you can use List<T>.RemoveAll(Predicate<T> match)
So there is a built-in thing to do this already.
Even better - the built-in one knows exactly how to avoid problems modifying the collection while iterating over it. And because it has access to the private internals, it's more efficient too.
So, just using the List class itself you can write code like this:
enemies.RemoveAll(enemy => (enemy.Health <= 0));
This is possible with a small tweak. Here's an example:
public static class Extensions
{
public static void ForEach<T>(this IList<T> list, Action<T> action)
{
for (int i = 0; i < list.Count; i++)
{
action(list[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
List<string> vals = new List<string>(new string[] { "a", "bc", "de", "f", "gh", "i", "jk" });
vals.ToList().ForEach<string>(delegate(string value)
{
if (value.Length > 1)
{
vals.Remove(value);
}
});
vals.ToList().ForEach<string>(delegate(string value)
{
Console.WriteLine(value);
});
Console.ReadKey();
}
}
Now, there are a couple of things worth mentioning here: first, normally elements would be skipped. However, a separate copy of the list is made by calling ToList(). Second, you should be careful to do this only with reference types - i.e. not with primitive types - otherwise you'll remove more than a single element with the remove method.
EDIT
I'd also like to add that probably any of the posted alternatives are better - but I thought it was interesting that this could be done; it's less performant but probably quicker to chuck into existing code.

In a generic list, is there a way to copy one property to another in a declarative /LINQ manner?

I have a class with two properties, say
public class Book {
public string TitleSource { get; set; }
public string TitleTarget { get; set; }
}
I have an IList<Book> where the TitleTarget is null and for each item in the list, I need to copy the TitleSource property to the TitleTarget property. I could do this through a loop, sure, but it seems like there's a LINQ or nice declarative way to do this. Is there?
Linq was designed as a way to consume things. If you look at web discussions about why there is no IEnumerable.ForEach(...) extension, you'll see that the Linq designers purposefully avoided Linq to Object scenarios where the methods were designed to change object values.
That said, you can cheat by "selecting" values and not using the results. But, that creates items which are thrown away. So, a foreach loop is much more efficient.
Edit for people who really want something besides foreach
Another "cheat" that wouldn't produce a new list would be to use a method that does little work of it's own, like Aggregate, All, or Any.
// Return true so All will go through the whole list.
books.All(book => { book.TitleTarget = book.TitleSource; return true; });
It's not LINQ as such, but there's:
books.Where(book => book.TitleTarget == null).ToList()
.ForEach(book => book.TitleTarget = book.TitleSource);
The main point is the ToList method call: there's no ForEach extension method (I don't think?) but there is one on List<T> directly. It wouldn't be hard to write your own ForEach extension method as well.
As to whether this would be better than a simple foreach loop, I'm not so sure. I would personally choose the foreach loop, since it makes the intention (that you want to modify the collection) a bit clearer.
#John Fisher is correct, there is no IEnumerable.ForEach.
There is however a ForEach on List<T>. So you could do the following:
List<Book> books = GetBooks();
books.ForEach(b => b.TitleTarget = b.TitleSource);
If you wanted a IEnumerable.ForEach it would be easy to create one:
public static class LinqExtensions
{
public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
foreach (var item in source)
{
action(item);
}
}
}
You can then use the following snippet to perform your action across your collection:
IList<Book> books = GetBooks();
books.ForEach(b => b.TitleTarget = b.TitleSource);
If you can use .NET 4.0, and you are using a thread-safe collection then you can use the new parallel ForEach construct:
using System.Threading.Tasks;
...
Parallel.ForEach(
books.Where(book => book.TitleTarget == null),
book => book.TitleTarget = book.TitleSource);
This will queue tasks to be run on the thread pool - one task that will execute the assignment delegate for each book in the collection.
For large data sets this may give a performance boost, but for smaller sets may actually be slower, given the overhead of managing the thread synchronization.
books.Select(b => b.TitleTarget = b.TitleSource);
This doesn't create any 'new items', just a query that you won't enumerate. That doesn't seem like a big deal to me.

Categories

Resources