Remove elements in an arraylist that exist in another arraylist - vb.net

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.

Related

How do I add a string input to an array?

basically i need to add a name to an array of candidates for an election. the user enters the candidates names, and i want to store them in an array. so far i have this:
Dim CandidateNames(candidates) As String
Dim x As Integer
'entering the names of each candidate so students can vote for them.
For x = 1 To candidates
Console.WriteLine("Enter a candidates name:")
CandidateNames(candidates) = Console.ReadLine()
Next
For x = 0 To candidates - 1
Console.WriteLine(CandidateNames(candidates) & " is candidate " & x)
Next
i want to then output all the names, which is what the second for loop does, but it only outputs the last entered name.
im in my second GCSE year of computer science, having never done any programming before so go easy on me please.
Here is the working code:
Dim candidates as Integer
candidates=5
Dim CandidateNames(candidates) As String
Dim x As Integer
For x = 0 To candidates
Console.WriteLine("Enter a candidates name:")
CandidateNames(x) = Console.ReadLine()
Next
For x = 0 To candidates
Console.WriteLine(CandidateNames(x) & " is candidate ")
Next
In your code, you are not storing and accessing the strings correctly.
There are two issues with your code. The first is with your loop bounds: the first loop is this:
For x = 1 to candidates
And the second loop is this:
For x = 0 to candidates - 1
In .NET, the lower bound for one-dimensional arrays is always 0 (insert rant about .NET array indexing design choices here), so you should be starting from 0 as in the second loop. I can never remember if VB arrays specify the upper bound or the count when you declare them, but conceptually the second is correct for the final index: if you want an array of n items, then it will be indexed from 0 to n - 1.
The second issue is that inside each loop, you are referring to CandidateNames(candidates) instead of CandidateNames(x). Instead of moving through each item in the loop in turn, you are only operating on the last item in the array.
Unless this is for an assignment requiring to use arrays, I'd suggest you consider using List(Of String) instead. Arrays make sense for a more limited set of uses cases, and I don't think this is one of them. Usually, the number of candidates for an election will be variable; with a list, you can have the user enter candidates until they're done, and the list will automatically expand as you go. Then, you can use a For Each loop to write out the contents of the list (though note you could use a For Each loop with an array as well). A list can still be accessed by index like an array.

Determine if an integer is a member of a list of integers

I need to determine if a particular integer does not exists in a datagridview column. I assume I should create an array of the integers from the dgv column, and then compare if the integer exists in the array. However, there is perhaps an easier or simpler way.
I have looked at many articles but none of them resolve my task. Some of the Stack Overflow articles show similar solutions but I can't quite determine what to do.
For a = 0 To Dgv1.RowCount - 1
If Not Dgv1(1, a).Value = Dgv0(1, m).Value Then
Dgv0(1, Dgv0.RowCount - 1).Value = Dgv0(1, m).Value
End If
Next
I hope to compare an integer with a column of integers in a datagridview and if it is present do nothing but if is not present add it to the datagrid view
Are you using wpf? If yes, create a model.
provide a checking mechanism at the setter, use observablecollection or list then bind it to the datagirdview
Get the row and column of the datagridview
then compare (means condtional statement) to the variable you wanna check
and of course it should be inside of loop, loop count is equal to the count of rows you have in the datagridview.
Here's an example code:
Dim column As String = "YourColumnNameHere"
' Assuming 2 is the number you wanna compare
Dim value As Integer = 2
For row As integer = 0 to dataGridView.RowCount - 1
If dataGridView.Rows(row).Cells(column).Value = value Then
' Do something here
Else
' Do something here
End If
Next

Remove A Row From A List Of DataRow

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

Comparing dates for overlap - not avoiding

