VB.NET: Value of Type 'Integer' cannot be converted to 'System.Array' - vb.net

In the below code I am getting the following error on line If (Not hash.Add(Numbers(Num))) Then Value of Type 'Integer' cannot be converted to 'System.Array'. What am I doing wrong?
Module Module1
Sub Main()
Dim array() As Integer = {5, 10, 12, 8, 8, 14}
' Pass array as argument.
Console.WriteLine(findDup(array))
End Sub
Function findDup(ByVal Numbers() As Integer) As Integer
Dim hash As HashSet(Of Array)
For Each Num In Numbers
If (Not hash.Add(Numbers(Num))) Then
Return (Num)
End If
Next
End Function
End Module

Inside findDup, this
Dim hash As HashSet(Of Array)
Should be
Dim hash As HashSet(Of Integer)
EDIT: As suggested by #Damien_The_Unbeliever, the code
This line
If (Not hash.Add(Numbers(Num))) Then
Should be
If (Not hash.Add(Num)) Then

You have created a hashset of Array, rather than a hashset of Integer. You could change it to Integer, and alter how you try to add things in your loop to:
Function findDup(ByVal Numbers() As Integer) As Integer
Dim hash As New HashSet(Of Integer)
For Each Num In Numbers
If (Not hash.Add(Num)) Then
Return (Num)
End If
Next
End Function
I am hoping you realize that it will only ever find the first duplicate, and doesn't return a value of any type if it doesn't find a duplicate.

You've declared hash to be HashSet(Of Array), which means that it holds arrays. But you're trying to add integers to it.
You need to either change its declaration (HashSet(Of Integer)) or change the call to Add (Add(Numbers)).
Which solution you use will depend on your intent. My guess is that you want to change the type of hash.

Is this what you mean to do?
Sub Main()
Dim someArray() As Integer = {5, 10, 12, 8, 7, 8, 8, 10, 14, 10}
' Pass array as argument.
Dim foo As List(Of Integer) = findDups(someArray)
'foo contains a list of items that have more than 1 occurence in the array
End Sub
Function findDups(ByVal Numbers() As Integer) As List(Of Integer)
Dim rv As New List(Of Integer)
For idx As Integer = 0 To Numbers.Length - 1
If Array.LastIndexOf(Numbers, Numbers(idx)) <> idx Then
If Not rv.Contains(Numbers(idx)) Then rv.Add(Numbers(idx))
End If
Next
Return rv
End Function

Related

Arraylist.Contains Doesn't Return True VB.NET

