6

I have this LINQ query:

List<Customers> customers = customerManager.GetCustomers();

return customers.Select(i => new Customer {
    FullName = i.FullName,
    Birthday = i.Birthday, 
    Score = i.Score,
    // Here, I've got more fields to fill
    IsVip = DetermineVip(i.Score)
}).ToList();

In other words, I only want one or two fields of the list of the customers to be modified based on a condition, in my business method. I've got two ways to do this,

  1. Using for...each loop, to loop over customers and modify that field (imperative approach)
  2. Using LINQ projection (declarative approach)

Is there any technique to be used in LINQ query, to only modify one property in projection? For example, something like:

return customers.Select(i => new Customer {
    result = i // telling LINQ to fill other properties as it is
    IsVip = DetermineVip(i.Score) // then modifying this one property
}).ToList();
Mehdi Emrani
  • 1,313
  • 2
  • 14
  • 26

5 Answers5

6

you can use

return customers.Select(i => {
    i.IsVip = DetermineVip(i.Score);
    return i;
}).ToList();
Omid Shariati
  • 1,904
  • 5
  • 22
  • 44
  • One caveat about doing this though is you do have to be a little careful. For example if you do `customer.FullName += "foo"` and you end up enumerating the collection twice, you'll get some pretty interesting results. – lc. Mar 13 '13 at 09:31
2

You "can", if you create a copy constructor, which initializes a new object with the values of an existing object:

partial class Customer
{
    public Customer(Customer original)
    {
        this.FullName = original.FullName;
        //...
    }
}

Then you can do:

return customers.Select(i => new Customer(i) { IsVip = DetermineVip(i.Score)})
    .ToList()

But the downfall here is you will be creating a new Customer object based on each existing object, and not modifying the existing object - this is why I have put "can" in quotes. I do not know if this is truly what you desire.

lc.
  • 113,939
  • 20
  • 158
  • 187
2

Contrary to other answers, you can modify the source content within linq by calling a method in the Select statement (note that this is not supported by EF although that shouldn't be a problem for you).

return customers.Select(customer => 
{
    customer.FullName = "foo";
    return customer;
});
Polity
  • 14,734
  • 2
  • 40
  • 40
  • Not sure, but I got one too. One caveat about doing this though is you do have to be a little careful. For example if you do `customer.FullName += "foo"` and you end up enumerating the collection twice, you'll get some pretty interesting results. – lc. Mar 13 '13 at 09:31
  • lc. perhaps you mistaken `customer` for `customers`. The solution provided will only iterate the collection once. – Polity Mar 13 '13 at 09:34
1

No, Linq was designed to iterate over collections without affecting the contents of the source enumerable.

You can however create your own method for iterating and mutating the collection:

public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach(T item in enumeration)
    {
        action(item);
    }
}

You can then use as follows:

return customers.ToList()
                .ForEach(i => i.IsVip = DetermineVip(i.Score))
                .ToList();

Note that the first ForEach will clone the source list.

ColinE
  • 68,894
  • 15
  • 164
  • 232
0

As customers already is a List, you can use the ForEach method:

customers.ForEach(c => c.IsVip = DetermineVip(c.Score));
nvoigt
  • 75,013
  • 26
  • 93
  • 142