Compare 2 datasets (one to many rows) - vb.net

VB.NET Winforms
I need to compare 2 datatables (that are each within their own dataset) and conclude with a judgement of OK or NG, one is a reference table, the other is raw data from a machine. One table would have one row with a QTY, the other table would have multiple rows to match that QTY, hopefully. The purpose here is verify torque values on fasteners. The first table here is the data I would get as what would be required for the torques to pass. Let's call this table dtTorquesRequired:
NINDEX NQTY_REQ NMIN NMAX
7 1 33.0 59.0
8 4 33.0 59.0
9 2 4.5 7.5
12 2 4.5 7.5
NINDEX will be the common column between the 2 datasets.
Let's use the last row as an example. The last row tells me that I need to have 2 torques from NINDEX 12 with a value between 4.5 - 7.5.
This is the data that I have to judge. Let's call this table dtTorquesPerformed:
NINDEX NTORQUE_NO NTORQUE_STATUS NTORQUE_VALUE
7 1 1 42.56
8 1 1 42.22
8 2 1 42.49
8 3 1 42.10
8 4 1 42.37
9 1 1 7.01
9 2 1 7.10
12 1 1 5.68
12 2 1 5.81
The judgement needs to have NINDEX match between the 2 tables, NTORQUE_STATUS must be 1, and NTORQUE_VALEU must be between NMIN and NMAX in dtTorquesRequired.
Now, I am currently doing this in my application using only SQL row count results, but it is slow. I am doing it via multiple chatty calls instead of a chunky call to the DB, and then work with the resulting datasets.
This is how I am currently doing it, but it is slow. I want to do all the processing locally instead of using SQL:
Private Function TorqueJudgement(ckanban As String) As Boolean
Dim ccode As String
Dim cline As String
Dim dsTorquesRequired As New DataSet
Dim dtTorquesRequired As New DataTable
Dim AreAllTorquesGood As Boolean = False
Dim BadTorqueCount As Int16 = 0
ccode = ckanban.Substring(0, 5)
cline = ckanban.Substring(5, 2)
SQL.ExecQuery("SELECT ....") ''GETS DTTORQUESREQUIRED
dsTorquesRequired = SQL.SQLDS
dtTorquesRequired = dsTorquesRequired.Tables(0)
dtTorquesRequired.Columns.Add("PassFail")
Dim rc As Int16 = -1 '//
For Each dr As DataRow In dsTorquesRequired.Tables(0).Rows
rc += 1
Dim nindex As Int16 = dr("NINDEX")
Dim qtyrequired As Int16 = dr("NQTY_REQ")
Dim nmin As Decimal = dr("NMIN")
Dim nmax As Decimal = dr("NMAX")
SQL.AddParam("#CKANBAN", ckanban)
SQL.AddParam("#NINDEX", nindex)
SQL.AddParam("#QTY_REQ", qtyrequired)
SQL.AddParam("#NMIN", nmin)
SQL.AddParam("#NMAX", nmax)
SQL.ExecQuery("SELECT ...") ''Searches for rows that match dtTorquesRequired
dtTorquesRequired.Rows(rc)("PassFail") = SQL.RecordCount
If Not dtTorquesRequired.Rows(rc)("NQTY_REQ") <= dtTorquesRequired.Rows(rc)("PassFail") Then
BadTorqueCount += 1
End If
Next
If BadTorqueCount > 0 Then
AreAllTorquesGood = False
ElseIf BadTorqueCount = 0 Then
AreAllTorquesGood = True
End If
Return AreAllTorquesGood
End Function
How can I query a datatable object locally using VB.NET without using SQL
Thanks in advance

