VB.NET LINQ To DataTable Select with Where Clause - vb.net

Here is my data table that I have stored in a data table variable named dt:
Dim dt As New DataTable
+---------+--------+---------------+
| Carrier | AVGTAT | Name |
+---------+--------+---------------+
| ABCD | 2078 | Term Check |
| ABCD | 0 | AdHoc |
| ABCD | 26406 | Cash on Term |
| REWS | 7358 | Failed Bill |
| ZELT | 11585 | BL150 |
I need to get the value of the AVGTAT column using LINQ to DataTable based Where Carrier = "x" and Name = "X"
How can I accomplish that?
Thank you!

Here is the C# version.
var avg = (from t1 in dt.AsEnumerable()
select new
{
Carrier = t1.Field<string>("Carrier"),
Name = t1.Field<string>("Name"),
Avg = t1.Field<int>("AVGTAT")
}).Where(s => s.Carrier == "X" && s.Name == "X")
.Select(v=>v.Avg).FirstOrDefault();
And the VB.NET version
Dim avg = dt.AsEnumerable().[Select](Function(x) New With {
Key .Carrier = x.Field(Of String)("Carrier"),
Key .Name = x.Field(Of String)("Name"),
Key .Avg = x.Field(Of Int32)("Level")
}).Where(Function(s) s.Carrier = "X" AndAlso s.Name = "X")
.[Select](Function(h) h.Avg).FirstOrDefault()

Related

Correct way to convert DataTable with duplicate values to Dictionary(of String, List(of Object)) in VB.NET

I am trying to convert a datatable to a dictionary. The table format is as follows:
ValueID | DependentValueID | DependencyDescription
__________________________________________________
A | a | some text
A | b | some text
B | c | some text
B | d | some text
B | e | some text
...
Each object has 3 properties:
ValueID
DependentValueID
DependencyDescription
The Dictionary format is as follows:
(Key) | (Value)
_______________
A | {
| [
| ValueID = A
| DependentValueID = a
| DependencyDescription = "some text"
| ],
| [
| ValueID = A
| DependentValueID = b
| DependencyDescription = "some text"
| ],
| }
-----------------------------------------------
B | {
| [
| ValueID = B
| DependentValueID = c
| DependencyDescription = "some text"
| ],
| [
| ValueID = B
| DependentValueID = d
| DependencyDescription = "some text"
| ],
| [
| ValueID = B
| DependentValueID = e
| DependencyDescription = "some text"
| ]
| }
...
The code I wrote to do this:
Private Sub MySub()
' Declare variables and fill datatables
If DependenciesDataTable IsNot Nothing AndAlso DependenciesDataTable.Rows.Count > 0 Then
For i As Integer = 0 To DependenciesDataTable.Rows.Count - 1
DependenciesObject.ValueID = DependenciesDataTable.Rows(i).Item("ValueID").ToString
DependenciesObject.DependentValueID = DependenciesDataTable.Rows(i).Item("DependentValueID").ToString
DependenciesObject.DependencyDescription = DependenciesDataTable.Rows(i).Item("DependencyDescription").ToString
If i > 0 Then
If DependenciesDataTable.Rows(i).Item("ValueID").ToString.Equals(DependenciesDataTable.Rows(i - 1).Item("ValueID").ToString) Then
DependenciesObject.Add(DependenciesObject)
Else
DependenciesDictionary.Add(DependenciesDataTable.Rows(i - 1).Item("ValueID").ToString, DependenciesObject)
DependenciesObject = New List(Of Object)
DependenciesObject.Add(DependenciesObject)
End If
ElseIf i = 0 Then
DependenciesObject.Add(DependenciesObject)
End If
If i = DependenciesDataTable.Rows.Count - 1 Then
DependenciesObject.Add(DependenciesObject)
DependenciesDictionary.Add(DependenciesDataTable.Rows(i).Item("ValueID").ToString, DependenciesObject)
DependenciesObject = New List(Of Object)
End If
Next
End If
End Sub
Though this works, I understand that it is a subpar solution since I am a junior developer. I would like some advice on how to improve my approach to the issue.
I see that dependenciesObject is a List(Of Object), but why? Use classes, or even something more temporary like Tuple... My solution will use a class to hold your dependency
Public Class Dependency
Public Property ValueID As String
Public Property DependentValueID As String
Public Property DependencyDescription As String
End Class
Then using LINQ, we can just select from the table into an IEnumerable(Of Dependency).
Dim dependencies = DependenciesDataTable.Select().Select(Function(row) New Dependency With {.ValueID = row("ValueID"), .DependentValueID = row("DependentValueID"), .DependencyDescription = row("DependencyDescription")})
Then group the results by the ValueID property, and select into a Dictionary
Dim dependenciesDictionary = dependencies.GroupBy(Function(d) d.ValueID).ToDictionary(Function(g) g.Key, Function(g) g.Select(Function(v) v).ToList())
You would end up with a Dictionary(Of String, List(Of Dependency))
And print them out to be sure
For Each kvp In dependenciesDictionary
Console.WriteLine($"Key: {kvp.Key}")
For Each v In kvp.Value
Console.WriteLine($"{vbTab}ValueID: {v.ValueID}, DependentValueID: {v.DependentValueID}, DependencyDescription: {v.DependencyDescription}")
Next
Next
Key: A
ValueID: A, DependentValueID: a, DependencyDescription: some text
ValueID: A, DependentValueID: b, DependencyDescription: some text
Key: B
ValueID: B, DependentValueID: c, DependencyDescription: some text
ValueID: B, DependentValueID: d, DependencyDescription: some text
ValueID: B, DependentValueID: e, DependencyDescription: some text

