Remove A Row From A List Of DataRow - vb.net

I am using a Dictionary to create a key to a List Of DataRow. I want to iterate through each key and remove rows in the List. This will throw an out of range exception when I explicitly try to remove a row. How can I alter my code to accomplish this?
For Each g As KeyValuePair(Of [String], List(Of DataRow)) In grouped
For Each row As DataRow In g.Value
If CInt(segment(1)) <= 4 Then
'THIS THROWS AN OUT OF RANGE EXCEPTION
g.Value.Remove(row)
End If
Next
Next
I only want to remove specific rows based on criteria. Can someone post an example? I am on an old browser the "add comment" function does not work
Can you show a code example of how to use a predicate based on row.Item("ID") with the RemoveAll function?
I tried this and am getting an exception
g.Value.RemoveAll(Function(l) l.Item(Con.ID) Is l.Item(Con.ID).ToString)

Use List.RemoveAll. Not only will this make the act of removing all of the items easier than trying to remove items in some form of looping construct, but it will be dramatically more efficient as the List can reorganize all of the items once at the end, rather than moving the items down one index at a time over and over.

I figured it out using a reverse For loop. I did not see an examlpe on how to use the RemoveAll. Please post an example if you have time
For i As Integer = g.Value.Count - 1 To 0 Step -1
Dim row As DataRow = CType(g.Value(i), DataRow)
Dim segment() As String = row.Item(c._ID).ToString.Split("-"c)
If CInt(segment(1)) <= 4 Then
g.Value.Remove(row)
End If
Next i

Related

How to pass Value from Last Loop?

