按周将数值放在数据表组中

时间:2013-04-09 10:01:12

标签: linq pivot linq-to-objects

我有一个数据表,其中包含从1月1日到3月的值,如下所示:

DATE        Employer    Job1    Job2
1/4/2013        A        1.3    2
1/4/2013        B        2.5    6
1/6/2013        C        3.7    2.4
1/7/2013        D        11 
1/7/2013        F        334    0
1/8/2013        A        1.87   1
1/8/2013        B        6.85   2
1/9/2013        C        58     226
1/16/2013       A        9.43   1.45
1/16/2013       B        5.27   0.6
1/122/2013      C        45.4   5
1/23/2013       A        44     4.78
1/29/2013       B        45     40  
2/2/2013        C        45     54.12
2/2/2013        D         7     4.4587
2/3/2013        F        265    11.486

更新

DataTable datatable = new DataTable("Employee");

datatable.Columns.Add("Date", typeof(string));
datatable.Columns.Add("Employee", typeof(string));
datatable.Columns.Add("Job1", typeof(double));
datatable.Columns.Add("Job2", typeof(double));

datatable.Rows.Add(new Object[] { "1/4/2013", "A", 1.3, 2 });
datatable.Rows.Add(new Object[] { "1/4/2013", "B", 2.5, 6 });
datatable.Rows.Add(new Object[] { "1/6/2013", "C", 3.7, 2.4 });
datatable.Rows.Add(new Object[] { "1/7/2013", "D", 11, 0.0 });
datatable.Rows.Add(new Object[] { "1/7/2013", "F", 334, 0 });
datatable.Rows.Add(new Object[] { "1/8/2013", "A", 1.87, 1 });
datatable.Rows.Add(new Object[] { "1/8/2013", "B", 6.85, 2 });
datatable.Rows.Add(new Object[] { "1/9/2013", "C", 58, 226 });
datatable.Rows.Add(new Object[] { "1/16/2013", "A", 9.43, 1.45 });
datatable.Rows.Add(new Object[] { "1/16/2013", "B", 5.27, 0.6 });
datatable.Rows.Add(new Object[] { "1/22/2013", "C", 45.4, 5 });
datatable.Rows.Add(new Object[] { "1/23/2013", "A", 44, 4.78 });
datatable.Rows.Add(new Object[] { "1/29/2013", "B", 45, 40 });
datatable.Rows.Add(new Object[] { "2/2/2013", "C", 45, 54.12 });
datatable.Rows.Add(new Object[] { "2/2/2013", "D", 7, 4.4587 });
datatable.Rows.Add(new Object[] { "2/3/2013", "F", "265", 11.486 });
datatable.Rows.Add(new Object[] { "3/3/2013", "A", "25", 28.124 });

我想在星期一到星期日的星期开始的时候总结一周的job1值。这是我到目前为止编写的代码。

DateTime minDate = datatable.AsEnumerable()
        .Min(r => DateTime.Parse(r.Field<string>("DATE")));

DateTime startDate = minDate.Date.Date.AddDays(+((6 + minDate.DayOfWeek
                                                    - DayOfWeek.Monday) % 7));

DateTime nextDate = startDate.AddDays(6);

DateTime maxDate = datatable.AsEnumerable()
        .Max(r => DateTime.Parse(r.Field<string>("DATE")));

while (nextDate < maxDate)
{
    var weekEmpGroups = datatable.AsEnumerable()
    .Select(r => new
    {
        Row = r,
        Employee = r.Field<String>("Employee"),
        Date = DateTime.Parse(r.Field<string>("DATE")) 
        // week = minDate.Date.Date.AddDays(+((6 + minDate.DayOfWeek
                                                 - DayOfWeek.Monday) % 7))
    })
    .GroupBy(x => x.Employee);

DataTable dtWeeklyResults = new DataTable();
dtWeeklyResults.Columns.Add("Employee", typeof(string));
var dtf = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat;

double weekCount = 0.0;
string expression;
DataRow[] foundRows;

foreach (var empGroup in weekEmpGroups)
{
    string employee = empGroup.Key;
    var newRow = dtWeeklyResults.Rows.Add();
    newRow["Employee"] = employee;                        

    expression = "Employee=" + employee + " AND Date Between " + startDate
               + " And " + nextDate;
    foundRows = datatable.Select(expression);

    if (foundRows.Length > 0)
    {
        // add values using linq
    }

} 

请建议这是否是正确的方法,以及如何每周添加所有值?对于Job1,结果应如下所示:

Employee  1/7-1/13    1/14-1/20    1/21-1/27    1/28-2/3  and so on...
A        sum of values for this 7 days
B
C
D

有人可以建议如何通过LINQ实现这一目标吗?

2 个答案:

答案 0 :(得分:1)

帮助方法

private static string GetColumnName(int weekNumber)
{
    DateTime jan1 = new DateTime(2013, 1, 1);

    int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
    DateTime firstMonday = jan1.AddDays(daysOffset);

    var cal = ci.Calendar;
    int firstWeek = cal.GetWeekOfYear(firstMonday, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);

    if (firstWeek <= 1)
    {
        weekNumber -= 1;
    }

    DateTime result = firstMonday.AddDays((weekNumber-1) * 7);

    return string.Format("{0}-{1}", result.ToString("M/d", ci), result.AddDays(6).ToString("M/d", ci));
}