You can use DataView objects to "query" DataTables:
Dim dtb As New DataTable
dtb.Columns.Add("Col0")
dtb.Columns.Add("Col1")
dtb.Columns.Add("Col2")
dtb.Columns.Add("Col3")
dtb.Rows.Add("R0C0", "R0C1", "A", "A")
dtb.Rows.Add("R1C0", "R1C1", "A", "A")
dtb.Rows.Add("R2C0", "R2C1", "A", "B")
dtb.Rows.Add("R3C0", "R3C1", "B", "B")
Dim dvw As New DataView(dtb)
dvw.RowFilter = "Col0>'R1C0'" 'WHERE
dvw.Sort = "Col2 DESC" 'ORDER BY
For Each drv As DataRowView In dvw
Dim drw As DataRow = drv.Row
Console.WriteLine(drw("Col0") & " " & drw("Col1") & " " & drw("Col2") & " " & drw("Col3"))
Next
Dim dtbDistinct As DataTable = dtb.DefaultView.ToTable(True, {"Col2", "Col3"}) 'DISTINCT
For Each drw As DataRow In dtbDistinct.Rows
Console.WriteLine(drw("Col2") & " " & drw("Col3"))
Next
Console.ReadKey()

Related

How can put split integers in a two-dimensional array?

I making matrix calculator. so, Textbox_A contains vbCrLf and tries to put it in Array_A.
and I would like to put Array_A in Result Matrix.
It's like
Textbox_a:
(1 2 3)
(4 5 6)
[Matrix to Array]
Array_a(0)(0) = 1
Array_a(0)(1) = 2
Array_a(0)(2) = 3
Array_a(1)(0) = 4
...
I have done string splits through several articles, but changing them to integers causes many problems.
This picture is Matrix_A and result Matrix
I don't know if the size of your initial matrix, formatted as text, is fixed, but here is some code to help you get started. The code tries to calculate the number of columns and rows.
The actual code is in the TextToArray function, that takes as input as string formatted as you described:
(1 2 3) (cr/lf)
(4 5 6)
and outputs a two dimensional array. The Main sub is just used to call TextToArray and display results.
So, in your example, you should pass TextBox_A.Text to TextToArray
There is minimal error checking here - you should add more to validate that data entered are numbers (check the Integer.TryParse function) and that the number of columns is the same across lines.
Sub Main(args As String())
Dim myInput As String = "(1 2 3)" & vbCrLf & "(4 5 6)"
Dim ret As Integer(,) = TextToArray(myInput)
If ret IsNot Nothing Then
For i As Integer = 0 To ret.GetUpperBound(0) - 1
For n As Integer = 0 To ret.GetUpperBound(1) - 1
Console.WriteLine(i & "," & n & "=" & ret(i, n))
Next
Next
Else
Console.WriteLine("No results - wrong input format")
End If
Console.ReadLine()
End Sub
Private Function TextToArray(matrix As String) As Integer(,)
Dim noOfRows As Integer = matrix.Split(vbCrLf).Count
Dim noOfColumns As Integer = 0
If noOfRows > 0 Then
noOfColumns = matrix.Split(vbCrLf)(0).Split(" ").Count
End If
If noOfColumns > 0 And noOfRows > 0 Then
Dim ret(noOfRows, noOfColumns) As Integer
Dim lines As String() = matrix.Split(vbCrLf)
Dim row As Integer = 0
For Each line As String In lines
Dim col As Integer = 0
line = line.Replace("(", "")
line = line.Replace(")", "")
For Each s As String In line.Split(" ")
ret(row, col) = Integer.Parse(s)
col += 1
Next
row += 1
Next
Return ret
Else
Return Nothing
End If
End Function
This outputs:
0,0=1
0,1=2
0,2=3
1,0=4
1,1=5
1,2=6

Count a specific string in a multiline textbox (VB.Net)