comps.Contains doesn't return TRUE even though comps contains it.
I debugged it step by step and I can't see where the problem is.
By the way the purpose of the code is to show the pairs that sum up to SUM value. (If the sum is 5 and theres 1 and 4 then the code should return 1 and 4)
Public Function getPairs(ByVal array As ArrayList, ByVal sum As Integer)
Dim comps, pairs As New ArrayList
For index = 0 To array.Count - 1
If (comps.Contains(array(index)) = True) Then
pairs.Add(array(index))
Else
comps.Add(sum - array(index))
End If
Next
Return pairs
End Function
Sub Main()
' 1,3,2,5,46,6,7,4
' k = 5
'Dim arraylist As New ArrayList()
Console.Write("Enter your array :")
Dim arraylist As New ArrayList
arraylist.AddRange(Console.ReadLine().Split(","))
Console.Write("Enter the sum:")
Dim sum As Integer = Console.ReadLine()
getPairs(arraylist, sum)
Console.ReadKey()
End Sub
The ArrayList you populate from user input contains strings (results from splitting the user input string). The comps ArrayList contains integers (results from subtraction). When it tries to find the string "2" in the ArrayList that contains a 2, it fails.
You should convert your user input to integers so that you are comparing the same data types.
First, turn on Option Strict. Tools Menu -> Options -> Projects and Solutions -> VB Defaults. This will point out problems with your code and help you to avoid runtime errors.
ArrayList is not used much in new code but is around for backward compatibility. List(Of T) is a better choice for new code.
Module Module1
Sub Main()
' 1,3,2,5,46,6,7,4
' k = 5
'Dim arraylist As New ArrayList()
Console.Write("Enter your array :")
Dim arraylist As New ArrayList
'Option Strict would not allow this line to compile
'.Split takes a Char, the same c tells the compiler that "," is a Char
arraylist.AddRange(Console.ReadLine().Split(","c))
Console.Write("Enter the sum:")
'Option Strict would not allow a string to be dumped into an integer
Dim sum As Integer
Dim Pairs As New ArrayList
If Integer.TryParse(Console.ReadLine, sum) Then
'Your Function returns an array list but you
'throw it away by not setting up a variable to receive it
Pairs = getPairs(arraylist, sum)
Else
Console.WriteLine("Program aborted. Sum was not a number.")
End If
For Each item In Pairs
Console.WriteLine(item)
Next
Console.ReadKey()
End Sub
'Functions need a return data type in the declaration
Public Function getPairs(ByVal array As ArrayList, ByVal sum As Integer) As ArrayList
Dim comps, pairs As New ArrayList
For index = 0 To array.Count - 1
'I don't see how this can ever be true since comps is empty
If comps.Contains(array(index)) Then 'Since .Contains returns a Boolean, no = True is necessary
pairs.Add(array(index))
Else
'Ideally each item in array should be tested to see if it is a number
'You will get an exception if CInt fails
comps.Add(sum - CInt(array(index)))
'You never use the comps ArrayList
End If
Next
'The pairs ArrayList is empty
Return pairs
End Function
End Module
I don't see how this code could accomplish what you describe as your goal. I think you should start again. Talk through how you would accomplish your task. Then write it out on paper, not in code. Then you will see more clearly how to code your project.
The big problem is the original code is this line:
Dim comps, pairs As New ArrayList
That code creates two ArrayList reference variables, but only one ArrayList object. comps remains null/Nothing.
But beyond that, the ArrayList type has been dead since .Net 2.0 came out back in 2005... more than 10 years now. It only exists today for backwards compatibility with old code. Don't use it!
This is better practice, especially in conjunction with Option Strict and Option Infer:
Public Function getPairs(ByVal items As IEnumerable(Of Integer), ByVal sum As Integer) As IEnumerable(Of Integer)
Dim comps As New HashSet(Of Integer)()
Dim result As New List(Of Integer)()
For Each item As Integer In items
If Not comps.Add(item) Then
result.Add(sum - item)
End If
Next
Return result
End Function
Sub Main()
Console.Write("Enter your array: ")
Dim input As String = Console.ReadLine()
Dim list As List(Of Integer) = input.Split(",").Select(Function(item) CInt(item)).ToList()
Console.Write("Enter the sum: ")
Dim sum As Integer = CInt(Console.ReadLine())
Dim pairs = getPairs(list, sum).Select(Function(s) s.ToString())
Console.WriteLine("Pairs are: {0}", String.Join(", " pairs))
Console.ReadKey()
End Sub

Alternative to Random.Next method [duplicate]