private static int GetWeekOfYear(DateTime value)
{
    return ci.Calendar.GetWeekOfYear(value, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
}

CultureInfo实例

static CultureInfo ci = new CultureInfo("en-us");

<强>逻辑

// load parsed data from DataTable to a list
var data = (from row in dt.AsEnumerable()
            select new
                {
                    Date = DateTime.Parse(row.Field<string>("Date"), ci),
                    Employee = row.Field<string>("Employee"),
                    Value = row.Field<double>("Job1")
                }).ToList();

// find min/max date and week number
var minDateTime = data.Select(i => i.Date).Min();
var maxDateTime = data.Select(i => i.Date).Max();
var minWeekNumber = GetWeekOfYear(minDateTime);
var maxWeekNumber = GetWeekOfYear(maxDateTime);

// prepare result DataTable
var resultDt = new DataTable("Job1");
resultDt.Columns.Add("Employee", typeof(string));
for (int i = minWeekNumber; i <= maxWeekNumber; i++)
    resultDt.Columns.Add(i.ToString(), typeof(double));

// prepare grouped data query
var employeeData = from d in data
                    group d by d.Employee into g
                    select new
                        {
                            Employee = g.Key,
                            Items = g.GroupBy(x => GetWeekOfYear(x.Date))
                                    .Select(x => new
                                    {
                                        Week = x.Key,
                                        Value = x.Sum(xx => xx.Value)
                                    })
                        };

// iterate over query results and fill resultsDt
foreach (var e in employeeData)
{
    var newRow = resultDt.NewRow();
    newRow["Employee"] = e.Employee;
    foreach (var d in e.Items)
        newRow[d.Week.ToString()] = d.Value;
    resultDt.Rows.Add(newRow);
}

// change column names from week numbers to proper start-end dates
foreach(DataColumn col in resultDt.Columns)
{
    int weekNumber;
    if (int.TryParse(col.ColumnName, out weekNumber))
    col.ColumnName = GetColumnName(weekNumber);
}

结果:

Job1
Employee  1/7-1/13  1/14-1/20  1/21-1/27  1/28-2/3  2/4-2/10  2/11-2/17  2/18-2/24  2/25-3/3  3/4-3/10  3/11-3/
A         1,3         1,87       9,43        44                                                            2
B         2,5         6,85       5,27                  45
C                     61,7                   45,4      45
D                     11                                7
F                    334                                         265

答案 1 :(得分:0)

如果任何人有相同的要求,以便每周显示结果,那么这里是代码:

          private static DataTable GetWeeklyColumnsAndData(DataTable datatable, string resultFor)
         {
            DateTime minDate = datatable.AsEnumerable()
                               .Min(r => DateTime.Parse(r.Field<string>("DATE")));

            DateTime maxDate = datatable.AsEnumerable()
                               .Max(r => DateTime.Parse(r.Field<string>("DATE")));

            var distinctValues = datatable.AsEnumerable()
                                 .Select(row => new
                                 {
                                    Employee = row.Field<string>("Employee")
                                 })
                                 .Distinct()
                                 .ToList();

           int totalEmployeeCount = System.Linq.Enumerable.Count(distinctValues);

           DataTable resultDt = new DataTable();
           resultDt.Columns.Add("Employee", typeof(string));

           DateTime firstMonday = (minDate.DayOfWeek == DayOfWeek.Monday) ? minDate : GetNextWeekday(minDate, DayOfWeek.Monday);

           DateTime startingMonday = firstMonday;

           // add columns first
           while (firstMonday < maxDate)
           {
             string weekName = string.Format("{0}-{1}", firstMonday.ToString("M/d", ci), firstMonday.AddDays(6).ToString("M/d", ci));
             resultDt.Columns.Add(weekName, typeof(string));
             firstMonday = firstMonday.AddDays(7);
           }

           for (int row = 0; row < totalEmployeeCount; row++)
           {
                            DateTime startDate = startingMonday;
                            DateTime endDate = startingMonday.AddDays(6);

                            DataRow newRow = resultDt.NewRow();

                            string employee = distinctValues[row].Employee.ToString();

                            // first column for entity                
                            newRow[0] = employee;

                            for (int col = 1; col < resultDt.Columns.Count; col++)
                            {
                                bool isBlank = false;
                                double total = 0;
                                string formattedMonday = endDate.ToString("M/d/yyyy");
                                string expression = String.Format("Employee = '{0}' AND DATE >= #{1}# AND DATE <= #{2}#", employee, startDate.ToString("M/d/yyyy"), formattedMonday);

                                DataView dv = datatable.DefaultView;
                                dv.RowFilter = expression;
                                if (dv.Count > 0)
                                {
                                    foreach (DataRowView rowView in dv)
                                    {
                                        DataRow r = rowView.Row;
                                        string value = r[resultFor].ToString();
                                        if (value != "")
                                        {
                                            total += Convert.ToDouble(value);
                                        }
                                        else
                                        {
                                            isBlank = true;
                                        }
                                    }
                                }
                                else
                                {
                                    isBlank = true;
                                }

                                if (total == 0 && isBlank)
                                {
                                    newRow[col] = "";
                                }
                                else
                                {
                                    newRow[col] = total;
                                }
                                startDate = endDate.AddDays(1);
                                endDate = startDate.AddDays(6);
                            }

                            resultDt.Rows.Add(newRow);
                        }

                        return resultDt;
         }



       public static DateTime GetNextWeekday(DateTime start, DayOfWeek day)
       {
             // The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
              int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
              return start.AddDays(daysToAdd);
       }