I very much want to count the values in a multiline textbox each time each value appears in descending order> in ascending order. I tried a lot but nothing works as it should. (VB.Net)
Textbox1.Lines
2
3
2
2
4
7
7
7
28
28
Expected Output: Textbox2.Lines
2 = Count = 3
7 = Count = 3
28 = Count = 2
3 = Count = 1
4 = Count = 1
What i try and dind't worked.
#1
Public Function CountCharacter(ByVal value As String, ByVal ch As Char) As Integer
Dim cnt As Integer = 0
For Each c As Char In value
If c = ch Then
cnt += 1
End If
Next
Return cnt
End Function
#2
Dim a As String = "this is test"
Dim pattern As String = "t"
Dim ex As New System.Text.RegularExpressions.Regex(pattern)
Dim m As System.Text.RegularExpressions.MatchCollection
m = ex.Matches(a)
MsgBox(m.Count.ToString())
#3
Public Shared Function StrCounter(str As String, CountStr As String) As Integer
Dim Ctr As Integer = 0
Dim Ptr As Integer = 1
While InStr(Ptr, str, CountStr) > 0
Ptr = InStr(Ptr, str, CountStr) + Len(CountStr)
Ctr += 1
End While
Return Ctr
End Function
You need to remember which strings have already appeared and then count them. To do that, you can use a dictionary.
Dim dict_strCount As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)()
' Run over each line in the input
For Each line As String In tb_yourTextBox.Text.Lines
' Check if we have already counted the string in this line
If dict_strCount.ContainsKey(line) Then
dict_strCount(line) += 1 ' if so, increment that count
Else
dict_strCount.Add(line, 1) ' if not, add it to the dictionary (with count of 1)
End If
Next
tb_yourOutputTextBox.Text = String.Empty ' clear the output
' run over all the elements in the dictionary in ascending order by value
' and output to the output textbox
For Each kvp As KeyValuePair(Of String, Integer) In dict_strCount.OrderBy(Function(x) x.Value)
tb_yourOutputTextBox.Text += kvp.Key & ": " & kvp.Value.ToString() & vbNewLine
Next
You may test it here

Does this code not work because it will take a long time or is it broken?

I am currently trying to generate random numbers until a user defined amount of identical numbers in a row appears. The numbers range from 1 to 10.
When I want 8 identical numbers in a row it takes between 3 and 5 seconds. It goes through about 5 million random numbers. When I want 9 in a row I have left it for over 45 minutes without any luck.
I thought it would only take about 10 times the amount of time as it did for 8 since 1/10 to the power of 9 is only ten times bigger than to the power of 8.
Is it a problem with my code or have I messed up the maths?
MY CODE:
Sub Main()
Console.WriteLine("how many identical numbers in a row do you want")
Dim many As Integer = Console.ReadLine
Dim storage(many - 1) As Integer
Dim complete As Boolean = False
Dim part As Integer = 0
Dim count As Integer = 0
Randomize()
While complete = False
count += 1
storage(part) = Int(Rnd() * 10) + 1
' Console.WriteLine(storage(part))
part += 1
If part = many Then
part = 0
End If
If storage.Min = storage.Max Then
complete = True
End If
End While
Console.WriteLine("===========")
Console.WriteLine(count.ToString("N"))
Console.WriteLine("===========")
For i = 0 To many - 1
Console.WriteLine(storage(i))
Next
Console.ReadLine()
End Sub
There is more than one possibility: it could be that the VB Rnd function is written so that it can never work, or it could be that it really does simply take a long time sometimes. You would have to do many trials and average them out to find out if your expected ratio of 1:10 works as expected.
Incidentally, you don't need to keep the last ten numbers. You can just keep a count of how many times the next random number equals the previous number. Also I suggest trying a different RNG (random number generator), for example the .NET one:
Module Module1
Dim rand As New Random()
Sub Main()
Console.Write("How many identical numbers in a row do you want: ")
Dim howMany As Integer = CInt(Console.ReadLine)
Dim count As Long = 0
Dim previous = -1
Dim soFar = 0
Dim n = 0
While soFar < howMany
count += 1
n = rand.Next(1, 11)
'Console.Write(n.ToString() & " ")
If n = previous Then
soFar += 1
Else
'If previous >= 0 Then
' Console.WriteLine(" X")
' Console.Write(n.ToString() & " ")
'End If
previous = n
soFar = 1
End If
End While
Console.WriteLine()
Console.WriteLine("Tries taken: " & count.ToString())
Console.ReadLine()
End Sub
End Module

