LinQ DataTable Group By - vb.net

I have a datatable in vb.net application. the data table is looking like this:
State Level City
AL T Arlton
AL T Brton
AL T Rurton
CO T Dver
CO T Crodo
WA T Blain
WA T Bwelham
I would like to group the state and do a loop
I tried the following but is not working as expected.
please help
what I tried so far:
Dim result = From r In dt Group r By state = r(0) Into Group Select Group
For Each r In result
Console.WriteLine(r("State"))
Next
The output from the loop should be
AL
CO
WA
Thank you

You cannot call GroupBy on a datatable. So you may first convert the datatable to a collection using the AsEnumerable() method and then apply your group by
Dim itms = dt.AsEnumerable().[Select](Function(x) New With {
Key .City = x.Field(Of String)("City"),
Key .State = x.Field(Of String)("State"),
Key .Level = x.Field(Of String)("Level")
})
Dim uniqueStatesgroupedStates = itms.GroupBy(Function(s) s.State, Function(p) p,
Function(k, v) New With {
Key .State = k,
Key .Item = v
})
' IF you want group records based in State
For Each r In uniqueStatesgroupedStates
Console.WriteLine(r.State)
Next
' IF you want only Unique states from your data table
Dim uniqueStates = itms.[Select](Function(s) s.State).Distinct().ToList()
For Each r In uniqueStates
Console.WriteLine(r)
Next
Your expected output looks like you want only the unique State names, In that case you simply need to do a Select on State property and call Distinct() on that.

I guess you need distinct instead of a group by, If I understood your question correctly following method will work for you.
Dim resultdt As DataTable = dt.DefaultView.ToTable(True, "state")
For Each row In newdt.Rows
Console.WriteLine(row(0).ToString)
Next
DataView.ToTable Method (): Creates and returns a new DataTable based on rows in an existing DataView

Related

How can I merge 2 DataView built in 2 different way?

I am not a c# developer and I found myself at work working with a huge C# application...hug..
Now I am trying to populate an existing DataGrid with some more data which has been 'created' in a different way...let me show you what I mean.
This is how the existing DataView is generated:
Dim lDataSet As System.Data.DataSet
Dim lSqlCommand As New System.Data.SqlClient.SqlCommand
Dim lConvert As New PeoplePlanner.Common.Data.Convert
lSqlCommand.CommandType = CommandType.StoredProcedure
lsqlCommand.CommandText = "AccessIntegrationEmployeeInboundSearch"
lDataSet = objDatabase.GetDataSet(lSqlCommand)
If Not IsNothing(lDataSet) Then
objDataView = lDataSet.Tables(0).DefaultView
End If
There is probably be stuff missing in that code but hopefully it is enough to get an overview. It uses Stored Procedure (SP) to get some data from the DB and it returns the result inside 'lDataSet' and then we do 'objDataView = lDataSet.Tables(0).DefaultView'.
Now the new data which I want to 'merge in' it has the same fields but it is not the response of a SP but of an operation I have created which returns back a List of OnBoardingEmployee(let's call it like that) and if I want to correctly show ONLY the data inside that list this is what I do:
Dim lDataView As System.Data.DataView
Dim op = CoreInjector.Inject(Of IGetAllDataHubIncomingMessagesToBeProcess).Execute()
lDataView = Common.Detail.DataGridOperationHelper.ConvertToDataTable(op.OnBoardingEmployee).DefaultView
objDataView = lDataView
Where:
op contains a list of OnBoardingEmployee which has the same fields as the existing table
lDataView basically is the same as objDataView that's why as last step I assign objDataView = lDataView
Now I would basically like to merge, join, add, whatever
The objDataView created for the first table:
objDataView = lDataSet.Tables(0).DefaultView
with the one I have created afterwards:
lDataView = Common.Detail.DataGridOperationHelper.ConvertToDataTable(x.OnBoardingEmployee).DefaultView
How do I do that? :'(
They have the same data structure or better say they use the same view.
Thanks for the help :)
I have got it working :) this is my implementation:
Dim employeeJSONTable As DataTable
Dim employeeExistingTable As DataTable
Dim myDataRow As DataRow
Dim employeeID As Integer
if Request.QueryString.Get("Function") = "HubInbound" then
Dim employeeJSONList = CoreInjector.Inject(Of IGetAllDataHubIncomingMessagesToBeProcess).Execute()
employeeJSONTable = Common.Detail.DataGridOperationHelper.ConvertToDataTable(employeeJSONList.OnBoardingEmployee)
employeeJSONTable.PrimaryKey = New DataColumn(){ employeeJSONTable.Columns("EmployeeID")}
End If
lDataSet = objDatabase.GetDataSet(lSqlCommand)
employeeExistingTable = lDataSet.Tables(0)
For Each row As DataRow In employeeExistingTable.Rows
employeeID = row.Item("EmployeeID")
myDataRow = employeeJSONTable.Rows.Find(employeeID)
if (myDataRow Is Nothing) then
employeeJSONTable.ImportRow(row)
End If
Next row
If Not IsNothing(employeeJSONTable.DefaultView) And Request.QueryString.Get("Function") = "HubInbound" Then
objDataView = employeeJSONTable.DefaultView
Elseif Not IsNothing(lDataSet) Then
objDataView = lDataSet.Tables(0).DefaultView
End if

