inquiry Data from data table to datagridview with filters - vb.net

i have datatable "dataitems" contain 98000 Rows And Datagridview have 11000 row
want to add the quantity form the datatable to the datagridview every item in it's row in the datagrid view
i am using this code but its take too too too much time to run and sometimes stop responding i need ideas to make it faster to run
Dim dt As New DataTable = najrndataset.dataitems
Dim Total As Integer
for x = 0 to datagridview1.rows.count -1
Dim b = datagridview1.rows(x).cells(1)
Dim c = 3
Total = (From r As DataRow In dt.AsEnumerable
Where r.Field(Of String)("Item_Number") = b And r.Field(Of Integer)("SOP_Type") = c
Select r.Field(Of Integer)("Quantity")).Sum
datagridview1.Rows(x).Cells(0).Value = Total
next

Hopefully you appreciate that if you have 11000 rows in a datagridview (terrible idea, by the way) and 98000 rows in a datatable, and you're running a loop that searches the table for each of the 11K items, and it does this by starting at number 1, and searching 98000 items for it, then going to 2 and searching 98000 items for it til you reach 11000 and searching 98000 items for it... At the end of the operation you're going to have performed 11000 x 98000 operations.. i.e. you're going to have performed 1.078 BILLION operations. This is why "its take too too too much time to run and sometimes stop responding" :)
You can speed this up by using a dictionary to track the sums, and it'll probably be fastest to index the 98000 items then set the dictionary from them:
Dim d as New Dictionary(Of String, Integer)
For Each di In najrndataset.dataitems.Where(Function(r) r.SOP_Type = 3)
If Not d.ContainsKey(di.Item_Number) Then
d(di.Item_Number) = di.Quantity
Else
d(di.Item_Number) += di.Quantity
End If
Next di
Then edit your grid (ugh; this isn't how datagridview are supposed to be used)
datagridview1.SuspendDrawing()
For x = 0 to datagridview1.rows.count - 1
Dim b = datagridview1.rows(x).cells(1)
Dim s As Integer
If d.TryGetValue(b, s) Then
datagridview1.rows(x).cells(0) = s
End If
Next x
datagridview1.ResumeDrawing()
You could also have a logic of "loop over the datagrid, putting 11000 items in the dictionary, loop over the datatable accumulating sums into the dictionary if the keys are present in the dictionary, loop over the datagriview putting the sums into the datagridview".

Not an answer, but too long for a traditional comment. I'm working to provide an answer separately.
I can't tell you how many times I've seen code like this:
Dim someVariable As New SomeObject
someVariable = someMethodToReturnSomeObject()
This pattern is bad code!
The New operator in the first line tells the compiler you want to allocate memory and run the constructor for your type. However, the next line assigns a new object... it throws away and forgets the memory and constructor work from the previous line. That's wasteful.
Instead, you want this:
Dim someVariable As SomeObject = someMethodToReturnSomeObject()
Worse, this speaks to a profound lack of understanding of the difference between a reference and an object, and what the computer is doing with your code in memory. This is a core principle of how many programming environments work. Code like the first example gives me big doubts of the capability of the programmer who writes it.

Related

How to make random number generator not repeat the same number more than twice vb? [duplicate]

I'm trying to randomize a number in VB.NET 3 times. And each time I randomize a number it should be different from the other two numbers.
For example I have 3 integers. Int1,Int2 and Int3. I will randomize Int1 between 1-10 , and then I will randomize Int2 between 1-10 however the value shouldn't be equal to the value I randomized in Int1 and the same goes for Int3 it shouldn't equal to Int1 and Int2.
I have figured out how to randomize a number, this is the code I'm using:
Dim RndNumber As Random
Dim num,num2 As Integer
RndNumber = New Random
num = RndNumber.Next(1, 11)
num2 = RndNumber.Next(1, 11)
Now I'm stuck on how I make num2 randomize a number between 1-10 that is not equals to num.
I appreciate any help, thanks.
In all the examples, RNG is a random number generator created from the NET Random class:
Private RNG = New Random()
Linq
If you only need two or three, you could loop until the current pick is not in the result set. But this is even simpler using some extension methods:
Dim nums = Enumerable.Range(1, 10).
OrderBy(Function(r) RNG.Next).
Take(3).
ToArray()
This starts with all numbers between 1 and 10, puts them in random order, takes the first 3 and stores them in the nums array. I used the multiline form, breaking after the .s to illustrate the steps.
Just change the range, size/count and Take() element as needed. For instance, for something like a lottery with 5 unique numbers 1-69 (condensed form):
Dim winners = Enumerable.Range(1, 69).OrderBy(Function(r) RNG.Next()).Take(5).ToArray()
Dim powerball = Enumerable.Range(1, 26).OrderBy(Function(r) RNG.Next()).Take(1).First
Since the Powerball can be a repeat of the first numbers, it comes from its own pool. Since we only want one, we dont need an array, just the First().
Manual
It is good to know the logic for these things, so this shows a manual version. This does it differently, by picking and actually checking random values:
' picked value storage
Dim picks As New List(Of Int32)
Dim pick As Int32 ' current candidate
Do
pick = RNG.Next(1, 11)
If picks.Contains(pick) = False Then
picks.Add(pick)
End If
Loop Until picks.Count = 3
Rather than loose vars, this uses a list to hold the picks. This makes it easy to see if the current pick has already been selected. For more than just a few values, use a HashSet(Of Int32) rather than a List for performance.
Random Pairs
To create a random sets of numbers with 2 of each, such as for a matching game, just double up the base pool of values then put them in random order:
' create pool of 2 values each for 1-13
Dim nums = Enumerable.Range(1, 13).ToArray()
' concat the set to make 2 of each value, randomize
Dim pool = nums.Concat(nums).OrderBy(Function(r) RNG.Next).ToArray()
For a manual method you would have to check the count of each value in the loop.
'Use up' Picks
One more variation is when you need a pool of randoms used periodically, but you don't know how many you will need in advance. Examples would be the balls for a BINGO game or a deck of cards.
Rather than a global indexer pointing to the last slot used (or next slot to use), a Stack(Of T) (or a Queue) will "use up" values as you need them:
' create, randomize pool of 100 ints
Dim nums = Enumerable.Range(1, 100).OrderBy(Function(r) RNG.Next).ToArray
' use array to create Stack<T>
Dim shoe As New Stack(Of Int32)(nums)
' same as:
Dim shoe = New Stack(Of Int32)(Enumerable.Range(1, 100).
OrderBy(Function(r) RNG.Next).ToArray())
This starts basically the same with 100 integers, randomized and stored in an array, but there is no Take(n) because we want them all. They values are then stored in a stack collection. Using it:
Console.WriteLine(shoe.Count)
For n As Int32 = 1 To 3
Console.WriteLine("Picked #{0}", shoe.Pop)
Next
Console.WriteLine(shoe.Count)
When you Pop a value it is removed from the collection automatically. If you use a lot of values from the shoe, you will want to check the count to make sure it is not empty.
100
Picked #12
Picked #69
Picked #53
97
After drawing 3 values, the shoe has only 97 values remaining.
Random Notes
In all cases your Random generator should be a form level object which you create once. Never create them in a loop or you will likely get the same value over and over.
The OrderBy(Function(r) RNG.Next) method of randomizing is usually good enough for casual use, but it is inefficient. If you will be randomizing large sets and/or using it frequently you should consider using a proper shuffle such as the Fisher-Yates shuffle shown here.

Code optimisation removing duplicates in VB

I'm looking to optimise my code. Specifically this process
Calculate a group of locations (basically squares on a grid)
Have a list of all the locations that have been calculated
Then I go through all these locations, 1 at a time.
The issue I'm having is removing or not including duplicate locations in the list. I've tried having a list of integers (integers to represent the location) but it's still very slow. To give you an idea of the numbers: I'm talking at least 15,000 different location calculations and around 1,000,000 possible locations.
Any help on this would be much appreciated!
Here is how I remove duplicates from an string array, perhaps it will be of help to you:
Dim OneDimensionalTable(1000) As String
....
OneDimensionalTable = RemoveDuplicates(OneDimensionalTable)
.....
Private Function RemoveDuplicates(ByVal items As String()) As String()
Dim noDupsArrList As New ArrayList()
For i As Integer = 0 To items.Length - 1
If Not noDupsArrList.Contains(items(i).Trim()) Then
noDupsArrList.Add(items(i).Trim())
End If
Next
Dim uniqueItems As String() = New String(noDupsArrList.Count - 1) {}
noDupsArrList.CopyTo(uniqueItems)
Return uniqueItems
End Function

Sum of elements (4) on mulitple lines in 2d array (txt file)

I have a text file that reads:
Left Behind,Lahaye,F,7,11.25
A Tale of Two Cities,Dickens,F,100,8.24
Hang a Thousand Trees with Ribbons,Rinaldi,F,30,16.79
Saffy's Angel,McKay,F,20,8.22
Each Little Bird that Sings,Wiles,F,10,7.70
Abiding in Christ,Murray,N,3,12.20
Bible Prophecy,Lahaye and Hindson,N,5,14.95
Captivating,Eldredge,N,12,16
Growing Deep in the Christian Life,Swindoll,N,11,19.95
Prayers that Heal the Heart,Virkler,N,4,12.00
Grow in Grace,Ferguson,N,3,11.95
The Good and Beautiful God,Smith,N,7,11.75
Victory Over the Darkness,Anderson,N,12,16
The last element of each line is a price. I would like to add up all the prices. I've been searching for so many hours now and cannot find a thing to answer my question. This seems soooo easy but I cannot figure it out!!! Please help out. BTW, this list is bound to change (adding of lines, deletion of lines, altering of lines) so if you can, please nothing concrete but instead leave the code open to changes. Thanks!!!
Just so you can see my pooooorrrr work, here is what I have (I think I deleted my code and rewrote a different way for several hours now.):
Dim Inv() As String = IO.File.ReadAllLines("Books.txt")
Dim t As Integer = Inv.Count - 1
Dim a As Integer = 0 to t
Dim sumtotal As String = sumtotal + Inv(4)
also,
for each line has either an "F" or an "N". how do I add up all the F's and all the N's. Do I do it via if statements?
First, you'll be better off using Double as your type instead of String. Second, observe how I use the Split function on each line, cast its last element as a double, and add it to the total. Yes, using an If Statement is how you can determine whether or not to add to the count of F or the count of N.
Dim lstAllLines As List(Of String) = IO.File.ReadAllLines("Books.txt").ToList()
Dim dblTotal As Double = 0.0
Dim intCountOfF As Integer = 0
Dim intCountOfN As Integer = 0
For Each strLine As String In lstAllLines
Dim lstCells As List(Of String) = strLine.Split(",").ToList()
dblTotal += CDbl(lstCells(3))
If lstCells(2) = "F" Then
intCountOfF += 1
Else
intCountOfN += 1
End If
Next

Function does not read last value

I am developing a program for a chain of restaurants, they should normally be able to calculate their guestcount and sales through a vb.net application with a connection to dbf files.
My dataset gets filled in correctly as is my datatable (checked by filling in datarowview = right data)
But then I get a problem, in all my functions using my datatable, the datatable skips the final value, in this case it is all values from a month so it either skips 31 or day 30.
Tried while, Tried for each, debugged alot (how i found it was the last value). But now I have no idea why the last value isn't used by the function
Public Function Getgctakeout(ByVal i_table As DataTable)
table = i_table
i = 0
gctakeout = 0
For Each row As DataRow In i_table.Rows
gctakeout = gctakeout + Convert.ToDouble(row(4))
Next row
'MessageBox.Show(gctakeout)
Return gctakeout
End Function
This function does not use the value of the last row to calculate gctakeout
what in the name of the lord is wrong :)
Assuming that your DataTable is really filled correctly, you have two other options to get the sum.
Use the old DataTable.Compute method which works also with .NET < 2.0
Use Linq-To-DatSet and it's Enumerable.Sum
1)
Dim Sum = CType(table.Compute("Sum(ColumnName)", Nothing), Double)
2)
Dim Sum = table.AsEnumerable().
Sum(Function(row)row.Field(Of Double)("ColumnName"))