Datatable: add computed column with subtotals

I have generated a datatable by querying a large mysql-database.
This resulted in about 7000 rows. Now I would like to add a column that calculates the sum of column "QUANTITY" for every unique value in column "BREAK2" (like a 'group by').
Example:
BREAK2 | QUANTITY | COMPUTED
A 10 30
B 20 40
A 10 30
A 20 30
B 20 40
If I use
Dim dc As DataColumn = New DataColumn
dc.DataType = System.Type.GetType("System.Double")
dc.ColumnName = "SumQTY"
dt.Columns.Add(dc)
For Each dr As DataRow In DT.Rows
dr("SumQTY") = DT.Compute("Sum(QUANTITY)", "BREAK2 = '" & dr("BREAK2") & "'")
Next
it takes a long time (several minutes before I break it off).
When I query the same data with a querytool named BRIO, and I add a computed column with formula "Sum(QUANTITY,BREAK2)", it takes about 1 second to get the results.
What could be the difference between these methods?
Why is the VB.net code taking so long?
I have tried to use LINQ but same bad performance.
Thanks.
Nas
This seems to be pretty fast
Sub Main
Dim sw = new Stopwatch()
sw.Start()
Dim rnd = new Random()
Dim dt = new DataTable()
dt.Columns.Add("BREAK2", "".GetType())
dt.Columns.Add("QUANTITY", 0.GetType())
dt.Columns.Add("COMPUTED", 0.GetType())
for i = 0 to 7000
dt.Rows.Add("A", rnd.Next(0, 30))
dt.Rows.Add("B", rnd.Next(0, 30))
dt.Rows.Add("C", rnd.Next(0, 30))
dt.Rows.Add("D", rnd.Next(0, 30))
Next
Dim keys As List(Of String) = dt.AsEnumerable() _
.Select(Function(x) x("BREAK2").ToString()) _
.Distinct().ToList()
For Each k in keys
Dim listOfRow = dt.AsEnumerable() _
.Where(Function(x) x.Field(Of String)("BREAK2") = k).ToList()
Dim total = listOfRow.Sum(Function(t) t.Field(Of Integer)("QUANTITY"))
For Each row in listOfRow
row.SetField(Of Integer)("COMPUTED", total)
Next
Next
sw.Stop()
Console.WriteLine(sw.ElapsedMilliseconds)
End Sub
As I have said in my comment above, it is silly to recalculate the same value for thousands of time.
Using LINQ you could extract the unique values of the column BREAK2, execute the sum just one time for the subset of rows identified by the key and then set the same subset of rows with the total.
On my PC this takes at max 100 milliseconds.

Limit the number of rows

How can I limit the number of rows in Datagridview column to lets say 50 record and place the other 50 into the next column on the same Datagridview? In my vb.net project I can't be scrolling up and down.
The Datagridview pull the data from an excel sheet.
Appreciate your help
Try this.
dataGridView1.RowCount = 50; //Let say we have 50 rows
if (dataGridView1.Rows.Count >= 50)
{
//TODO:
}
I assumed this as single column table ..
dim tbl as DataTable '--------> this is your displayed table
dim nLimit as Integer = 15 '----- this is your limit as you want
dim dc as DataColumn
'get column name
dim sCol as String = tbl.Columns(0).Name
dim nRow as Integer = tbl.rows.count
'how much column ?
dim n as Integer = nRow / nLimit
if n * nLimit < nRow then n+= 1
for x as Integer = 1 to n
dc = New DataColumn
dc.DataType = System.Type.GetType("System.String")
dc.Caption = sCol & n.ToString
dc.ColumnName = sCol & n.ToString
tbl.Columns.Add(dc)
next
Then you can show it ...