I have been attempting many ways to retrieve 2 collections together, while the first collection holds a comma-separated value in a column, we can not find a solution to passing the first collection value to the second For Each.
This code simply retrieves database rows and adds each result to our list control using the Add() method.
Dim transferstable As New DataTable
count = 0
For Each row As DataRow In transferstable.Rows
Dim name = Truncate(row.Item("name"), 42)
ListControl1.Add(name, row.Item("username")", row.Item("added"), avatars, row.Item("online"), images(count), 0)
count += 1
Next
Problem
We need to nest the loops, so we get the value from the first collection from the "avatars" column (image1,image2,image3) and call it from Add() - 4th parameter.
We only get always 1 string result into the view, while the actual query reports many rows with 2 strings (image1, image2) so I tried this:
Dim lst As New List(Of String) From {
transferstable.Rows(0).Item(8)
}
count = 0
For Each item As String In lst
For Each row As DataRow In transferstable.Rows
Dim name = Truncate(row.Item("name"), 42)
ListControl1.Add(name, row.Item("username")", row.Item("added"), item, row.Item("online"), images(count), 0)
count += 1
Next
Next
And the still the same single result! (8) is the GROUP_CONCAT column for "avatars" How do we pass this list over to the 4th parameter?
We want to retrieve these as URL remote images and render them to view with Bitmap.
Expected result:
A list of 15x15 pictures that represent each split result from GROUP_CONCAT(avatars)
I've been at all different ways to do this for most of the day, I know nesting is the right direction but I can't figure out why only 1 result is coming back (image1 - not image1,image2, etc.)
Some physical image files do not exist anymore, so rendering that to view also has it halt after a few single string results, so it quits and gives an error, like a 404 but does not proceed with the 180 other rows.
https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/for-each-next-statement

inquiry Data from data table to datagridview with filters

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.

Remove elements in an arraylist that exist in another arraylist

i have 2 array list, dateListDead and dateListNotMinggu. Both is DateTime List of Array. This is the ilustration of the date value in list of array
The arrayList value
its supposed to remove specific element that exist in other array list.
so far i tried, this code it's not working.
Dim d, x As Integer
For x = 0 To dateListDead.Count - 1
For d = 0 To dateListNotMinggu.Count - 1
If dateListNotMinggu(d) = dateListDead(x) Then
dateListNotMinggu.RemoveAt(d)
End If
Next
Next
the error is : index out of range. how could it be ? i define the parameter of end looping base on arraylist.count -1
The main is that you are using a For loop from the first index to the last index but you don't account for the change of index when you remove a value. If there might be multiple values then you should start and the end rather than the beginning. In that case, removing an item won't affect the indexes of the items you are yet to test. If there can only be one match then you should be exiting the loop when you find one.
Either way, while you don't have to, I would suggest using a For Each loop on the outside. If you want to perform an action for each item in a list then that's exactly what a For Each loop is for. Only use a For loop if you need to use the loop counter for something other than accessing each item in turn.
For multiple matches:
For Each dateDead As Date In dateListDead
For i = dateListNotMinggu.Count - 1 To 0 Step -1
If CDate(dateListNotMinggu(i)) = dateDead Then
dateListNotMinggu.RemoveAt(i)
End If
Next
Next
For a single match:
For Each dateDead As Date In dateListDead
For i = 0 To dateListNotMinggu.Count - 1
If CDate(dateListNotMinggu(i)) = dateDead Then
dateListNotMinggu.RemoveAt(i)
Exit For
End If
Next
Next
Note that I have also cast the Date values as that type for comparison, which is required with Option Strict On. Option Strict is Off by default but you should always turn it On because it will help you write better code by focusing on data types.
Also, the code above would work with a List(Of Date) as well as an ArrayList but the casts would not be required with a List(Of Date). That's one of the advantages of using a generic List(Of T) over an ArrayList, which paces no restrictions on what it can contain.
If you really must use a For loop because that's what your homework assignment says then it would look like this:
For i = 0 To dateListDead.Count - 1
For j = dateListNotMinggu.Count - 1 To 0 Step -1
If CDate(dateListNotMinggu(j)) = CDate(dateListDead(i)) Then
dateListNotMinggu.RemoveAt(j)
End If
Next
Next
and this:
For i = 0 To dateListDead.Count - 1
For j = 0 To dateListNotMinggu.Count - 1
If CDate(dateListNotMinggu(j)) = CDate(dateListDead(i)) Then
dateListNotMinggu.RemoveAt(j)
Exit For
End If
Next
Next
Note that it is convention to use i as a first option for a loop counter, then j for the first nested loop, then k for the second nested loop. You should only use something else if you have good reason to do so. Remember that the loop counter doesn't represent the value in the list but rather its index. That's why you use i for index and not d for date or the like.
EDIT:
As per Jimi's comment below, the way this would usually be tackled is with a simple LINQ query. If you were using LINQ then you definitely wouldn't be using an ArrayList but rather a List(Of Date). In that case, the code would look like this:
dateListNotMinggu = dateListNotMinggu.Except(dateListDead).ToList()
If you were completely insane and wanted to use LINQ and ArrayLists then this would work:
dateListNotMinggu = New ArrayList(dateListNotMinggu.Cast(Of Date)().
Except(dateListDead.Cast(Of Date)()).
ToArray())
Take note that, as I replied in the comments, using LINQ will generate a new list, rather than changing the existing one.

Checking duplicate Values on DataGrid

I have a DataGrid which is bound with a DataTable having two columns which store sequences, in my DataGrid these sequence columns are bound with DataGridViewComboBoxes. User is able to set sequence from ComboBoxes. Default values in sequence columns is 0.
I just want to check duplicacy in both the columns on button click, user should not be able select any duplicate value in both the columns.
If i implement it by using ToTable method of DataView to find distinct values it also takes rows with value "0"
if i implement unique constraint on column on DataTable it also checks for 0.
If try to remove values with 0 it also changes DataGrid As DataGrid is bound with DataTable
If i try to declare a new DataTable from existing dataTable it also gets bound to DataGrid Automatically.
Please help me.
Here is an example of how you can check for duplicate values in a DataTable:
Option Strict On
Module Module1
Sub Main()
Dim dt As New DataTable
dt.Columns.Add("mycolumn", GetType(Integer))
dt.Rows.Add({"1"})
dt.Rows.Add({"2"})
dt.Rows.Add({"2"})
dt.Rows.Add({"4"})
dt.Rows.Add({"7"})
Dim duplicateDictionary As New Dictionary(Of Integer, Integer) 'value, count
For Each row As DataRow In dt.Rows
Dim count As Integer = 0
Dim value As Integer = CInt(row("mycolumn"))
duplicateDictionary.TryGetValue(value, count)
duplicateDictionary(value) = count + 1
Next
For Each kv As KeyValuePair(Of Integer, Integer) In duplicateDictionary
If kv.Value > 1 Then 'we have a duplicate
Debug.WriteLine("{0} is a duplicated value, encountered {1} times", kv.Key, kv.Value)
End If
Next
End Sub
End Module
Adding a UniqueConstraint is possible as well, but I find it too intrusive at times, depending on how your editing works. For direct in-grid editing, you may want the user to save a non-valid record in memory, and allow to fix the error, showing validation errors instead of constraint violation exception. Of course you never save invalid data to the database.

Simplest/fastest way to check if value exists in DataTable in VB.net?

I have a DataTable (currently with multiple columns but I could just grab one column if it makes it easier). I want to check if a String value exists in a column of the DataTable. (I'm doing it many times so I want it to be reasonably fast.)
What is a good way to do this? Iterating through the DataTable rows each time seems like a bad way. Can I convert the column to a flat List/Array format, and use a built in function? Something like myStrList.Contains("value")?
You can use select to find whether that value exist or not. If so, it returns rows or it will not. Here is some sample code to help you.
Dim foundRow() As DataRow
foundRow = dt.Select("SalesCategory='HP'")
If the data in your DataTable doesn't change very often, and you search the DataTable multiple times, and your DataTable contains many rows, then it's likely going to be a lot faster to build your own index for the data.
The simplest way to do this is to sort the data by the key column so that you can then do a binary search on the sorted list. For instance, you can build an index like this:
Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As List(Of String)
Dim index As New List(Of String)(table.Rows.Count)
For Each row As DataRow in table.Rows
index.Add(row(keyColumnIndex))
Next
index.Sort()
Return index
End Function
Then, you can check if a value exists in the index quickly with a binary search, like this:
Private Function ItemExists(index As List(Of String), key As String) As Boolean
Dim index As Integer = index.BinarySearch(key)
If index >= 0 Then
Return True
Else
Return False
End If
End Function
You could also do the same thing with a simple string array. Or, you could use a Dictionary object (which is an implementation of a hash table) to build a hash index of your DataTable, for instance:
Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As Dictionary(Of String, DataRow)
Dim index As New Dictionary(Of String, DataRow)(table.Rows.Count)
For Each row As DataRow in table.Rows
index(row(keyColumnIndex)) = row
Next
Return index
End Function
Then, you can get the matching DataRow for a given key, like this:
Dim index As Dictionary(Of String, DataRow) = BuildIndex(myDataTable, myKeyColumnIndex)
Dim row As DataRow = Nothing
If index.TryGetValue(myKey, row) Then
' row was found, can now use row variable to access all the data in that row
Else
' row with that key does not exist
End If
You may also want to look into using either the SortedList or SortedDictionary class. Both of these are implementations of binary trees. It's hard to say which of all of these options is going to be fastest in your particular scenario. It all depends on the type of data, how often the index needs to be re-built, how often you search it, how many rows are in the DataTable, and what you need to do with the found items. The best thing to do would be to try each one in a test case and see which one works best for what you need.
You should use row filter or DataTable.Rows.Find() instead of select (select does not use indexes). Depending on your table structure, specifically if your field in question is indexed (locally), performance of either way should be much faster than looping through all rows. In .NET, a set of fields needs to be a PrimaryKey to become indexed.
If your field is not indexed, I would avoid both select and row filter, because aside from overhead of class complexity, they don't offer compile time check for correctness of your condition. If it's a long one, you may end up spending lots of time debugging it once in a while.
It is always preferable to have your check strictly typed. Having first defined an underlying type, you can also define this helper method, which you can convert to extension method of DataTable class later:
Shared Function CheckValue(myTable As DataTable, columnName As String, searchValue As String) As Boolean
For row As DataRow In myTable.Rows
If row(columnName) = searchValue Then Return True
Next
Return False
End Function
or a more generic version of it:
Shared Function CheckValue(myTable As DataTable, checkFunc As Func(Of DataRow, Boolean)) As Boolean
For Each row As DataRow In myTable.Rows
If checkFunc(row) Then Return True
Next
Return False
End Function
and its usage:
CheckValue(myTable, Function(x) x("myColumn") = "123")
If your row class has MyColumn property of type String, it becomes:
CheckValue(myTable, Function(x) x.myColumn = "123")
One of the benefits of above approach is that you are able to feed calculated fields into your check condition, since myColumn here does not need to match a physical myColumn in the table/database.
bool exists = dt.AsEnumerable().Where(c => c.Field<string>("Author").Equals("your lookup value")).Count() > 0;