How to create simple set in vb.net - vb.net

What I mean by simple set is set in a sense of simple collections of stuffs
https://en.wikipedia.org/wiki/Set_(mathematics)
So not set in a sense of setting some variable into something.
I can use list(of string). However, say I don't care about order. I care that every item is there exactly once. Well, list is not appropriate right? Unless we have a different code.
I look for "set" in vb.net but I got results for different meaning of set.
as far as I know, hashtable(of string) is not available.
What would be the appropriate container?
The collection should only check whether an object exist or not in the set. That's it. O(1) for both operation

You want the HashSet(Of T) class. It works basically the same way as the Keys of a Dictionary, where items are first compared by the result of their GetHashCode methods and, if those are the same, their Equals method.
It's worth noting that HashSet(Of T) implements ICollection(Of T), and therefore IEnumerable(Of T), but not IList(Of T). That means that you can enumerate the collection using a For Each loop and you can get the Count, but items have no index.
As mentioned in the comment below, the Add method will return a Boolean to indicate whether the item was added or not, based on whether the item was already contained in the set (False) or not (True).

Related

Iterate over COM collection without foreach or .Item()

I need to iterate over a COM collection within Matlab. In VB I could use For Each item In Collection; in Python I could use for item in Collection. But unfortunately such constructs are not available in Matlab, which uses a simple loop, something like this:
for index = 1 : Collection.Count
item = Collection.Item(index);
% now do something useful with the item
end
This works well in general. But in my particular Collection, .Item() only accepts a string (the item's name), not a numeric index. So the code snippet above fails, because I do not know the item names in advance, before iterating over the loop.
In VB I could do item = Collection(index), but this fails in Matlab, since Matlab understands this as "item is Collection number index". In other words, Collection(2) tries to access the 2nd collection, not the 2nd item within Collection. This is why we typically use .Item(index) in Matlab, but as I said above, this fails in my particular collection where Item only accepts strings.
So my question is: can I iterate over a COM collection without a foreach construct or an Item(index) method?
I can tell you how to do it in COM, but I have no knowledge if Matlab can do this.
There is a special property DISPID_NEWENUM, with the name _NewEnum. Retrieving this property gets a IUnknown interface. You can get an interface to an IEnumVARIANT via QueryInterface.
So simply ask for this interface. Then call the Next method and retrieve VARIANT by VARIANT from your enumeration until the return value is no longer S_OK. Usually S_FALSE is returned when the end of the collection is reached. I always use a count of 1 when I call Next.
So if MatLab can use other interfaces than IDispatch, it should be possible.

Getting the Index of an Added ListView.Item

So all I am trying to do is get the index of an Item Programmatically added to a ListView from a Database.
Example:
Index = lvwNames.Items.Add(Player)
Player is a Class that uses Get/Set methods for ID, FirstName, and, LastName.
I have googled my heart out to no avail.
EDIT
So I had a GetInformation() procedure that populated my Player Class. So I was helped by a personal friend, I just needed to pass in my ListViewItem ByRef into there and I can grab the data I needed (SUCH A DUMMY!). Still having an issue with the Index. I would like to have the newly added item selected after it is added.
Getting the Index of an Added ListView.Item as per the title.
Method A: Look at the count
lvwNames.Items.Add(Player.ToString())
Dim ndx = lvwNames.Items.Count-1
Method B: Use an object
Dim lvi As New ListViewItem(Player.ToString())
lvwNames.Items.Add(lvi)
Dim ndx = lvi.Index
However, Player is an Object and there is no matching Add overload, with Option Strict (which all good code should use). The best you can do is pass it as ToString() as shown.
The result will be something like WindowsApplication1.Player unless your class overrides ToString(). In no case will the ListView parse it to put the properties ("Get/Set methods") as sub items which sort of sounds like what you expect.
For that, a DatagridView would be a better choice using a List(Of Player). Each property could be mapped to its own column.

Add to a list of lists with a key in VB.NET

I have a List(Of List(Of MyObject)). I have a method that returns a List(Of MyObject). I want to add each returned list to the list of lists. This is straightforward with LoL.Add(L). However, I want to be able to refer to each list by a key.
For example:
Key: "A"
MyObject1
MyObject2
Key: "B"
MyObject3
MyObject4
MyObject5
I have read over a dozen questions similar to this issue: group a list of lists, group a list of objects into a list of lists, and grouping a list of objects into a new list of list of objects. I can't seem to adapt the code in the answers to my problem.
All the questions I've read seem to assume that the list of lists is good to go and not being made piecemeal. All my attempts end up with type conversion errors and exceptions.
I'm not sure if I should use IGrouping or a Dictionary object instead, but my attempts there also failed.
I tried reverse-engineering another piece of code I have, which works in a similar fashion:
Dim assignmentListByDay = _routeAssignments.OrderBy(Function(a) a.AssignmentDate).
GroupBy(Function(a) a.AssignmentDate)
Here, _routeAssignments is of type IEnumerable(Of MyObject). assignmentListByDay becomes IEnumerable(Of IGrouping(Of Date, MyObject). Later in my code, I can do:
For Each assignmentList In assignmentListByDay
Dim ucAssignment As New ucSingleDayAssignment With _
{.AssignmentDate = assignmentList.Key,
.Assignments = assignmentList.ToList}
'do stuff
Next
I tried to do something similar by making my list of lists into a List(Of IGrouping(Of String, MyObject), but I can't figure out how to convert the returned list to IGrouping(Of String, MyObject) or if that's even the right approach.
Note that I'm free to change what is returned and the various types (List, IEnumberable, IGrouping, Dictionary), but I don't know which to use in this situation. Right now it seems like my best bet is to add each list to a list of lists or one giant list, then do the GroupBy. That doesn't seem terribly efficient, though.
In the end, my goal is to iterate over each group, then iterate over each list in that group.
A list of lists is not the right structure if you need a key. One choice would be a Dictionary (Of String, List(Of MyObject)).

VB.Net Copy a list to store original values to be used later

I have a WPF form that takes a list of objects that have locations and sizes and plots them on the canvas. I'm currently trying to implement an undo button that will throw out all the changes that have been made to the positions of the objects and revert back to the original collection that was retrieved when the form loaded.
As it stands now I go out to the database on the load of the form and get all the objects that will need to be displayed then assign the list that is returned to two seperate collections. The problem that comes up is that the two collections are actually pointers to the original collection and whenever one is changed the changes are reflected in the second collection.
Is it possible to copy a list of objects so that changes made to one collection won't affect the secondary collection?
So far I've tried simply using the assignment operator, passing the source collection into a function byval and scrolling through each element of the list manually adding it to the second collection and using linq to get all the objects from the original list and pushing the results to a separate temporary list and assigning the second collection to the temporary list.
I feel like I'm overcomplicating the issue but almost all the places I've come across while googling say that this behavior is by design, which I understand but it seems like this would be a fairly common idea.
Here's a function I have used before to make "Deep" copies of objects:
Public Function DeepCopy(ByVal ObjectToCopy As Object) As Object
Using mem as New MemoryStream
Dim bf As New BinaryFormatter
bf.Serialize(mem, ObjectToCopy)
mem.Seek(0, SeekOrigin.Begin)
Return bf.Deserialize(mem)
End Using
End Function
This is kind of a low level approach compared to some of the other answers, but allows you to deep copy any object. I've used it successfully in a situation similar to yours where I needed a deep copy of an array.
Simply assign all items of the listA to listB using this code
For Each elm In ListA
ListB.Add(elm)
Next
There was another answer that was since deleted that said to use var copy = list.ToList(); to get a copy of the list. This will work with the following caveat: Both lists will still reference the same objects, so any changes to those objects will reflect in both lists. As long as you're only changing the order of the objects in the list, this solution is perfectly viable.
You would have to create a new list and add copies of the items in list1 to it. You could do this using object initialization, e.g.
Dim list2 = (From item in list1
Select New ItemType With {.Property1 = item.Property1, .Property2 = item.Property2}.ToList()
An alternative would be to add a Copy constructor to ItemType
Public Sub New(item as ItemType)
Me.Property1 = item.Property1
Me.Property2 = item.Property2
End Sub
And your list copy could be simplified to
Dim list2 = (From item in list1
Select New ItemType(item)}.ToList()
Just beware that if any of the properties of your ItemType are references, you would need to make copies of these objects also. (This is known as a Deep Copy)

Comparing arrays in VB.NET

Let me provide a little detail to explain what I'm trying to accomplish before I get into the nuts and bolts of the question.
I've got two data sources - one is a SQL Server containing part numbers, descriptions, etc. The other is a CAD system that does not have a database in a traditional sense. What I'm trying to do is read out the bill of materials from the SQL Server and compare it to the CAD assembly drawing to ensure that the CAD system contains the same information as the SQL Server.
Getting the data from the SQL Server is fairly straight forward. I query the database and populate a datagrid. Done. Quick. Easy.
Getting the data from the CAD system is a little more involved. I have to load the assembly drawing to get a listing of all the component parts and then load those individual drawings to pull the "Part Number" property from the drawing. This is a somewhat time consuming and slow process (unfortunately) since each of the files must actually be accessed. I load those properties into an array (I guess a list might be more efficient).
So now I have a datagrid and array with part numbers. I need to compare them and colorize the grid accordingly. The grid should remain transparent if the part exists in both, color the row yellow if it only exists in the grid, and add a row colored red if only in the array.
As best I can tell, this means looping through the array on each line of the grid. The thought process is this:
Default the grid to yellow rows.
Loop through the grid and loop through the array to compare. If a match is found, make the row transparent and delete the element from the array.
After step 2 is completed, the array should only contain elements that are not found in the grid. Resize the array to remove the empty elements.
Add the elements of the array to the grid and color those new rows red.
The problems with this logic is that it seems expensive from a performance standpoint. Surely there is a better method? Also, if I modify the grid in some manner (like a resort) I have to go through the process again. I'd really appreciate some advice on this.
Thanks!
Note: written in Visual Studio 2005.
You could load the data from the CAD system in a dictionary (indexed by part number). Then you could go through the grid and check if it exists in the dictionary, which is a fast operation ( O(1) ). You could do exactly as you say, remove the found elements in the dictionary and add the remaining elements in the datagrid.
Here's some code for creating and using a dictionary (used C# style comments to preserve formatting):
//First argument is your key type, second is your item type
Dim cadParts As New Dictionary(Of Integer, Part)
//Add items to the parts dictionary
For Each part As Part In cadPartsArray
cadParts.Add(part.PartNumber,part)
Next
//Check if a part exists
Dim partNumber As Integer = 12345
If cadParts.ContainsKey(partNumber) ...
//Remove a part
cadParts.Remove(partNumber)
//Go through the remaining values
For Each part As Part In cadParts.Values ...
Edit:
1) Yes, if your key (here, part number) is a string, then a Dictionary(Of String,...) would be used.
2) I assumed you had a class named Part, which contained some information about a part. If you just have a part number, and no other info, then you could create a Hashset instead. It is basically the same as a dictionary, but with this structure the value is also your key. You would create a hashset like this:
Dim cadParts As New Hashset(Of String)
I won't go through code examples because it is very close to the Dictionary. ContainsKey becomes Contains, and Add accepts only one argument (which would be your part number here).
3) Yes, loop through them and add them to the hashset.
If part number is unique (it is not repeated in the values you want to search for) then you can use sorted dictionary. Then remove the duplicates and use remaining items.
To compare you can use part number as follows:
if(dictionary.ContainsKey(partNumber))
dictionary.Remove(partNumber)
end if
Given that there will only be a certain number of rows of the data grid visible at one time, it might be quicker to implement some code in the OnPaint method (I'm a little rusty here so apologies if that isn't exactly right) for each of the rows which checks against the array of part information and sets the background color of each row as it becomes visible, perhaps even marking each row as having been processed so the operation doesn't need to be repeated. There might be an initial performance gain here over processing all of the rows at once.
Of no real relevance; but from previous experience this sounds like you are interfacing with AutoDESK Inventor files?
Another solution could be to implement the IComparable(Of T) interface
That would require for you to build a class that you would use for both situation.
Public Class Item
Implements IComparable(Of Item)
Private _Description As String
Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
Private _PartNo As Integer
Public Property PartNo() As Integer
Get
Return _PartNo
End Get
Set(ByVal value As Integer)
_PartNo = value
End Set
End Property
Public Function CompareTo(ByVal other As Item) As Integer Implements System.IComparable(Of Item).CompareTo
' Your rules for comparing content for objects
If other.PartNo <> Me.PartNo Then Return 1
If other.Description <> Me.Description Then Return 1
' Return 0 if the object are the same
Return 0
End Function
End Class
Here is a small test that works with upper implementation.
Dim item As New Item
item.Description = "Desc"
item.PartNo = 34
Dim item2 As New Item
item2.Description = "Desc"
item2.PartNo = 35
Dim item3 As New Item
item3.Description = "Desc"
item3.PartNo = 36
Dim listFromDatabase As New Generic.List(Of Item)
listFromDatabase.Add(item)
listFromDatabase.Add(item2)
If listFromDatabase.Contains(item2) Then
MessageBox.Show("item2 was found in list")
End If
If Not listFromDatabase.Contains(item3) Then
MessageBox.Show("item3 was NOT found in list")
End If
Hope it helps a bit,
- Dan