5

Is there a way to do the following using Linq:

foreach (var c in collection)
{
    if (c.Condition == condition)
    {
        c.PropertyToSet = value;
        // I must also check I only set this value to one minimum and only one element.
    }
    else
    {
        c.PropertyToSet = otherValue;
    }
}

To clarify, I want to iterate through each object in a collection and then update a property on each object except for one element of my collection that should updated to another value.

At this moment I use a counter to check I set my value to one and only one element of my collection. I removed it from this example to let people suggest other solutions.

The original question without exception in collection is here

EDIT

I ask this question because I'm not sure it's possible to do it with LinQ. so your answers comfort my opinion about LinQ. Thank you.

Community
  • 1
  • 1
Bastien Vandamme
  • 17,659
  • 30
  • 118
  • 200
  • 2
    Are you _changing_ your original collection or would you like to return a copy with that property modified? – Benjamin Gruenbaum Mar 13 '13 at 15:28
  • I'm changing my collection. what is the difference? – Bastien Vandamme Mar 13 '13 at 15:29
  • What type of collection is this.. Is this a Hashtable, Dictionary<>, List<>, etc..please clarify what type of collection this is. you could accomplish this using a Hashtable or a Dictionary<> can you show how collection is defined – MethodMan Mar 13 '13 at 15:33
  • 1
    @B413: The difference is whether you want to mutate the original entities or not. If you use `.Select(x => new x with property changed)`, the original collection is untouched. If you want to modify the original, then you need something like `.ToList().ForEach(c => mutate c)`. – mellamokb Mar 13 '13 at 15:33
  • 3
    Linq is for querying, not for modification. You can get objects which match your condition, or which don't. I think foreach is the best option here (it enumerates only once) – Sergey Berezovskiy Mar 13 '13 at 15:34
  • 1
    It's interesting to see how a seemingly simple task evolves into Quasimodo's uglier brother if Linq is forced into the mix. – Piotr Justyna Mar 13 '13 at 15:39
  • @DJ KRAZE consider a list<> – Bastien Vandamme Mar 13 '13 at 15:51

6 Answers6

8

You can use .ForEach to make the change, and .Single to verify only one element matches the condition:

// make sure only one item matches the condition
var singleOne = collection.Single(c => c.Condition == condition);
singleOne.PropertyToSet = value;

// update the rest of the items
var theRest = collection.Where(c => c.Condition != condition);
theRest.ToList().ForEach(c => c.PropertyToSet = otherValue);
mellamokb
  • 56,094
  • 12
  • 110
  • 136
6

I don't suggest you to implement this with Linq. Why? Because Linq is for querying, not for modification. It can return you objects which match some condition, or objects which don't match. But for updating those objects you still need to use foreach or convert query results to list and use ForEach extension. Both will require enumerating sequence twice.

So, simple loop will do the job:

foreach (var c in collection)
{
    c.PropertyToSet = (c.Condition == condition) ? value : otherValue;
}
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
2
collection.Where(x => <condition>).ToList().ForEach(x => <action>);
Alexey
  • 1,826
  • 3
  • 17
  • 20
2

Hacky way to use LINQ if you persist to use:

var result = collection.Select(c =>
{
    c.PropertyToSet = c.Condition == condition ? value : otherValue;
    return c;
});

But my recommendation, don't do this, you code actually get the best approach, for more readability, you can change:

foreach (var c in collection)
   c.PropertyToSet = c.Condition == condition ? value : otherValue;
cuongle
  • 74,024
  • 28
  • 151
  • 206
  • It's possibly dangerous if you pass around the unenumerated IEnumerable. If the `c.PropertyToSet ...` line is not idempotent, "accidentally" iterating more than once would yield unexpected results. – Austin Salonen Mar 13 '13 at 16:43
2

You can use a ternary operator in conjunction with a linq statement:

    collection.ToList().ForEach(c => c.PropertyToSet = c.Condition == condition ? value : otherValue);

However I woud just use a regular foreach here to avoid converting the collection to a list.

Dmitriy Khaykin
  • 5,238
  • 1
  • 20
  • 32
1

Well, you could do:

var itemToSetValue = collection.FirstOrDefault(c => c.Condition == condition);

if(itemToSetValue != null)
     itemToSetValue.PropertyToSet = value;

// Depending on what you mean, this predicate 
// might be c => c != itemToSetValue instead.
foreach (var otherItem in collection.Where(c => c.Condition != condition))
{
    otherItem.PropertyToSet = otherValue;
}

Now of course that's not a pure LINQ solution, but pure LINQ solutions are not appropriate for modifying existing collections.

Ani
  • 111,048
  • 26
  • 262
  • 307