How to select datarow from datatable

I get a data from textfile and use dataset.tables(0) and select columns by dataview. But I want to know how can I select or sorting rows data by (column2 - data&time). In short, I want see selected rows by column2
Can you give me a tips or example?
If The datatable is like this.
I want selected column2's 201405310010 and 201405310310 when I select datarows.
from
column2 | column3 | column4
**201405310010 | 2.155 | 22.90**
201405310030 | 2.156 | 22.90
201405310050 | 2.152 | 22.90
201405310110 | 2.151 | 22.90
201405310130 | 2.175 | 22.90
201405310150 | 2.155 | 22.90
201405310210 | 2.115 | 22.90
201405310240 | 2.165 | 22.90
**201405310310 | 2.255 | 22.90**
Code
to
column2 | column3 | column4
201405310010 | 2.155 | 22.90
201405310310 | 2.255 | 22.90
For i As Integer = 0 To cols.Length Step 1
ds.Tables(0).Columns.Add()
Next
Dim row As String = sr.ReadLine()
While row IsNot Nothing ' != null
Dim rvalues As String() = RegularExpressions.Regex.Split(row, "/")
ds.Tables(0).Rows.Add(rvalues)
row = sr.ReadLine() 'row
End While
Dim diplayview = New DataView(ds.Tables(0))
Dim subset As DataTable = diplayview.ToTable(False, "column1", "column2", "column3", "column4", "column19", "column20") 'select columns
GridView1.DataSource = subset
GridView1.DataBind()

Transaction in Microsoft Acess, error 3034