This question already has answers here:
How to Choose Random number from given list of numbers in VB.Net
(4 answers)
Closed 4 years ago.
In visual basic, is there a method that will allow me to select a random number from a list of numbers, rather than selecting a random number between two numbers?
In other words, I want something like Random().Next(1, 3, 4, 6, 7) where it will select a random number from those numbers (so selecting 2 is not an option)
I've looked here, but can't seem to find anything.
Try the following:
Imports System
Public Module Module1
Private Function NextRandom(ParamArray numbers() As Integer) As Integer
Dim Result As Integer = Integer.MinValue
If numbers.Length > 0 Then
Dim rnd As New Random
Dim i As Integer = rnd.Next(0, numbers.Length)
Result = numbers(i)
End If
Return Result
End Function
Public Sub Main()
Console.writeline("Your next number is: {0}", NextRandom(1, 3, 5, 6, 7, 8, 9))
End Sub
End Module
More info and examples here
Working example of the code here
Hope this helps.
If you want each value in your list only once then just shuffle your list.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim lst As New List(Of Integer) From {1, 3, 4, 6, 7}
Dim shuffledList As List(Of Integer) = lst.OrderBy(Function(i) Rnd.Next()).ToList()
For Each i In shuffledList
Debug.Print(i.ToString)
Next
End Sub
You get a different order each time you click the button.
Define your list as an array:
Dim List = New Integer() {Your numbers here}
Then use the standard random.next to generate an index for the array:
Dim generator As New Random
Dim index As Integer
index = generator.Next(0, List.length)
Then define your random number as the number that corresponds to the index in the array:
Dim randNumber as Integer
randNumber = List(index)

Vb.Net 2D Dictionary - very slow

I need to create a 2D dictionary/keyvalue pair.
I tried something like this.
Dim TwoDimData As New Dictionary(Of String, Dictionary(Of String, String))
'Create an empty table
For Each aid In AIDList '(contains 15000 elements)
TwoDimData.Add(aid, New Dictionary(Of String, String))
For Each bid In BIDList 'contains 30 elements
TwoDimData.Item(aid).Add(bid, "")
Next
Next
'Later populate values.
[some code here to populate the table]
'Now access the value
'The idea is to access the info as given below (access by row name & col name)
Msgbox TwoDimData.Item("A004").Item("B005") ' should give the value of 2
Msgbox TwoDimData.Item("A008").Item("B002") ' should return empty string. No error
Issue:
The issue is in Creating the empty table. It takes 70 seconds to create the TwoDimData table with empty values. Everything else seems to be fine. Is there any way to improve the performance - may be instead of using Dictionary?
I suggest you try Dictionary(Of Tuple(Of String, String), String) instead. That is, the keys are pairs of strings (Tuple(Of String, String)) and the values are strings. That would appear to correspond nicely to the diagram in your question.
Dim matrix As New Dictionary(Of Tuple(Of String, String), String)
' Add a value to the matrix:
matrix.Add(Tuple.Create("A003", "B004"), "3")
' Retrieve a value from the matrix:
Dim valueAtA003B004 = matrix(Tuple.Create("A003", "B004"))
Of course you can define your own key type (representing a combination of two strings) if Tuple(Of String, String) seems too generic for your taste.
Alternatively, you could also just use (possibly jagged) 2D arrays, but that would potentially waste a lot of space if your data is sparse (i.e. if there are many empty cells in that 2D matrix); and you'd be forced to use numeric indices instead of strings.
P.S.: Actually, consider changing the dictionary value type from String to Integer; your example matrix suggests that it contains only integer numbers, so it might not make sense to store them as strings.
P.P.S.: Do not add values for the "empty" cells to the dictionary. That would be very wasteful. Instead, instead of simply retrieving a value from the dictionary, you check whether the dictionary contains the key:
Dim valueA As String = "" ' the default value
If matrix.TryGetValue(Tuple.Create("A007", "B002"), valueA) Then
' the given key was present, and the associated value has been retrieved
…
End If
I would think that a simple structure would suffice for this?
Public Structure My2DItem
Public Row As Integer
Public Col As Integer
Public Value As String
End Structure
Public My2DArray As Generic.List(Of My2DItem) = Nothing
Public Size As Integer
Public MaxRows As Integer
Public MaxCols As Integer
'
Sub Initialise2DArray()
'
Dim CountX As Integer
Dim CountY As Integer
Dim Item As My2DItem
'
'initialise
MaxRows = 15000
MaxCols = 30
Size = MaxRows * MaxCols
My2DArray = New Generic.List(Of My2DItem)
'
'Create an empty table
For CountY = 1 To 15000
For CountX = 1 To 30
Item = New My2DItem
Item.Row = CountY
Item.Col = CountX
Item.Value = "0"
My2DArray.Add(Item)
Item = Nothing
Next
Next
'
End Sub
And to read the data out of the array,
Function GetValue(Y As Integer, X As Integer) As String
'
Dim counter As Integer
'
GetValue = "Error!"
If My2DArray.Count > 0 Then
For counter = 0 To My2DArray.Count - 1
If My2DArray(counter).Row = Y Then
If My2DArray(counter).Col = X Then
GetValue = My2DArray(counter).Value
Exit Function
End If
End If
Next
End If
'
End Function
And to read your sample cell A004 B005
MyStringValue = GetValue(4,5)
I would suggest creating a class that has properties for the AID and BID and use this as the basis for the values you want to store
Public Class AIdBId
Public Property AId As Integer
Public Property BId As Integer
Public Sub New(aId As Integer, bId As Integer)
Me.AId = aid
Me.BId = bid
End Sub
End Class
Note that I have used integers for everything because it seems that is all you need and it is more efficient that using a string
Then you can add values where they are non-zero:
'define your dictionary
Dim valueMatrix As New Dictionary(Of AIdBId, Integer)
'add your values
valueMatrix.Add(New AIdBId(1, 1), 1)
valueMatrix.Add(New AIdBId(2, 3), 1)
valueMatrix.Add(New AIdBId(4, 3), 3)
valueMatrix.Add(New AIdBId(5, 8), 8)
'check if a value exixts
Dim valueExixsts As Boolean = valueMatrix.ContainsKey(New AIdBId(9, 9))
'get a value
Dim i As Integer = valueMatrix(New AIdBId(4, 3))
So you can now combine these two to return the value if there is one or zero if not:
Private Function GetValue(valuematrix As Dictionary(Of AIdBId, Integer), aId As Integer, bId As Integer) As Integer
Dim xRef As New AIdBId(aId, bId)
If valuematrix.ContainsKey(xRef) Then
Return valuematrix(xRef)
Else
Return 0
End If
End Function

