LINQ Order DataTable based on string date - vb.net

I have a dataTable with column "date" which has dates in string format.
I can't sort it with LINQ.
Table example
ID date
1 20.02.2022
2 15.05.2021
3 03.07.2019
This is the LINQ expression I have so far, but it always says String was not recognized as a valid DateTime
(From x In dt.AsEnumerable()
Order By Convert.ToDateTime(x("date").ToString)
Select x).CopyToDataTable
I've also tried this, but with the same result
(From x In dt.AsEnumerable()
Order By DateTime.ParseExact(x("date").ToString,"dd/MM/yyyy",System.Globalization.CultureInfo.InvariantCulture)
Select x).CopyToDataTable
What am I missing ?

Make your life easy; use a strongly typed datatable. It can still be used like a normal datatable but it's less of a pig to work with
add a new DataSet type of file to your project
call it eg MyProjectNameHereDataSet
right click the design surface, choose Add DataTable, give it a nice name like Concerts
right click the DataTable and Add Column
call it ID, make it an int
add another column, call it something more interesting than Date - ConcertDate perhaps. Avoid naming columns words that are keywords or types
make it a DateTime type
This visual designer effectively just wrote you a class with two properties, int Id and DateTime ConcertDate, and it made a datatable capable of holding them.
When you're loading your data into your table, do the parsing then (you'll have to now it's strongly typed). It is more efficient to do so than doing it every time you query
Dim dt = new ConcertsDataTable()
'in a loop? Some api call? Wherever the data comes from
dt.AddConcertsRow(theId, DateTime.ParseExact(theDate, "dd.MM.yyyy", CultureInfo.InvariantCulture)
Now you can LINQ it more nicely; no need for AsEnumerabke, or digging column names out by string and casting them etc
dt.OrderBy(Function(r) r.ConcertDate)
Nice:
Dim totalAttendancePast = 0
For Each ro in dt
If ro.ConcertDate < DateTime.Now Then totalAttendancePast += ro.Attendance
Next ro
Nasty:
For Each ro as DataRow in dt.Rows
If DateTime.ParseExact(ro("ConcertDate"), "dd.MM.yyyy") < DateTime.Now Then totalAttendancePast += DirectCast(ro("Attendance"), Integer)
Next ro
Nicer:
dt.
Where(Function(r) r.ConcertDate < DateTime.Now).
Sum(Function(r) r.Attendance)
Nastier:
dt.AsEnumerable().
Where(Function(r) DateTime.ParseExact(ro("ConcertDate"), "dd.MM.yyyy") < DateTime.Now).
Sum(Function(r) r.Field(Of Integer)(Attendance))
You can use this strongly typed datatable anywhere you would use a normal datatable - you can still access it by string column names etc if you desperately wanted to:
someDatagridview.DataSource = dt
someDataAdapter.Fill(dt)
MessageBox.Show(dt.Rows(0)("Id").ToString())
But it's very helpful to have intellisense be able to guide you and stay in strongly typed land:
MessageBox.Show(dt.First().Id.ToString())

As pointed out in the comments, I was using "dd/MM/yyyy" when my data comes in as "20.02.2021".
I used "dd.MM.yyyy" and it works.

Related

Copy two dataTables to another table in vb.net

I have two dataTables which I want to union to make one final dataTable.
Both are results of different functions.
I tried this :
dtfinal = dt1.Copy()
dtfinal = dt2.Copy()
But here dt2 data is replaced by dt1. What should be used to get union of both into the final dt.
You can use DataTable.Merge:
Dim allTables() As DataTable = {dt1, dt2}
Dim dtfinal = new DataTable("dtfinal")
dtfinal.BeginLoadData() ' Turns off notifications, index maintenance, and constraints while loading data
For Each t As DataTable in allTables
dtfinal.Merge(t) ' same as table.Merge(t, false, MissingSchemaAction.Add)
Next
dtfinal.EndLoadData()
If you don't have primary keys specified you could end up with repeating rows where you actually want to merge them. Then either specify the PKs or use this method i have provided here(needs conversion from C#):
Combining n DataTables into a Single DataTable

Retrieve the latest or the last value of a column in a dataset

The below code gives the first row.. but i need to get the latest or the last row updated. Please help
Dim dt As DateTime = ds.Tables(0).Rows(0)("Columnname")
You can use the Rows.Count property as shown in other answer or just let do that to Linq
Dim row = ds.Tables(0).AsEnumerable().Last()
Dim dt As DateTime = row.Field(Of DateTime)("ColumnName")
Of course this works for the last row of the table. This doesn't mean something like the last (more recent) value for the "ColumnName". If this is your intention then you need to "Sort" the datatable or better ask the source (a database ? ) of the rows to sort it.
If you are not able to change the data loading query to have it sorted directly from the database engine then you could reach (in code) the latest row ordered by "ColumnName" using something like this
' Create a dataview from the datatable, with no filter and ordered by ColumnName
Dim dv As DataView = New DataView(ds.Tables(0), "", "ColumnName ASC", DataViewRowState.CurrentRows)
dt = dv.Cast(Of DataRowView).Last().Row.Field(Of DateTime)("Column")
You have to use ds.Tables(x).Rows.Count-1
Dim dt As DateTime = ds.Tables(0).Rows(ds.Tables(0).Rows.Count - 1)("Columnname")

Best way to extract rows from DataTable (based on date field) and copy to another DataTable

I have a DataTable containing about 30 rows and I need to extract all rows having a date field bigger than a date stored into a variable.
(This code will be executed a lot of times)
I found three ways to do this but I would like to know how to choose because I don't know the differences between various codes.
Here is what I was able to write (and my worries):
1st way (DataTable.Select)
Dim SelectedRows() As DataRow = DT_DBData.Select("In_Date=#" & _
LastDate.ToString("yyyy-MM-dd") & "#")
Using New_Dt As DataTable = SelectedRows.CopyToDataTable
'Some code
End Using
I'm worried about the string format: I'm afraid that some rows may be not extracted because of a date formatting error.
2nd way (query Linq)
Using New_Dt As DataTable = (From DBData In DT_DBData.AsEnumerable() _
Where DBData.Field(Of Date)("In_Date") >= LastDate).CopyToDataTable
'Some code
End Using
I never used Linq and so I don't know what kind of issues can it give me.
3rd way (For Each Loop + If Then)
Using New_Dt As DataTable = DT_DBData.Clone
For Each dr As DataRow In DT_DBData.Rows
If dr("In_Date") >= LastDate Then
New_Dt.Rows.Add(dr.ItemArray)
End If
Next
'Some code
End Using
I'm not really worried about this code. I only think that the others could be better or faster (but I can't answer to this)
Faster is kind of irrelevant when dealing with 30 rows.
The first one is kind of wasteful. You start with a DataTable, Select to get a subset, then convert the result into a new DataTable. Time to extract matching Rows: 8 ms.
You can work with the SelectedRows array without putting it into a new DataTable. If it goes back to the DB after "some code", I would not extract it from the DT.
By the way, there is no reason to worry about matching date formats as long as the DB column is a date type (and therefore, the DataTable column will be also). Dates do not have a format; formats are just how computers (and by extension, us) display them to users.
Dim drs = dt.Select(String.Format("StartDate > '{0}'", dtTgt.Date), "")
The date type I pass will compare/filter just fine with the DateTime data for that column. Formats only come into play when you convert them to string, which is mostly only needed for those pesky users.
One option you missed might be especially useful if this will be done over and over: A DataView:
dt.Load(cmd.ExecuteReader)
' create dataview
Dim dv As New DataView(dt)
dv.RowFilter = String.Format("StartDate > '{0}'", dtTgt.Date)
dv.Sort = "StartDate asc"
' show/iterate/whatever
dgv.DataSource = dv
If the data goes back to the DB, using this method, the rows will retain all the rowstate values.

