How do I sort a datatable? I need to return a datatable from a function. I have been struggling with this for hours, and the internet has a few different answers, none of which seem to work for me.
Edit: I want to punch myself. Do a DataView.Sort on your table, then a DataView.ToTable() to put the sorted data into a new dataset...
Example:
Dim view As New DataView(OriginalDataSet) 'Put your original dataset into a dataview
view.Sort = "ColumnName" ' Sort your data view
Dim NewDataSet As DataTable = view.ToTable() ' Put your dataview into a new datatable
End of example
I have a relatively simple example table below, taken from a teaching website. The one twist is that there are duplicate values in the row I am trying to sort on.
Module Module1
Sub Main()
' Get a DataTable instance from helper function.
Dim table As DataTable = GetTable()
End Sub
''' <summary>
''' Helper function that creates new DataTable.
''' </summary>
Function GetTable() As DataTable
' Create new DataTable instance.
Dim table As New DataTable
' Create four typed columns in the DataTable.
table.Columns.Add("Dosage", GetType(Integer))
table.Columns.Add("Drug", GetType(String))
table.Columns.Add("Patient", GetType(String))
table.Columns.Add("Date", GetType(DateTime))
' Add five rows with those columns filled in the DataTable.
table.Rows.Add(25, "Indocin", "David", DateTime.Now)
table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now)
table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now)
table.Rows.Add(21, "Combivent", "Janet", DateTime.Now)
table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now)
table.Rows.Add(21, "Aspirin", "Janet", DateTime.Now)
Return table
End Function
End Module
I have tried selecting into an array, then looping through the array and putting each row into a new datatable, but the select isn't grabbing rows. Example:
drarray = ds.Select("I want to select all here", "MySortColumn")
I have tried various looping strategies, with inner loops, etc and can't seem to figure that out.
I have tried dataTable.DefaultView.Sort = "sortExp" but I can't get that to work.
So what am I missing? I figure with the DefaultView and Select methods I'm just missing something syntactly.
So what's the best way to go, and what am I missing?
You can use something like this:
Return table.Select("","Columns to sort on").CopyToDataTable
Use a DataView to create a view of your data in the DataTable. This allows you to sort, filter, etc. Here's a C# example: Datatable VS dataview
This may help you sortExp can be field on which based the sort should be performed filterExp that should evaluate to true or false. assuming the following fields
Dim filterExp As String = "Patient<> ''"
Dim sortExp As String = "Date "
dt_item.Select(filterExp, sortExp, DataViewRowState.CurrentRows)
The above code shows how to filter and sort the data table dt_item. The filter expression selects Date whose Patient is not NULL. The sort expression causes the results to be sorted by the Date column
Related
I working with List(Of) in VB.NET. I would like to "sort" a list by a key/unique field (FEIN), but get an error. I created the list with three columns (FEIN, Status, Term_Date). This list loads fine. Additionally, I would like to "search" this list in a quick and efficient way (BinarySearch?). I attempted using FindIndex, but it is very slow. I realize my sort/search syntax assumes a single column list, but I'm trying to get this to work with a multi-column list. I attached my code with comments and errors. I'm working in Visual Studio 2019, VB.NET
'define columns in list
Public Class AddInfo
'custom class additional info
Public Property FEIN As String
Public Property Status As String
Public Property Term_Date As String
Public Sub New(ByVal FEIN As String,
ByVal Status As String,
ByVal Term_Date As String)
Me.FEIN = FEIN
Me.Status = Status
Me.Term_Date = Term_Date
End Sub
End Class
'populate list (3 columns) from source datatable (dtAddInfo), this list populates nicely
Dim lstAddInfo = New List(Of AddInfo)
For Each r As DataRow In dtAddInfo.Rows
lstAddInfo.Add(New AddInfo(r("FEIN"), r("Status"), r("Term_Date")))
Next
'sort newly populated list by FEIN
lstAddInfo.Sort() 'this throws "Failed to compare two elements in the array."
'find index/row in list using FEIN
Dim index = lstAddInfo.BinarySearch("123")) 'this throws "Unable to cast object of type 'System.String' to type"
'NOTE: I tried using the following instead of BinarySearch. It works, but is painfully slow (list contains 220K rows)
Dim index = lstAddInfo.FindIndex(Function(x) x.FEIN = "123")) 'search for FEIN in list
Unfortunately, I cannot reduce the number of rows from the source database table. But...I was able to track down another alternative. I used a LINQ query to join the two datatables (Excel and AddInfo) to create a new, pared-down target datatable based on FEIN. This seems to speed things up significantly. I attached the code for building a joined datatable. Thanks again.
'use LINQ query to join Excel and Add Info datatables on FEIN
Dim joinedResults =
From Excl In dt_Excel.AsEnumerable()
Join AddInfo In dt_Add_Info.AsEnumerable()
On Excl.Field(Of String)("FEIN") Equals AddInfo.Field(Of String)("FEIN")
Select
FEIN = Excl.Field(Of String)("FEIN"),
Status = AddInfo.Field(Of String)("Status"),
Term_Date = AddInfo.Field(Of String)("Term_Date")
'define target datatable
dt_Add_Info = New DataTable
dt_Add_Info.Columns.Add("FEIN", GetType(String))
dt_Add_Info.Columns.Add("Status", GetType(String))
dt_Add_Info.Columns.Add("Term_Date", GetType(String))
'populate target datatable
For Each row In joinedResults
Dim dr As DataRow = dt_Add_Info.NewRow()
dr("FEIN") = row.FEIN
dr("Status") = row.Status
dr("Term_Date") = row.Term_Date
dt_Add_Info.Rows.Add(dr)
Next
I am having trouble with a piece of sample code I am using in a Visual Basic project.
This is the sample:
Dim dataRow As DataRow
dataRow = dataSet.Tables(0).NewRow()
I am getting a NullReferenceException on the second line of the sample when I run it.
Any help is greatly appreciated!!!
The most likely explanation is that there is no table at index 0. It may also be that the datSet itself is null.
Not certain as to what the intent of the code is, but from the looks of it you are trying to add a new row to a dataset? If that is the case, you would need declare a new data row and add it to the dataset. Alternatively you should be able to add a row to the dataset and then set datarow to the new row index.
A DataSet is a collection of DataTables. I'm guessing that the DataTable reference has not yet been established and is not pointing to a DataTable object. Check whether the DataTable reference is null (Nothing in VB.NET) and if so, create a new DataTable object and add some columns to it. Then you will be able to add a new row as the DataTable reference will be pointing to a DataTable object that rows can be added to:
If IsNothing(dataset) = True Then
dataset = New DataSet
dataset.Tables.Add("Table1")
End If
If IsNothing(dataSet.Tables(0)) = True Then
dataSet.Tables(0) = New DataTable
dataSet.Tables(0).Columns.Add("FirstName", GetType(String))
dataSet.Tables(0).Columns.Add("Surname", GetType(String))
dataSet.Tables(0).Columns.Add("DateOfBirth", GetType(DateTime))
End If
Dim dataRow As DataRow = dataSet.Tables(0).NewRow
dataRow.Item("FirstName") = "John"
dataRow.Item("Surname") = "Smith"
dataRow.Item("DateOfBirth") = #11/30/1998#
dataSet.Tables(0).Rows.Add(dataRow)
How can I select from Datatable in VB.net all rows which contains my custom object? - actually its type is INotifyPropertyChanged.
I consider using datatable.select method with filter expression, however no idea how comparision works with select.
Try using LINQ's Where method, something like,
' this will return a list of DataRow object
Dim rows = datatable.AsEnumerable().Where(Function(x) TypeOf x("mycolumn") Is INotifyPropertyChanged).ToList()
OR
' this will return a new DataTable with your selected rows
Dim dt2 = datatable.AsEnumerable().Where(Function(x) TypeOf x("mycolumn") Is INotifyPropertyChanged).CopyToDataTable()
I'm new to VB.net and I don't know how to display certain columns and rows in datagridview that was imported from CSV file. My problem is I have many columns and all I want to display is 2 columns:
Name,Age,Mobile Number,ID number
Alex,18,09848484841,0010
George,19,02987654321,0020
Toni,17,09277470257,0030
How can I display only the Name & Age columns and its rows?
If you use a datatable you get the data structure and collection together. something like this:
Dim sr As New IO.StreamReader(filename)
Dim dt As New DataTable
Dim newline() As String = sr.ReadLine.Split(","c)
dt.Columns.AddRange({New DataColumn(newline(0)), _
New DataColumn(newline(1))})
While (Not sr.EndOfStream)
newline = sr.ReadLine.Split(","c)
Dim newrow As DataRow = dt.NewRow
newrow.ItemArray = {newline(0), newline(1)}
dt.Rows.Add(newrow)
End While
DataGridView1.DataSource = dt
Use a custom class with properties that match the data you want to store and make an instance of that class for each row of data use read, then have a List(Of {custom class}) to hold each object and the DGV's DataSource property can view the collection in the grid. The property names in the class will be used as the header.
I’m working on a form that has a few data grid views that are populated from LINQ queries, no problem there it works as it should however that sorting does not work. After doing some reading its because LINQ results do not support sorting.
As I have the LINQ results already is there a way of copying the results into a dataset or datatable then binding the data grid view to that so sorting will work?
Thanks
EDIT:
Thanks to everyone for the answers, sadly I'm off on holiday for 2 weeks so cant try out and upvote the correct one. However when I return it will be on the top of my list
You can use the CopyToDataTable extension method for this.
The standard implementation of this method only works over IEnumerable<T> where T is of type DataRow but there is an example on MSDN of making your own extension method which works on anonymous types.
I've actually not used CopyToDataTable, in the past I've created a similar end result by creating a BindingList which supports sorting and then creating an instance of it with the query as the IList in the constructor, but the CopyToDataTable approach looks a lot cleaner to me.
In the end I used bits from each option and a little tweak to get it to work with nullable fields.
First thing is getting the CopyToDataTable function into VB, this is done here
http://msdn.microsoft.com/en-us/library/bb669096.aspx
When I tried this it would fail if the column was nullable so I searched and found a mod to that code that would work. Here it is in full
Public Function ExtendTable(ByVal table As DataTable, ByVal type As Type) As DataTable
For Each f As FieldInfo In type.GetFields()
If (Not _ordinalMap.ContainsKey(f.Name)) Then
Dim dc As DataColumn
dc = If(table.Columns.Contains(f.Name), table.Columns(f.Name), table.Columns.Add(f.Name, f.FieldType))
_ordinalMap.Add(f.Name, dc.Ordinal)
End If
Next f
For Each p As PropertyInfo In type.GetProperties()
If Not _ordinalMap.ContainsKey(p.Name) Then
Dim colType As Type = p.PropertyType
If (colType.IsGenericType) AndAlso (colType.GetGenericTypeDefinition() Is GetType(Nullable(Of ))) Then
colType = colType.GetGenericArguments()(0)
End If
Dim dc As DataColumn = IIf(table.Columns.Contains(p.Name), table.Columns(p.Name), table.Columns.Add(p.Name, colType))
_ordinalMap.Add(p.Name, dc.Ordinal)
End If
Next
Return table
End Function
Upvotes all round as they all would have worked I just took this option as it is neater
In C# I do it thus:
DataTable dt = new DataTable();
dt.Columns.Add("a", Type.GetType("System.String"));
dt.Columns.Add("b", Type.GetType("System.String"));
dt.Columns.Add("c", Type.GetType("System.String"));
dt.Columns.Add("d", Type.GetType("System.String"));
foreach (var row in [linqQueryName] )
{
DataRow destRow = dt.NewRow();
destRow["a"] = row.linqCol1;
destRow["b"] = row.linqCol2;
destRow["c"] = row.linqCol3;
destRow["d"] = row.linqCol4;
dt.Rows.Add(destRow);
}
See the code:
Here testData is the data from LINQ query on a list of Class having ID and Name as properties
Dim dataTable As New DataTable()
dataTable.Columns.Add("ID", GetType(Integer))
dataTable.Columns.Add("Name", GetType(String))
For Each item As var In testData
Dim dataRow As DataRow = dataTable.NewRow()
dataRow("ID") = item.ID
dataRow("Name") = item.Name
dataTable.Rows.Add(dataRow)
Next