I have an issue with Access 2010.
I use transaction to encapsulate my modification in a form. This transaction is started in the Form_Load() sub.
Private Sub Form_Load()
Debug.Print "Right here"
DAO.DBEngine.Workspaces(0).BeginTrans
Debug.Print "Here too"
...
End Sub
So the transaction is started at the very first line (nothing else is running before, the Debug.Print are just here to show you the code run through the line).
When I click on the button "save" or "rollback", I run some code like this :
Private Sub BtnSauvegarder_Click()
DAO.DBEngine.Workspaces(0).CommitTrans dbForceOSFlush
DoCmd.OpenForm "F_ListeDemande", acNormal, , , acFormEdit, acWindowNormal
DoCmd.Close acForm, Me.name
End Sub
And that's here I got the error 3034, both in save or rollback code (which are similar)
BUT, the weirdest thing is here : when I entered the form, my Listbox are bugy, nothing are inside. If I entered in Design view, do nothing and then entered in the Normal view, everything run right : Listbox have the Recordset they are supposed to have and the transaction work fine.
This is not the first time I use transaction, I used the same way on other forms without any problem.
So what I am doing wrong ?
EDIT :
I made this code to output the current state of DAO.DBEngine, in case it is useful.
Public Sub DebugDBEngine()
Dim ws As Workspace
Dim db As Database
Dim p As Property
For Each ws In DAO.DBEngine.Workspaces
Debug.Print "Workspace : " & ws.name
For Each p In ws.Properties
On Error Resume Next
Debug.Print "| " & p.name & " = " & p.value
Next
For Each db In ws.Databases
Debug.Print "| Database : " & db.name
For Each p In db.Properties
On Error Resume Next
Debug.Print "| | " & p.name & " = " & p.value
Next
Next
Next
End Sub
So I use it just after the beginning of the transaction and the output is this :
Workspace : #Default Workspace#
| Name = #Default Workspace#
| UserName = admin
| IsolateODBCTrans = 0
| Type = 2
| Database : H:\Projet\05\15\10h28 - Suivi commande et fournisseur.accdb
| | Name = H:\Projet\05\15\10h28 - Suivi commande et fournisseur.accdb
| | Connect =
| | Transactions = True
| | Updatable = True
| | CollatingOrder = 1036
| | QueryTimeout = 60
| | Version = 14.0
| | RecordsAffected = 0
| | ReplicaID =
| | DesignMasterID =
| | ANSI Query Mode = 0
| | Themed Form Controls = 1
| | AccessVersion = 09.50
| | NavPane Category = 0
| | UseMDIMode = 0
| | ShowDocumentTabs = True
| | Build = 24
| | HasOfflineLists = 70
| | Picture Property Storage Format = 0
| | CheckTruncatedNumFields = 1
| | ProjVer = 119
| | NavPane Closed = 0
| | NavPane Width = 226
| | NavPane View By = 0
| | NavPane Sort By = 1
| | Show Navigation Pane Search Bar = 0
| | WebDesignMode = 0
| | Theme Resource Name = Thème Office
| | Property Sheet Label Width = 2820
| | StartUpShowDBWindow = True
| | StartUpShowStatusBar = True
| | AllowShortcutMenus = True
| | AllowFullMenus = True
| | AllowBuiltInToolbars = True
| | AllowToolbarChanges = True
| | AllowSpecialKeys = True
| | UseAppIconForFrmRpt = False
| | AllowDatasheetSchema = True
| | DesignWithData = True
| | Show Values Limit = 1000
| | Show Values in Indexed = 1
| | Show Values in Non-Indexed = 1
| | Show Values in Remote = 0
| | Auto Compact = 0
| | Track Name AutoCorrect Info = 0
| Database : H:\Projet\05\15\10h28 - Suivi commande et fournisseur.accdb
| |(same things as above, it is the same database)
So, the same DB is open twice. I verify : where the transactions are correctly runing, only one DB is open.
EDIT2 :
I tested again with this debug to enter in normal view, then design and then normal. The first time, I've got the output just above. The second time, it is the same without the second same database, there is juste one DB.
So now, I'm sure the problem is they are two databases opened. All I have to find is WHY it open twice the same DB.
With hard searching, I probably found the problem.
In Design view, I set to Listboxes some query to fill them. It use one database to do it.
In my VBA code, I use Recordset to fill the Listboxes. And they use another database, even if it is the same.
So my solution is simple : I don't define the rowsource in Design view, so only the VBA will fill the Listboxes.

How to transform a repeating list into a list with a count?