DataTable.Select with converting string value to Date format and sort it

I have a data table that is being returned by a 3rd party component, so I have no way of changing the SQL string to get what I want. So, I get a table with several columns, all of them are Strings. One of the columns is BILLDATE which is of type string, but actually holds a date in a MM/DD/YYYY format.
Issue: I need to sort the records in descending order and pick the record with the biggest date.
CODE:
Dim dataRows As DataRow()
Dim dt As New DataTable
dt = GetTable()
dataRows = dt.Select("", "BILLDATE DESC")
Sample data:
10/23/2010
9/23/2010
8/23/2010
7/23/2010
6/23/2010
From the sample data, the 9/23/2010 record is returned as the first record, not the 10/23/2010.
Here is what I tried:
dataRows = dt.Select("MAX(CONVERT(DateTime,BILLDATE))", "") - run-time Error
dataRows = dt.Select("", "Convert(BILLDATE,'System.DateTime')") - run-time Error
I would prefer not to iterate through all the records to get the latest date and select it. Any ideas?
EDIT 1 - 2012-12-07 4:42pm:
Added definition for dataRows. It is of type DataRow()
You can use DateTime.ParseExact with multiple format strings. You need them since your "dates" sometimes have one and sometimes have two places. I would use Linq instead:
Dim ordered = From row In dt.AsEnumerable()
Let billDate = Date.ParseExact(
row.Field(Of String)("Billdate"),
{"MM/dd/yyyy", "M/dd/yyyy"},
Globalization.CultureInfo.InvariantCulture,
Globalization.DateTimeStyles.None)
Order By billDate Descending
Select row
' if you want to create a new DataTable from the rows: '
Dim tblOrdered = ordered.CopyToDatatable()
Dim LastDate As Date = dt.AsEnumerable().Max(Function(a) Convert.ToDateTime(a("BILLDATE")))

Return Max value with LINQ Query in VB.NET?

I have a method that takes in an id, a start date, and and end date as parameters. It will return rows of data each corresponding to the day of the week that fall between the date range. The rows are all doubles. After returning it into a DataTable, I need to be able to use LINQ in VB.NET to return the maximum value. How can I achieve this? Here is the initial setup?
Dim dt as DataTable = GetMaximumValue(1,"10/23/2011","11/23"/2011")
'Do linq query here to return the maximum value
The other alternative is to just return a Maximum value just given an id which would be slightly easier to implement, so the method would look like this:
Dim dt as DataTable = GetMaximumValue(1)
'Do linq query here to return maximum value
Important
What if I want to query against a DataRow instead of a DataTable and the column names are not the same, they are something like MaxForMon, MaxForTue, MaxForWed`, etc... and I need to take the Maximum value (whether it is MaxForMon, MaxForTue, or some other column). Would an option for this be to alias the column returned in the stored proc? The other thing I thought about was instead of doing this in LINQ, I probably should just handle it in the T-SQL.
You can use the DataSet Extensions for Linq to query a datatable.
The following query should help.
Dim query = _
From value In dt.AsEnumerable() _
Select value(Of Double)("ColumnName").Max();
If you don't now in which column will hold the maximum you could use: (C#)
var result = from r in table.AsEnumerable()
select r.ItemArray.Max();