I have a DataColumn of DateTime, I would like to know how I can have only the sooner date (min) and the later date (max).
Thanks
object maxDate = dataTable.Compute("MAX(TheDateColumnName)", null);
object minDate = dataTable.Compute("MIN(TheDateColumnName)", null);
This would give what you are looking for:
// Initial Code for Testing
DataTable dt = new DataTable();
dt.Columns.Add("Dates", typeof(DateTime));
dt.Rows.Add(new object[] { DateTime.Now });
dt.Rows.Add(new object[] { DateTime.Now.AddDays(1) });
dt.Rows.Add(new object[] { DateTime.Now.AddDays(2) });
This is the code you would use:
// Actual Code
DataColumn col = dt.Columns[0]; // Call this the one you have
DataTable tbl = col.Table;
var first = tbl.AsEnumerable()
.Select(cols => cols.Field<DateTime>(col.ColumnName))
.OrderBy(p => p.Ticks)
.FirstOrDefault();
var last = tbl.AsEnumerable()
.Select(cols => cols.Field<DateTime>(col.ColumnName))
.OrderByDescending(p => p.Ticks)
.FirstOrDefault();
To add to the answer from kyle, isn't it easier to just do:
for greatest date:
var last = tbl.AsEnumerable()
.Max(r => r.Field<DateTime>(col.ColumnName));
and for earliestdate:
var first = tbl.AsEnumerable()
.Min(r => r.Field<DateTime>(col.ColumnName));
Just retreive a List of DateTime from your DataColumn,
Foreach row in your DataColumn add the current element to your List of DateTime.
List<DateTime> and use Sort method then get the first and the last values.
Depending of your framework version, for 2.0 use above, for >=3.5
you can use Max and Min
or
With linq .OrderByDesc(p => p.X).FirstOrDefault(); on your DateTime List
DataTable dt = new DataTable("MyDataTable");
DataColumn dc = new DataColumn("DateColumn");
dc.DataType = typeof(DateTime);
dt.Columns.Add(dc);
for (int i = 0; i <= 5; i++)
{
DataRow newRow = dt.NewRow();
newRow[0] = DateTime.Now.AddDays(i);
dt.Rows.Add(newRow);
}
DateTime maxDate =
Convert.ToDateTime(
((from DataRow dr in dt.Rows
orderby Convert.ToDateTime(dr["DateColumn"]) descending
select dr).FirstOrDefault()["DateColumn"]
)
);
DateTime minDate =
Convert.ToDateTime(
((from DataRow dr in dt.Rows
orderby Convert.ToDateTime(dr["DateColumn"]) ascending
select dr).FirstOrDefault()["DateColumn"]
)
);
Simpliest for me:
make a dataset with a Table "Tablename" and a column "itemDate" ,set Datatyp System.DateTime
you can read all elements in a list of datetime and search for minDate
Dim List_Date As New List(Of DateTime)
Dim minDate As DateTime
For Each elem As DataRow In DataSet1.Tables("Tablename").Rows
List_Date.Add(elem.Item("itemDate"))
Next
minDate = List_Date.Min
Related
I want to get a DataColumn (of DataTime type) of my DataTable in List. How can I do this with LINQ?
I tried the following lines but didn't work:
DateTimeList = dt.Columns.Cast<DataColumn>()
.Where(dc => dc.DataType == typeof(DateTime)).ToList());
DateTime values are created like the follow one:
new DateTime(2019, 6, 17, 16, 46, 05)
To return a List<DateTime> type from the DateTime columns in a DataTable:
var dates = dt.Columns.Cast<DataColumn>()
.Where(c => c.DataType == typeof(DateTime))
.SelectMany(c => c.Table.Rows.Cast<DataRow>()
.Select(r => r.Field<DateTime>(c.ColumnName))).ToList();
The query gets all the DateTime values whether the dt contains one or more Column<DateTime> type.
If you have a single DataColumn<DateTime> type in the table, you can write instead:
var dates = dt.Rows
.Cast<DataRow>()
.Select(r => r.Field<DateTime>(dt.Columns.Cast<DataColumn>()
.FirstOrDefault(c => c.DataType == typeof(DateTime))?.ColumnName)).ToList();
I have Data Table with the following data
Number Type Order count
1 1 R 1
1 1 R 1
1 1 R 1
1 2 R 1
I am looking to get to this result
Number Type Order count
1 1 R 3
1 2 R 1
How can I group by three columns
var result = dt.AsEnumerable()
.GroupBy(x => {x.Field<string>("Number"))//need to group by Type and order also need to sum te total counts
rgoal
Your question made me curious, so I did some digging on Stack Overflow.
esc's answer appears will also solve your issue. It is posted under: How do I use SELECT GROUP BY in DataTable.Select(Expression)?:
Applying his method to your problem gave me this solution:
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
This will return a DataTable matching the schema of the input DataTable with the grouping and counts you requested.
Here is the full code I use to verify in LINQPad:
DataTable dt = new DataTable("Demo");
dt.Columns.AddRange
(
new DataColumn[]
{
new DataColumn ( "Number", typeof ( int ) ),
new DataColumn ( "Type", typeof ( int ) ),
new DataColumn ( "Order", typeof ( string ) ),
new DataColumn ( "Count", typeof ( int ) )
}
);
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,2,"R", 1 });
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
foreach (DataRow row in dt2.Rows)
{
for (int i = 0; i < dt2.Columns.Count; i++)
Console.Write("{0}{1}",
row[i], // Print column data
(i < dt2.Columns.Count - 1)? " " : Environment.NewLine); // Print column or row separator
}
Here are the results:
1 1 R 3
1 2 R 1
I have a datatable that looks like this
The output I'm trying to achieve is described in the picture, where I want to group by the month of the datetime, and agent I suppose. This is what I have so far.
DataTable dtTemp = new DataTable();
dtTemp.Columns.Add("Agent", typeof(string));
dtTemp.Columns.Add("Amount", typeof(decimal));
dtTemp.Columns.Add("Date", typeof(DateTime));
dtTemp = dtTemp.AsEnumerable()
.GroupBy(r => r[0])
.Select(g =>
{
var row = dt.NewRow();
row[0] = g.Key;
row[1] = g.Sum(r => (decimal)r[1]);
return row;
})
.CopyToDataTable();
Any ideas how to achieve this?
Thanks in advance!
From analyzing your post, you need to group by data table rows by column Agent and only month and year from column Date.
You need to take one temporary data table dt that can hold data type for each column in your resultant data table result.
So CopyToDataTable() will copy all of your dtTemp group data to new result data table with temporary dt columns data type.
DataTable dt = new DataTable();
dt.Columns.Add("Agent", typeof(string));
dt.Columns.Add("Amount", typeof(decimal));
dt.Columns.Add("Date", typeof(string));
DataTable result = dtTemp.AsEnumerable()
.Select(x => new
{
Agent = x.Field<string>("Agent"),
Amount = x.Field<decimal>("Amount"),
Date = x.Field<DateTime>("Date").ToString("MM-yyyy")
})
.GroupBy(x => new { x.Agent, x.Date })
.Select(g =>
{
var r = dt.NewRow();
r["Agent"] = g.Key.Agent;
r["Amount"] = g.Sum(c => c.Amount);
r["Date"] = g.FirstOrDefault().Date;
return r;
})
.CopyToDataTable();
Output:
var temp = dtTemp.AsEnumerable().GroupBy(grp => new { grpmonth = Convert.ToDateTime(grp["Date"]).Month, grpyear = Convert.ToDateTime(grp["Date"]).Year, grpagent = grp["Agent"] })
.Select(val =>
{
var row = dtTemp.NewRow();
row["Agent"] = val.FirstOrDefault()["Agent"];
row["Amount"] = val.Sum(amt => Convert.ToDecimal(amt["Amount"]));
row["Date"] = val.FirstOrDefault()["Date"];
return row;
}
)
.CopyToDataTable();
For reference
So from your input sequence, you want all used Agents, with the total sum of the Amounts per month.
Let's assume your DataTable is a sequence of Rows, and that it easily can be converted to a sequence of Rows:
class RowData
{
public string Agent {get; set}
public DateTime Date {get; set;}
public int Amount {get; set;}
}
IEnumerable<RowData> tableData = ...
The solution if your problem is to make groups of RowData with equal value for Agent, and to group these groups again to make subgroups with equal value for year and month
var AgentsWithAmountsPerMonth = tableData
.GroupBy(row => row.Agent, // make groups of rows with same Agent
// ResultSelector: get the Agent (=key), with all rows that have this Agent
(agent, rowsWithThisAgent) => new
{
Agent = agent,
// to calculate the totals per year/month, extract the year / month / amount
TotalsPerMonth = rowsWithThisAgent.Select(row => new
{
Year = row.Date.Year,
Month = row.Date.Month,
Amount = row.Amount,
})
// and group by same Year / Month:
.GroupBy(row => new {row.Year, row.Month},
// ResultSelector
(yearMonth, rowsWithThisYearMonth) => new
{
Year = yearMonth.Year,
Month = yearMonth.Month,
Total = rowsWithThisYearMont.Select(row => row.Amount).Sum(),
// Or put the year and month in one field:
Month = new DateTime(yearMonth.Year, yearMonth.Month, 1),
},
});
});
What I m trying to do is relatively simple. I would like to use linq to compute some aggregated function on a group and then put the result back into a datatable of the same format. I did a lot of research and think I should use System.Data.DataSetExtensions and copy to datatable funtion. Here is my random datatable:
DataTable ADataTable = new DataTable("ADataTable");
// Fake table data
ADataTable.Columns.Add("PLANT", typeof(int));
ADataTable.Columns.Add("PDCATYPE_NAME", typeof(int));
ADataTable.Columns.Add("Month", typeof(int));
ADataTable.Columns.Add("Year", typeof(int));
ADataTable.Columns.Add("STATUS_NAME_REPORT", typeof(string));
ADataTable.Columns.Add("SAVINGS_PER_MONTH", typeof(double));
for (int i = 0; i < 15; i++)
{
for (int j = 1; j < 5; j++)
{
DataRow row = ADataTable.NewRow();
row["PLANT"] = j;
row["PDCATYPE_NAME"] = j;
row["Month"] = DateTime.Now.Month;
row["Year"] = DateTime.Now.Year;
row["STATUS_NAME_REPORT"] = "Report";
row["SAVINGS_PER_MONTH"] = j*i;
ADataTable.Rows.Add(row);
}
}
Now I will clone this format and do a simple sum on it via linq:
DataTable newtable = ADataTable.Clone();
// The actual query
IEnumerable<DataRow> query = (from rows in ADataTable.AsEnumerable()
group rows by new
{
PLANT = rows.Field<int>("PLANT"),
PDCATYPE_NAME = rows.Field<int>("PDCATYPE_NAME"),
Month = rows.Field<int>("Month"),
Year = rows.Field<int>("Year"),
STATUS_NAME_REPORT = rows.Field<string>("STATUS_NAME_REPORT")
} into g
select new
{
g.Key.PLANT,
g.Key.PDCATYPE_NAME,
g.Key.Month,
g.Key.Year,
g.Key.STATUS_NAME_REPORT,
sum = g.Sum(savings => savings.Field<double>("SAVINGS_PER_MONTH")),
});
newtable = query.CopyToDataTable<DataRow>();
The LINQ works fine but as soon as I put IEnumarable DataRow in front I get error that I cannot convert anonymys type to datarow. But if I put select new datarow I get an error that fields are unknown...
How do I proceed please?
You have multiple options, First is to use reflection to create a DataTable based on IEnumerable<T> and the other options is to populate your DataTable by enumerating your query like:
var query = ADataTable.AsEnumerable()
.GroupBy(row => new
{
PLANT = row.Field<int>("PLANT"),
PDCATYPE_NAME = row.Field<int>("PDCATYPE_NAME"),
Month = row.Field<int>("Month"),
Year = row.Field<int>("Year"),
STATUS_NAME_REPORT = row.Field<string>("STATUS_NAME_REPORT")
});
foreach (var g in query)
{
newtable.LoadDataRow(new object[]
{
g.Key.PLANT,
g.Key.PDCATYPE_NAME,
g.Key.Month,
g.Key.Year,
g.Key.STATUS_NAME_REPORT,
g.Sum(savings => savings.Field<double>("SAVINGS_PER_MONTH"))
}, LoadOption.OverwriteChanges);
}
The error in your code is because of selecting an anonymous type using select new and then trying to store it in IEnumerable<DataRow>. You can't specify DataRow in select as it is not accessible directly.
You may also see: How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow
This also works:
newtable2 = ADataTable.AsEnumerable().GroupBy(a => new
{
PLANT = a.Field<int>("PLANT"),
PDCATYPE_NAME = a.Field<int>("PDCATYPE_NAME"),
Month = a.Field<int>("Month"),
Year = a.Field<int>("Year"),
STATUS_NAME_REPORT = a.Field<string>("STATUS_NAME_REPORT")
}).Select(g =>
{
var row = newtable2.NewRow();
row.ItemArray = new object[]
{
g.Key.PLANT,
g.Key.PDCATYPE_NAME,
g.Key.Month,
g.Key.Year,
g.Key.STATUS_NAME_REPORT,
g.Sum(r => r.Field<double>("SAVINGS_PER_MONTH"))
};
return row;
}).CopyToDataTable();
using System.Data.DataSetExtensions (Which requires a reference)
I am iterating on a lot of strings and I want to fill my first(and only) 3 Columns with each result and then start again in a new row. like:
A | B | C
------+--------+------
"DOG" | "CAT" | "FISH"
"FDF" | "AAA" | "RRR"
AND SO ON....
Basically after each row is "full" open new row.
HtmlNodeCollection tables = doc.DocumentNode.SelectNodes("//table");
HtmlNodeCollection rows = tables[2].SelectNodes(".//tr");
DataTable dataTable = new DataTable();
dataTable.Columns.Add("A", typeof(string));
dataTable.Columns.Add("B", typeof(string));
dataTable.Columns.Add("C", typeof(string))
try like this
for (int i = 0; i < rows .Count(); i++)
{
DataRow datarowObj= dataTable .NewRow();
datarowObj["A"] = yourValue;
datarowObj["B"] = yourValue;
datarowObj["C"] = yourValue;
dataTable.Rows.Add(datarowObj);
}
You could use Linq's GroupBy to split the long list into groups of 3:
sample-data:
DataTable table = new DataTable();
table.Columns.Add("Col1");
table.Columns.Add("Col2");
table.Columns.Add("Col3");
List<string> longList = Enumerable.Range(1, 99).Select(i => "row " + i).ToList();
group the long list into parts of three:
var groupsWithThree = longList
.Select((s, i) => new { Str = s, Index = i })
.GroupBy(x => x.Index / 3);
add them to the table:
foreach (var group3 in groupsWithThree)
table.Rows.Add(group3.First().Str, group3.ElementAt(1).Str, group3.Last().Str);
Note that it presumes that the list is divisible by three.
Manage with DataRoxw, for instance, after adding an empty DataRow to your DataTable :
DataRow row = table.Rows[0];
foreach (object item in row.ItemArray)
{
?
dataTable.Rows.Add(new object[] { "A1", "B1", "C1" })
// Alternatively
object[] arr = new object[] { "A2", "B2", "C2" };
dataTable.Rows.Add(arr);