Inspecting a Word mail merge data source programmatically

I want to iterate over all rows of a MS-Word mail merge data source and extract the relevant data into an XML.
I'm currently using this code:
Imports Microsoft.Office.Interop
Do
objXW.WriteStartElement("Recipient")
Dim objDataFields As Word.MailMergeDataFields = DataSource.DataFields
For Each FieldIndex As Integer In mdictMergeFields.Keys
strValue = objDataFields.Item(FieldIndex).Value
If Not String.IsNullOrEmpty(strValue) Then
strName = mdictMergeFields(FieldIndex)
objXW.WriteElementString(strName, strValue)
End If
Next
objXW.WriteEndElement()
If DataSource.ActiveRecord = LastRecord Then
Exit Do
Else
DataSource.ActiveRecord = Word.WdMailMergeActiveRecord.wdNextDataSourceRecord
End If
Loop
And it turns out to be a little sluggish (About 1 second for each row). Is there any way to do it faster?
My fantasy is finding a function like MailMergeDataSource.ToDatatable and then inspecting the datatable.
Any time you're iterating through something row by row, and then doing some kind of processing on each row, is going to get a little slow.
I would be inclined to approach this problem by having a step before this which prepared the mdictMergeFields collection so that it only contained elements that were not 'null or empty', this will mean you won't have to check for that on each iteration. You could do this in process, or 'sneakily' in the background while the user is doing something else.
The other thing to try (might help!) is to change the "Do... Loop" block so that you're not checking at the end of each imported row whether or the record is the 'last record'. Instead, get a count of the records, and then compare the current index to the knowm maximum (which might be quicker)
I.E.:
Dim i, x as Integer
i = ActiveDocument.MailMerge.DataSource.RecordCount
Do While x < i
objXW.WriteStartElement("Recipient")
Dim objDataFields As Word.MailMergeDataFields = DataSource.DataFields
For Each FieldIndex As Integer In mdictMergeFields.Keys
strValue = objDataFields.Item(FieldIndex).Value
If Not String.IsNullOrEmpty(strValue) Then
strName = mdictMergeFields(FieldIndex)
objXW.WriteElementString(strName, strValue)
End If
Next
objXW.WriteEndElement()
x += 1
Loop
I don't really work with the Office Interop much, but hopefully this might offer some assistance! Post back, let me know how it goes.
/Richard.