Object reference not set to an instance of an object while assinging value to byte object

I am trying to read byte array and i want to store first value of array in list. I tried one small console application example but i got above issue. I searched a lot on internet but didnt get answer
Sub Main()
Dim lData As New List(Of Byte)
Dim lBuffer() As Byte = {5, 99, 4, 7}
Dim a() As Byte
For ReadValue As Integer = 0 To lBuffer.Length
a(0) = lBuffer(ReadValue)
Exit For
Next
lData.AddRange(a)
End Sub
Your problem here is that you have declared the array but you have not initialized it. By declaring a variable with Dim a() As Byte, you have said "This thing exists and here's what it looks like". However, you haven't actually provided anything that will occupy the definition of a(), or in other words you haven't instantiated it yet.
Think of it this way. I tell you that apples exist and that they look like oddly shaped red orbs, there are seeds inside of them, and they are edible. Now I'm going to tell you to take all of the seeds out of the apple and eat the apple. Picking seeds out of an apple is completely possible, and eating apples is also possible. One problem though, you can't do what I told you. Why? Because I never gave you an apple to do those things with.
To be more specific in regards to you question, You have created an array of bytes with Dim a() As Byte. That's the first step, you told it "Hey, this is going to be a collection of things and all of those things are going to be a Byte". The main problem with arrays in the way you used it is that arrays need to know how big they are when you use them. So you have two options, you can either a) declare the array with a size which will create an empty array where all elements are null or b) you can do it like you did and then assign an array that has already been defined to it.
I'll give you examples of both the methods:
Sub Main()
Dim lData As New List(Of Byte)
Dim lBuffer() As Byte = {5, 99, 4, 7}
' Tell the array how big to be in the first place
Dim a(lBuffer.Length - 1) As Byte
For ReadValue As Integer = 0 To lBuffer.Length - 1
a(ReadValue) = lBuffer(ReadValue)
Next
lData.AddRange(a)
End Sub
Sub Main()
Dim lData As New List(Of Byte)
Dim lBuffer() As Byte = {5, 99, 4, 7}
Dim a() As Byte
' Lets create a temporary list and then use the .ToArray function
' which will return an already instantiated array.
Dim byteList as new List(of Byte)
For ReadValue As Integer = 0 To lBuffer.Length - 1
' Add each item to the list
byteList.Add(lBuffer(ReadValue))
Next
' Now, convert the list to an array and set it to a()
a = byteList.ToArray
lData.AddRange(a)
End Sub
Either of those options should stop the NullReferenceException error you are receiving. However you already have a List(of Byte) defined and instantiated, so the easiest option would probably be just assigning the values to the list directly:
Sub Main()
Dim lData As New List(Of Byte)
Dim lBuffer() As Byte = {5, 99, 4, 7}
For ReadValue As Integer = 0 To lBuffer.Length - 1
lData.Add(lBuffer(ReadValue))
Next
End Sub
Or even shorter code would be to use a Lambda expression:
Sub Main()
Dim lData As New List(Of Byte)
Dim lBuffer() As Byte = {5, 99, 4, 7}
lBuffer.ToList.ForEach(Sub(x) lData.Add(x))
End Sub
However the shortest possible answer to achieve the same thing would be to use the .ToList extension directly:
Sub Main()
Dim lData As List(Of Byte)
Dim lBuffer() As Byte = {5, 99, 4, 7}
lData = lBuffer.ToList()
End Sub
After re-reading this, I thought of an even shorter version. You can cut out the middleman of the byte array completely and just instantiate the List(Of Byte) from your array list.
Sub Main()
Dim lData As New List(Of Byte) From {5, 99, 4, 7}
End Sub
And I believe that's the shortest possible way to do it. If someone can prove me wrong though, I'd love to learn.