I have a list(Of transaction) :
Type | State
----------------
boat | buy
boat | buy
boat | sell
car | sell
car | sell
car | sell
plane| buy
How can I transform it to a list(Of transactionShortened) :
Type | State | Quantity
-----------------------
boat | buy | 2
boat | sell | 1
car | sell | 3
plane| buy| | 1
Elegantly.
Use LINQ and Enumerable.GroupBy:
Dim transactionGroups =
From t In transactions
Group t By Key = New With {
.Type = t.Type,
.State = t.State
} Into Group
Select New With {
.Type = Key.Type,
.State = Key.State,
.Quantity = Group.Count()
}
For Each tGroup In transactionGroups
Dim type = tGroup.Type
Dim state = tGroup.State
Dim quantity = tGroup.Quantity
Next

Summing values based on type then on Month

I have a List<Object> where Object contains Name, Month, Type, Value.
is it possible to sum all the Value based on type, and then based on month?
I am using .NET 3.0 in VB.NET
I was doing withe like 4 For loops, but its taking a while to run.
Is there a quicker way?
For clarification, I have something like this:
Name | Month | Type | Value
-----------------------------
hello | Jan | A | 1
hello | Jan | A | 2
hello | Jan | B | 2
hello | Feb | A | 3
hello1 | Jan | A | 6
hello1 | Jan | A | 2
hello1 | Jan | B | 2
hello1 | Feb | A | 3
I want to produce it into this
Name | Month | Type | Value
---------------------------
hello | Jan | A | 3
hello | Jan | B | 2
hello | Feb | A | 3
hello1 | Jan | A | 8
hello1 | Jan | B | 2
hello1 | Feb | A | 3
If LINQ is not possible:
You could use Dictionarys to hold the month-values and the type-values. So you only need one loop to fill all. Here is an example:
Foo is your object and FooType is your type:
Class Foo
Public Enum FooType As Int32
FirstType = 1
SecondType = 2
ThirdType = 3
FourthType = 4
FifthType = 5
End Enum
Public Property Name As String
Public Property Month As Date
Public Property type As FooType
Public Property value As Double
End Class
load some sample-data:
Dim monthRnd As New Random(Date.Now.Millisecond)
Dim yearRnd As New Random(Date.Now.Millisecond)
Dim valueRnd As New Random(Date.Now.Millisecond)
Dim fooTypeRnd As New Random(Date.Now.Millisecond)
Dim allFoos As New List(Of Foo)
For i As Int32 = 1 To 1000
Dim foo As New Foo
foo.Name = i & ". Foo"
foo.type = CType(fooTypeRnd.Next(1, 5), Foo.FooType)
foo.Month = New Date(2000 + yearRnd.Next(0, 12), monthRnd.Next(1, 12), 1)
foo.value = valueRnd.Next(1, 10000) * valueRnd.NextDouble()
allFoos.Add(foo)
Next
Fill total value, grouped by type and by month:
Dim sumTotal As Double = 0D
Dim monthSums As New Dictionary(Of Date, Double)
Dim typeSums As New Dictionary(Of Foo.FooType, Double)
For Each f As Foo In allFoos
sumTotal += f.value
If typeSums.ContainsKey(f.type) Then
typeSums(f.type) += f.value
Else
typeSums.Add(f.type, f.value)
End If
If monthSums.ContainsKey(f.Month) Then
monthSums(f.Month) += f.value
Else
monthSums.Add(f.Month, f.value)
End If
Next
Now you can access these values easily via Dictionary key.
For example:
Dim feb2010Value As Double = 0d
Dim feb2010 As New Date(2010, 2, 1)
If monthSums.ContainsKey(feb2010) Then
feb2010Value = monthSums(feb2010)
End If
I’m still not sure what you want but your example makes it seem as if you group by name and by month and by type at the same time.
Either way, that’s an aggregation into a group. The following should work.
Dim result = From obj In objects _
Group obj By obj.Name, obj.Month, obj.Type Into _
Value = Sum(obj.Value) _
Select Name, Month, Type, Value
However, you shouldn’t use a List(Of Object) – use the correct object type instead.
(Untested, haven’t got VB handy so the actual syntax can deviate in small ways.)