LINQ VB.NET Return multiple columns - vb.net

I have a LINQ statement that will return all the fields in a row. How can I modify this statement to return just two fields, "Address 1" and "Address 2"? (row is a DataRow)
Dim fields As String() = row.ItemArray.Select(Function(field) field.ToString()).ToArray()

DataGridViewRows? I'm not sure I understand your question completely, but I think you're looking for anonymous types.
Ex.:
Dim fields = New With {.Column1 = row.Cells.Item("Column1Name"), _
.Column2 = row.Cells.Item("Column2Name")}

Here's one way of doing it:
Dim fields As String() = (From obj In {row.Item("a1"), row.Item("a2")} Select CStr(obj)).ToArray()

Related

How to Select Rows in a Datatable?

How can I use the Variable Itmnmbr instead of hard-coding its value, 'i-2051'?
Dim fr() As DataRow
Dim Itmnmbr As string = "i-2051"
fr = dt.Select("item = 'i-2051'")
The most direct way is to use an Interpolated String, which are available from Visual Studio 2015, VB.Net 14:
Dim Itmnmbr As string = "i-2051"
fr = dt.Select($"item = '{Itmnmbr}'")
As a suggestion, let's change the names of Variables / Fields so it's easier to read them and also understand what these objects are used for. For example:
Dim dt as New DataTable()
'[...]
Dim itemNunmber As string = "i-2051"
Dim filteredRows As DataRow() = dt.Select(...)
itemNunmber is easier to read than Itmnmbr and filteredRows is more explicit than fr. There are some convetions that most are used to, as dt for DataTable, ds for DataSet etc., in this context. Better be sure that when you read your code after some time you don't get mad with yourself :)
Note that an Interpolated String is the same as a string formatted with String.Format(), so these two are actually the same thing:
Dim filteredRows As DataRow() = dt.Select($"item = '{itemNumber}'")
Dim filteredRows As DataRow() = dt.Select(String.Format("item = '{0}'", itemNumber))
Setting Option Infer On (should be On already), to make use of local type inference, you can write:
Dim filteredRows = dt.Select($"item = '{itemNumber}'")
and let the compiler infer the Type. In Visual Studio, if you move the mouse pointer over the variable, it will tell you what Type that is.
You have other options, if you need more dynamic selections.
The DataTableExtensions (which require a Project Reference to the System.Data.DataSetExtensions assembly - usually already linked along with System.Data), let you use the the AsEnumerable() method.
In LINQ to Objects style:
Here, using the default string Comparer
Dim filteredRows =
dt.AsEnumerable().Where(Function(dr) dr("item").ToString().Equals(itemNumber))
Or in LINQ to SQL style:
Here, using the InvariantCulture for the comparison.
Dim filteredRows =
From row In dt.AsEnumerable()
Where row.Field(Of String)("item").Equals(itemNumber, StringComparison.InvariantCulture)
Select row
See also: StringComparison and Best practices for comparing strings in .NET
These two last methods don't return an array of DataRow objects references, but a EnumerableRowCollection. The advantage is (when you can make use of it) that the collection is returned only when you actually use it (the execution is deferred).
When used correctly, it can improve the performance of your code. Try it out.
Instead of DataTable.Select(), you could also filter your DataTable, using its DefaultView.RowFilter property.
dt.DefaultView.RowFilter = $"item = '{itemNumber}'"
' You can save the filter to restore it later, if needed
Dim previousFilter = dt.DefaultView.RowFilter
When you present the Rows of your DataTable, only the Rows that meet the criteria defined by the Filter are shown (e.g., in a DataGrid of sort).
As mentioned, you're working with References here. The Collection of Rows returned by DataTable.Select() contain references of the Rows in the DataTable.
For example, if you consider the Collection and the filtered DataTable:
Dim filteredRows = dt.Select($"item = '{itemNumber}'")
dt.DefaultView.RowFilter = $"item = '{itemNumber}'"
Assume that filteredRows contains a single Row. Then you apply a Filter.
If you now change the value of filteredRows(0)("item"):
filteredRows(0)("item") = "Some other value"
when you present your DataTable in a UI, no Rows will be shown, since the Filter is active and now none of the Rows meet the filter's criteria: setting filteredRows(0)("item") has changed the value of the Row it refers to.
To remove a Filter, set it to string.Empty:
dt.DefaultView.RowFilter = Sting.Empty
To restore the previously saved filter:
dt.DefaultView.RowFilter = previousFilter

How to get the value of a column using a dataview in vb.net

