views:

165

answers:

4

I'm using C# and ASP.NET 3.5. Basically I'm retrieving a column of data from a dataset and putting this into a list like so:

    List<String> dates = new List<String>();

    foreach (DataRow rowMonth in myDS.Tables[0].Rows)
    {
        string ListedMonthYear = (string)rowMonth[0];
        dates.Add(ListedMonthYear);
    }

The returned values are:

 Apr-10
 Mar-10
 Feb-10
 Jan-10
 Dec-09
 Nov-09
 Oct-09

I'm interested in splitting these values into two lists with the idea of performing operations on them in the future.

Apr   |  2010
Mar   |  2010
Feb   |  2010
Jan   |  2010
Dec   |  2009
Nov   |  2009
Oct   |  2009

What is the best way to do so?

EDIT: rowMonth is just the datarow that includes all date related values - the month-year, the month beginning, month ending, month active or inactive. Basically I'm just trying to extract that first column month-year to do operations on and ignore the rest.

+5  A: 

You can use LINQ:

var months = (from item in dates
              select item.Substring(0, 3))
             .ToList();

var years  = (from item in dates
              select 2000 + int.Parse(item.Substring(4, 2)))
             .ToList();

You might prefer storing the two parts separately in the same list:

var result = (from item in dates
              select Tuple.Create(item.Substring(0, 3),
                                  2000 + int.Parse(item.Substring(4, 2))))
             .ToList();
dtb
@dtb: I think you mean `item.Substring(0, 3))`
Yuriy Faktorovich
@Yuriy Faktorovich: Indeed. Thanks.
dtb
I think Tuple might be 2010 feature - is there anything similar in 2008?
firedrawndagger
@firedrawndagger: No, but you easily define your own class that holds a string and an integer.
dtb
+2  A: 

If you only have dates after 2000, you could do the following:

dates.Select(d => d.Split('-')).Select(parts => new { Month = parts[0], Year = "20"+parts[0]})
sgriffinusa
That only works until 2100, didnt you learn anything from Y2K???
rmx
I'll be long gone by then, so its not my problem!! I figure if any code that I have written lasts until 2100, I'll be so much of a legend that DateTime issues won't be a problem. </sarcasm>
sgriffinusa
Yeah, that's what they said in 1965.
mquander
I certainly hope I don't have to support this application for another 90 years.
firedrawndagger
+4  A: 

This way you won't be iterating over the list twice. But it only works for years 2k and above, and I assumed you wanted years as a list of integers.

List<string> months = new List<string>();
List<int> years = new List<int>();


dates.ForEach(s =>
            {
                months.Add(s.Substring(0, 3));
                years.Add(2000 + int.Parse(s.Substring(4, 2)));
            });
Yuriy Faktorovich
ToList just for ForEach? Dude!
dtb
@dtb I was under the impression that the benchmarks put it at way faster than doing a foreach loop.
Yuriy Faktorovich
+1 This is the better performing code, although we have no indication of whether the lists will get very big.
Daniel Dyson
@Yuriy Faktorovich: `enumerable.ToList().ForEach` is faster than `foreach (var x in enumerable)`? Interesting. Link?
dtb
@dtb: I've done the benchmarks, and my current version is the fastest. It combines our two answers. I'm looking for the benchmarks on `ToList().ForEach` vs `foreach`.
Yuriy Faktorovich
@dtb: you're right, if you don't have a list to begin with, it is slower.
Yuriy Faktorovich
@dtb. Yes, your edited answer is faster
Daniel Dyson
ToList().ForEach is faster than foreach
Venkat
@Venkat I couldn't find an article that agreed, and my own tests showed different.
Yuriy Faktorovich
+2  A: 

I would consider using a DateTime object instead of string so that you can derive the date/time representation on-the-fly, and not have to maintain two lists.

For instance, your original parsing method could be changed to the following.

List<DateTime> dates = new List<DateTime>();

foreach (DataRow rowMonth in myDS.Tables[0].Rows)
{
    DateTime rowDate = DateTime.Parse(rowMonth[0]);
    dates.Add(rowDate);
}

Then to produce the two required lists, you can use linq as follows.

// Converting to lists to demonstrate, though may not be required in your app.
List<short> years = dates.Select(d => d.Year).ToList();
List<string> months = dates.Select(d =>
    CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(d.Month)).ToList();

Note this isn't exactly what you wanted as there are some small caveats.

  • DateTime.Parse may need to be tailored with a format string if the date in the row isn't a well-known date format.
  • GetMonthName() returns the full month name; you can hack a solution to take the first three characters of the string, but there is probably a more elegant solution.
Steve Guidi
+1. If it is possible to use `DateTimes` instead of `strings` for this project, do it. It makes more sense and is much easier to get the individual time components, instead of having to rely on creating substrings.
jloubert