Getting the index of the largest integer in an array

I have an array of integers and I need to know the index of the largest number (not the actual value, just the index of whichever is highest).
However, if one or more indexes "tie" for the highest value, I need to have all of the indexes that share that high value.
I assume this function would need to return an array (since it could be one or more indexes), but I am not totally sure how to go about getting the more efficient solution.
If this is going to be a common thing you could write your own Extension. You should add some additional sanity/null checking but this will get you started:
Module Extensions
<System.Runtime.CompilerServices.Extension()> Function FindAllIndexes(Of T)(ByVal array() As T, ByVal match As Predicate(Of T)) As Integer()
''//Our return object
Dim Ret As New List(Of Integer)
''//Current array index
Dim I As Integer = -1
''//Infinite loop, break out when we no more matches are found
Do While True
''//Loop for a match based on the last index found, add 1 so we dont keep returning the same value
I = System.Array.FindIndex(array, I + 1, match)
''//If we found something
If I >= 0 Then
''//Append to return object
Ret.Add(I)
Else
''//Otherwise break out of loop
Exit Do
End If
Loop
''//Return our array
Return Ret.ToArray()
End Function
End Module
Then to call it:
Dim ints As Integer() = New Integer() {1, 2, 8, 6, 8, 1, 4}
Dim S = ints.FindAllIndexes(Function(c) c = ints.Max())
''//S now holds 2 and 4
If you are using .NET 3.5, you can use the Max() Extension function to easily find the highest value, and use Where to locate the matching records in your source array.
IList has an IndexOf member, which helps. This code is completely untested, and probably has at least one off-by-one error.
Public Function GetPostionsOfMaxValue(ByVal input() As Integer) As Integer()
Dim ints = New List(Of Integer)(input)
Dim maxval = ints.Max
Dim indexes As New List(Of Integer)
Dim searchStart As Integer = 0
Do Until searchStart >= ints.Count
Dim index = ints.IndexOf(maxval, searchStart)
If index = -1 Then Exit Do
indexes.Add(index)
searchStart = index + 1
Loop
Return indexes.ToArray
End Function