I have a list of records and for Employee R1005, I need to check if that Employee has been Enabled for login alert (i.e EnableLoginAlert = Yes), then a button will be displayed.
CompanyID EmployeeNo EnableLoginAlert
10046 R1005 Yes
20041 Ajax12 No
47021 Drek Yes
I have tried the below codes:
If dCompanyDetails.Tables(0).Rows.Count > 0 Then
Dim dataView As DataView = dCompanyDetails.Tables(0).DefaultView
dataView.RowFilter = "EmployeeNo = '" & strEmployeeNumber & "'"
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
If svalue = "No" Then
AlertButton.Visible = False
ElseIf svalue = "Yes" Then
{
//Do something else
}
End If
End If
If you are going to use a DataView then use it. This:
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
is simply going back to the DataTable and using it, ignoring the DataView. The DataView contains DataRowView objects so get the one you need and use it. It is similar to a DataRow and you can use it the same way in this case:
Dim enableLoginAlert = CStr(dataView(0)("EnableLoginAlert")) = "Yes"
Now you have an actual Boolean that represents the state you want.
That's not how you should do it though. Generally speaking, you would use a DataView when you want to bind data. In fact, if you bind a DataTable then the data you see in the UI actually comes from the DefaultView. That's why you can filter and sort it. In this case, there are better options.
If you want to find a row by its primary key then the Rows collection of a DataTable has a Find method, e.g.
Dim row = dCompanyDetails.Tables(0).Rows.Find(strEmployeeNumber)
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you're searching by other than the primary key, the DataTable itself has a Select method. Because multiple rows may match, it returns an array, so you need to get the row out of that, e.g.
Dim row = dCompanyDetails.Tables(0).Select($"EmployeeNo = '{strEmployeeNumber}'").First()
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you want to look up a single row it's perhaps easiest to use LINQ:
Dim row = dCompanyDetails.Tables(0).Rows.Cast(Of DataRow).AsQueryable().FirstOrDefault(Function(r) r("EmployeeNo").ToString() = strEmployeeNumber)
If row IsNot Nothing AndAlso row("EnableLoginAlert").ToString() = "Yes" Then
...
..though I'd be the first to claim that using LINQ on base DataTables is very verbose, because of the Cast/AsQueryable. I'd use strongly typed DataTables (in a dataset); if you were to convert your code to using strongly typed tables it would look like:
Dim r = someDataSet.AProperTableName.FirstOrDefault(Function(r) r.EmployeeNo = strEmployeeNumber)
If r?.EnableLoginALert = "Yes" Then
...
...using strongly typed datatables is much less messy..
nb: You need to Imports System.Linq for these to work
That LINQ is the same thing as:
For Each r as DataRow in dCompanyDetails.Tables(0)
If r("EmployeeNo").ToString() = "R1005" AndAlso r("EnableLoginAlert").ToString() = "Yes" Then
...
You also have the option of using DataTable.Select (not a LINQ thing, though LINQ has a Select too)
Dim matchingRows = dCompanyDetails.Tables(0).Select($"[EmployeeNo] = '{strEmployeeNumber}'")
If matchingrows.Count > 0 AndAlso matchingRows(0)("EnableLoginAlert").ToString() = "Yes"

VB Running grouped LINQ into DataTable

I'm trying to run a LINQ to datatable, that normally works for me. This time I'm trying something new to me, Grouping and taking the first row from each group. questionGroups is of type IEnumerable(Of Object). When I open the query in the debugger I see a collection of DataRows, each with an ItemArray of the values I expect in the columns. How do I run this query to a DataTable, or select the two columns I want and run those into a Dictionary?
Public member 'ToTable' on type 'WhereSelectEnumerableIterator(Of
VB$AnonymousType_0(Of Object,IEnumerable(Of Object)),Object)' not
found.
Dim answerGroup As String = "QuestionSortKey"
Dim answerNo As String = "AnswerNo"
Dim surveyDefinitionNo As String = "Pk_SurveyDefinitionNo"
Dim query = _
From rows In surveyAnswerKeys.Rows _
Where rows(answerNo) IsNot Nothing _
Order By Guid.NewGuid() _
Group By questionSortKey = rows(answerGroup) _
Into questionGroups = Group _
Select questionGroups.First()
Dim randomAnswerNos As DataTable = query.ToTable
Edit: This question is an offshoot of this one: VB LINQ - Take one random row from each group

Search Through A DataTable LINQ

I am trying to return a list of data rows where a filed in a datatable matches some criteria. Here is what I have
'Dim returnedList = myDatatable.Where(Function(x) x.Item("TagergetField").ToString = "TheCriteria").ToList()
My Where clause isnt correct and I am not sure how to return a filtered datatable with only the records that match my criteria. I want to return a datatable object not a List
Just try with
datatable.AsEnumerable().Where(Function(x) x("TagergetField").ToString = "TheCriteria").ToList()
this will return a List(Of DataRow)
You could achieve the same result without resorting to Linq
Dim rows = dataTable.Select("TargetField = 'TheCriteria'").ToList()
Note how the last one is more readable and considerably more performant (Just tested)
EDIT
To get a datatable you could use the DataSetExtension namespace method CopyToDataTable
Dim dataTable1 = rows.CopyToDataTable()

Display all contents of a "table" created in LINQ in VB.NET

Working from a previous question I asked (which was answered very well).. Ive come across another snag... In the following code
Public Sub Main()
Dim EntireFile As String
Dim oRead As System.IO.StreamReader
oRead = File.OpenText("testschedule.txt")
EntireFile = oRead.ReadToEnd
Dim table As New List(Of List(Of String))
' Process the file
For Each line As String In EntireFile.Split(Environment.NewLine)
Dim row As New List(Of String)
For Each value In line.Split(",")
row.Add(value)
Next
table.Add(row)
Next
' Display all contents of 5th column in the "table" using LINQ
Dim v = From c In table Where c(5) = ""
For Each x As List(Of String) In v
Console.WriteLine(x(0)) ' printing the 1st column only
Next
Console.WriteLine("Value of (2, 3): " + table(1)(2))
End Sub
`
The area where it says Dim v = From c In table Where c(5) = "" the blank quotations will only accept a specific number that its looking for in that column.
For Example:
Dim v = From c In table Where c(5) = "7" Will only show me any 7's in that column. Normally there will be many different values and I want it to print everything in that column, I just cant figure out the command to have it display everything in the selected column
Once again Many MANY Thanks!
If you want to show all rows (to be precise: items in the IEnumerable), just remove the Where condition
Dim v = From c In table
Just a note: table is not a very good name for your list, it leads the thought to SQL. This is just Linq2Objects and you don't query tables you query plain objects with a syntax very similar to Linq2SQL that in turn is heavily inspired by SQL.