I'm working on a timetabling piece of code. I am using a system of university modules and events associated to those modules, ie
Module CSC3039
Event1 - Lecture
Event2 - Lecture
Event3 - Practial etc
I need to check the times of each event in the module against each other and compare for clashes. The clashes do not need to be rectified, just highlighted. The table I will use is Events containing Event_ID (PK), Module_code (FK), Start_Date_Time, End_Date_Time plus other fields that don't matter here. I have figured out that I need to implement a For Each statement, ultimately resulting in an if statement such as:
if (startTime1 <= endTime2 or endTime1 >= startTime2) CLASH
My problem is trying to figure out the actual for loop here. I don't know what to write to declare my start times and end times. I presume it is a case of taking event1 and getting its start and end and then checking if event 2, 3 or 4 fit the above if statement. I'm trying to get this but could really use some guidance.
EDIT... Based on suggestions below I have implemented the following code:
'return all relevant tables from the Modules database, based on the module code entered by the user.
Dim eventTime = (From mods In db.Modules
Join evnt In db.Events On mods.Module_code Equals evnt.Module_code
Join rm In db.Rooms On rm.Room_ID Equals evnt.Room_ID
Join build In db.Buildings On build.Building_code Equals rm.Building_code
Where ((mods.Module_code = initialModCode) And (evnt.Room_ID = rm.Room_ID))
Select evnt.Event_ID, evnt.Module_code, evnt.Event_type, evnt.Start_Date_Time, evnt.End_Date_Time, build.Building_code, rm.Room_Number)
'use the gridview to display the result returned by the above query
gdvEventsTable.DataSource = eventTime
gdvEventsTable.DataBind()
Dim listClashes As New List(Of Array)
For i As Integer = 0 To eventTime.Count - 1
For j As Integer = i + 1 To eventTime.Count - 1
If (eventTime.ToList(i).Start_Date_Time < eventTime.ToList(j).End_Date_Time) And (eventTime.ToList(i).End_Date_Time > eventTime.ToList(j).Start_Date_Time) Then
MsgBox("Clash", MsgBoxStyle.MsgBoxSetForeground, "")
listClashes.Add(eventTime)
Else
MsgBox("No Clash", MsgBoxStyle.MsgBoxSetForeground, "")
End If
Next
Next
When trying to add an event to my array list I have noticed, in debug, that no events are sent to the list.
If you want to compare all the pairs of events that are in an array or some kind of a collection, you can use a loop like:
Dim ModuleEventArray() As ModuleEvent
'...
For i As Integer = 0 To ModuleEventArray.Length - 1
For j As Integer = i + 1 To ModuleEventArray.Length - 1
'test if ModuleEventArray(i) overlaps with ModuleEventArray(j)
Next
Next
ModuleEvent here would be another class or structure that has fields startTime and endTime. The test
if (startTime1 <= endTime2 or endTime1 >= startTime2)
is not enough to test for overlap, but maybe you can figure out the correct test yourself :)
EDIT:
Since I see you use some sort of collection, not array, the code you need should be something like:
For i As Integer = 0 To eventTime.Count - 1
For j As Integer = i + 1 To eventTime.Count - 1
If (eventTime.Item(i).Start_Date_Time < eventTime.Item(j).End_Date_Time) And (eventTime.Item(i).End_Date_Time > eventTime.Item(j).Start_Date_Time) Then
MsgBox("Clash")
Else
MsgBox("No Clash")
End If
Next
Next
Before you write your code, you need to first decide what your algorithm is going to be. For example, if you use the naive method your presume, the code is indeed straightforward (basically 2 nested loops) but the complexity if O(n²).
Depending on how much data you have, whether it is in a database, how likely you expect clashes to be, whether you always have the full list of events at the start or you need to find clashes incrementally, etc... different solutions might be preferred. One consideration is whether you need to partition the list into non-clashing sets of events or just produce a yes/no answer (one one for each event) stating whether there is a clash.
You might consider doing something different instead, like sorting the list by start time before you start comparing. That will allow you to walk the list only once.
My comparisons are coming from the database. Prior to the code below I have a query which returns all the records from my Events table, based on the user input of a Module_Code. This code will show the clashes, through a msgbox. I will be changing it to populate a list. It's not the prettiest and will probably lead to a lot of duplication but it achieves my main objective.
For Each evnt In eventTime
Dim startTime1 = evnt.Start_Date_Time
Dim endTime1 = evnt.End_Date_Time
For Each evat In eventTime
Dim startTime2 = evat.Start_Date_Time
Dim endTime2 = evat.End_Date_Time
If (startTime1 < endTime2) And (endTime1 > startTime2) Then
MsgBox("Clash")
Else
MsgBox("No Clash")
End If
Next
Next

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.