datatable.AsEnumerable doesn't work (basic example)

Dim x = From row In f_table.AsEnumerable()
Select row("Crop")
From what I understand, the "f_table.AsEnumerable" should make my search object ("row" in this case) a datarow object. This simple example runs without any exceptions but does not find any entries (This search works if I switch to an array of datarows that have been taken from f_table, in place of f_table.AsEnumerable).
Any ideas why AsEnumerable is not allowing for searching the rows of the table?
edited/added: The following is what I have, where "emptyrows" is a subset array of rows from f_table.
Dim emptyrows_grouped = From row In emptyrows
Order By row("Date"), row("Time")
Group By New With {.date = row("Date")}.date,
New With {.crop = row("Crop")}.crop
Into Group
What i want is this form:
Dim emptyrows_grouped = From row In f_table.AsEnumerable
Where row.Field(Of String)("SamplePosition") Like "Emp%"
Order By row("Date"), row("Time")
Group By New With {.date = row("Date")}.date,
New With {.crop = row("Crop")}.crop
Into Group
It works like this:
Dim query = dt.AsEnumerable
.Where(Function(dr) dr("column name").ToString = "something").ToList
This yields a List of DataRows where this column has the value of "something"
GroupBy:
Dim query = dt.AsEnumerable
.Where(Function(dr) dr("column name").ToString = "something")
.GroupBy(Function(dr) dr("column name"))
Never mind - I'm a gigantic fool today because f_table is the wrong datatable. I used the right one and it worked.
Dim emptyrows_grouped = From row In file_table.AsEnumerable
Where row.Field(Of String)("SamplePosition") ="Empty"
Order By row("Date"), row("Time")
Group By New With {.date = row("Date")}.date,
New With {.crop = row("Crop")}.crop
Into Group
Please excuse my wasting of your time!!

VB:NET using LINQ how can i find the row index for a specified string in some Datatable column

to check if a string exists in some column i use something like
mydatatable.AsEnumerable().Any(Function(r) r.Field(Of String)("somecolumn") = "somestring")
but how can i find the row index of "somestring"? considering its allowed to exist only once in mydatatable , and what if it existed more than once?
You can use the overload that passes the index:
Dim rows = myDataTable.AsEnumerable().
Select(Function(r, i) New With {.Row = r, .Index = i}).
Where(Function(x) x.Row.Field(Of String)("somecolumn") = "somestring")
If rows.Any() Then
Dim firstIndex As Int32 = rows.First.Index
End If

Convert the following VB code to a LINQ statement?

