tags:

views:

578

answers:

5

I have a bit of code that i'd like to turn into a linq expression (preferably with lambdas) to make it easier to use as a delegate. The code looks like this:

List<DateTime[]> changes = new List<DateTime[]>();
changes = PopulateChanges();
for (int i = 0; i < changes.Count; i++)
{
    for(int j = 0; j < changes[i].Length; j++)
    {
        changes[i][j] = DateTime.MinValue;
    }
}

For the life of me, I can't seem to figure this one out. I've tried using ForEach and various forms of select, etc.. nothing seems to work right.

FYI, I know that DateTime defaults to MinValue, in reality this is clearing the arrays to default after they've already been set.

Can anyone help me with a working expression?

EDIT:

I guess what I'm really saying here is I want a concise way to set all elements of a multi-dimensional array to a given value. Certainly, the nested for loop works, and I can certainly place it in a function (which I have already done). I just want something more concise that can be used more easily in a delegate without creating a multi-line monster.

+1  A: 

LINQ is not really intended to be used for updating collections. It's about querying, i.e. create enumerables over existing collections. I think your code is perfectly readable and won't improve much if you force it to have lots of lambdas and such.

If you want to create a new List<DateTime[]> with each element set to a value, you can do this:

var changes = Enumerable.Range(0, width)
                        .Select(x => Enumerable.Range(0, height)
                                               .Select(y => DateTime.MinValue)
                                               .ToArray())
                        .ToList();

If you really want to do it using ForEach/LINQ, it's possible to abuse Select (but I don't recommend it):

changes.ForEach(array => array.Select((dt, i) => array[i] = DateTime.Now)
                              .LastOrDefault());
dtb
I really don't understand why you say it's not meant for this. Think about a typical Linq2SQL query where you query data and then update the values. How is this any different?
Mystere Man
With LINQ2SQL you query the database and then modify the returned objects. If you want to modify all returned objects, you still have to use a loop to iterate over the objects. And the enumerable containing the objects isn't modified at all. LINQ is about querying, not for updating collections.
dtb
+2  A: 

An interesting question, but Linq is about querying and what you're doing here doesn't look much like a query to me. Even if there is a solution out there, I'm not sure I'd use Linq for something like this.

Drew Noakes
I disagree, it is in fact a query, but a query with side effects. I'm querying to return every element of a List<DateTime[]>, so I can then set those values. There are many such queries used in Linq, such as projecting transofrmation, etc.. I don't really see how this is any different.
Mystere Man
There's not a single query in LINQ that modifies the queried collection (hence "query").
dtb
nothing that modifies the original but I think what he is trying to accomplish can be done...
J.13.L
It wouldn't be at all difficult to write a LINQ "query" with side-effects to do what Mystere Man requires, but (a) it would be a dirty hack, and (b) it would probably require as much code as an equivalent, cleaner, non-LINQ solution.
LukeH
A: 

Can you describe some use cases of how you propose to use the said delegate?

I mean, if PopulateChanges() is returning you a List<> of DateTime arrays, and you are going through all of them and setting them to DateTime.MinValue, what exactly is the code unit that you want to put in the delegate?

feroze
All I am doing is creating a working example, as my FYI says.. i'm really caling just the for loop part of it (the rest is just setup) in an area that's different from this. I have another List<string[]> that contains the data, this just maps each element of the string array to a time when they last changed. When changes are cleared, I need to reset the times to MinValue. When new changes occure, then those elements get a new DateTime.
Mystere Man
A: 

There are always best practices and such but I don't know if I agree with everything that everybody has said...

If I understand you correctly you have to enumerate your nested array for several different reasons and would like to encapsulate that functionality to be reused.. There are several different ways to accomplish but here is one using linq and one with out...

List<DateTime[]> changes = new List<DateTime[]>();
changes = PopulateChanges();

// *** As pointed out ForEach this will not change the value in the array, Thanks!***
changes.ForEach(change => change.ToList().ForEach(date => date = DateTime.MinValue));

// this how ever works but is kind of confusing...
changes = changes.Select(change => change.Select(date => DateTime.MinValue).ToArray()).ToList();
//Even that would get old writing it over and over again...

I would do it this way...

private void ForEachOnNestedDates(List<DateTime[]> list, Func<DateTime, DateTime> method)
{
   // Use your code...
   for (int i = 0; i < list.Count; i++)
   {
      for(int j = 0; j < list[i].Length; j++)
      {
          list[i][j] = method.Invoke(list[i][j]);
      }
   }
}

this would allow you to reuse this and do different things to each item. ex.

//Add a Day to each datetime value
ForEachOnNestedDates(changes, (DateTime date) => date.AddDays(1));

//You also make it even more readable
ForEachOnNextedDates(changes, DoSomethingWithDate);

private DateTime DoSomethingWithDate(DateTime value)
{

}

this would be used for thing done to each date time as opposed to thing done with each date time... That would be slightly different.

J.13.L
Assigning a value to a parameter of an lambda does not change the queried collection...
dtb
I stand corrected. The second option should work though...
J.13.L
+2  A: 

This should do the trick.

It's not LINQ, and it's not really much different to your nested for loops, just slightly less verbose.

changes.ForEach(
    x => { for (int i = 0; i < x.Length; i++) x[i] = DateTime.MinValue; });

There are certainly ways to (ab)use LINQ to achieve the same results, but I'd consider them to be dirty hacks. Besides, the LINQ equivalent probably wouldn't be any less verbose than my example above.

LukeH
I like this. Close enough for me, I think.
Mystere Man