I only want to return a certain number of rows into the DataTable via LINQ's Take or use it on the Rows property, but I am not sure how or where to do it: Here is the current code:
Dim dt As DataTable = GetDataTable("sp", params)
For Each dr As DataRow In dt.Rows
Dim o As New OR()
o.P = p
o.Id = dr ("A")
o.R = dr ("B")
Next
Would it be something like:
Dim dt As DataTable = GetDataTable("sp", params).AsEnumerable().Take(10)
When I run the above, I get the error:
The 'TakeIterator' start tag on line 4 position 60 does not match the end tag of 'Error'. Line 4, position 137.
Unable to cast object of type '<TakeIterator>d__3a1[System.Data.DataRow]' to type 'System.Data.DataTable'.`
If you need a DataTable:
Dim dt As DataTable = (From row in GetDataTable("sp", params) Take 10).CopyToDataTable()
If you need a List(Of [Or]) (you need the brackets since Or is a keyword)
Dim list As List(Of [Or]) = (From row In GetDataTable("sp", params)
Select New [Or]() {
o.Id = row.Field(Of Int32)("Id"),
o.R = row.Field(Of String)("R")
}).Take(10).ToList()
Edit:
is there a way I can also include a Where with the Take, for example, I have 3 statuses in my table, Open, Semi-Open, Closed, I only want to Take 10 for Open, but I want to leave Semi-Open and Closed alone. As an example Semi-Open and Closed will have 5 records combined and I will take 10 from Open, so I would get a total of 15 rows
Yes, there's a way:
Dim dt = GetDataTable("sp", params)
Dim open10 = From row In dt
Where row.Field(Of String)("Status") = "Open"
Take 10
Dim rest = From row In dt
Where row.Field(Of String)("Status") <> "Open"
Dim tblResult = open10.Concat(rest).CopyToDataTable()

Sorting Linq results ascending using subselect and then descending

I am trying to work out a LINQ query to pull items from a List, the List contains a child-nested List - which I need to query for an item, then use the resulting item in my final sort of all records returned the query.
The parent is List(Of MediaItems) - the class structure as follows:
MediaItems
.ID (int)
.Src (string)
.Advert (bool)
.AdOptions As List(Of AdvertOptions)
.Counter (int)
AdvertOptions class consists of:
.Age (int)
.Gender (int)
.Priority (int)
I want to query for any MediaItems that meet the following criteria:
.Advert = true
.Age = x (paramter in calling function)
.Gender = y (paramter in calling function)
To do this, I am using the following LINQ query: (Age and Gender are the function parameters)
Where(Function(s) s.Advert And s.AdOptions.Any(Function(a) a.Gender = Gender And a.Age = Age))
I now need to sort the results based on two sorting levels:
AdOptions.Priority (in descending order), then sort by Counter in ascending order
This is my failing attempt:
Where(Function(s) s.Advert And s.AdOptions.Any(Function(a) a.Gender = Gender And a.Age = Age)).OrderBy(Function(a) From p1 In a.AdOptions Where p1.Gender = Gender And p1.Age = Age Select p1.Priority).ThenByDescending(Function(s) s.Counter)
My attempt is not working, I am getting the following error:
at least on object must implement IComparable
Can anyone see what needs to be done to achieve my goal?
Ben
On reading your code a bit closer, I spotted that you order by AdOptions
I think this is what you really want
Sub Demo(ByVal gender As Integer, ByVal age As Integer)
Dim items = New List(Of MediaItem)()
Dim matching = Function(a) a.Gender = gender And a.Age = age
Dim ads = items.Where(Function(s) s.Advert And s.AdOptions.Any(matching))
Dim sorted = ads _
.OrderBy(Function(a) a.AdOptions.Where(matching).Max(Function(ao) ao.Priority)) _
.ThenByDescending(Function(s) s.Counter)
' if you really wanted a sorted list of AdOptions, you'd write
Dim optionlist = ads.OrderByDescending(Function(s) s.Counter) _
.SelectMany(Function(a) a.AdOptions.Where(matching).OrderBy(Function(ao) ao.Priority))
' to combine the two previous options
Dim adsWithOptions = ads.OrderByDescending(Function(s) s.Counter) _
.Select(Function(a) New With { _
.Ad = a, _
.Options = a.AdOptions.Where(matching) _
.OrderBy(Function(ao) ao.Priority) _
})
End Sub