sorting an array of integers vb console - arrays

I've written this code to sort an array of 5 numbers in ascending order but I've got an error:
A first chance exception of type 'System.IndexOutOfRangeException' occurred in ConsoleApplication1.exe
Here is the code:
Module Module1
Dim numbers(5) As Integer
Dim flag As Boolean
Dim i As Integer = 0
Sub InputNumbers()
For i = 0 To 4
Console.WriteLine("Input Numbers ")
numbers(i) = Console.ReadLine()
Next i
End Sub
Sub Sort()
Dim temp As Integer
Do
flag = False
For i = 0 To 4
If numbers(i) > numbers(i + 1) Then
temp = numbers(i + 1)
numbers(i + 1) = numbers(i)
numbers(i) = temp
End If
Next i
Loop Until flag = True
End Sub
Sub Output()
For i = 0 To 4
Console.WriteLine("The result is : " & numbers(i))
Next i
End Sub
Sub Main()
InputNumbers()
Sort()
Output()
Console.ReadKey()
End Sub
The error is found here:
For i = 0 To 4
If numbers(i) > numbers(i + 1) Then
temp = numbers(i + 1)
numbers(i + 1) = numbers(i)
numbers(i) = temp
End If
Next i
Can someone please help?

Your array actually contains 6 elements.
'5 represents the upper bond (0 to 5)
Dim numbers(5) as integer
'Declare a single-dimension array of 5 values
Dim numbers(4) As Integer
Then, your statement below is wrong
For i = 0 To 4
If numbers(i) > numbers(i + 1) Then
temp = numbers(i + 1)
numbers(i + 1) = numbers(i)
numbers(i) = temp
End If
Next i
In general, to avoid breaking your code if you ever change the array size, I would use GetUpperBound to get the last index of your array. Furthermore, you cannot make a for to loop up to the last element since in your loop, you look at index: i+1 which will give you an index out of range exception on the last element (That is why I added the "-1" after GetUpperbound.
For i = 0 To numbers.GetUpperBound(0) -1
If numbers(i) > numbers(i + 1) Then
temp = numbers(i + 1)
numbers(i + 1) = numbers(i)
numbers(i) = temp
End If
Next
Ultimately however, the most efficient way to sort your array without hassle is to do this.
Array.Sort(numbers)
For your output function, I would use either GetUpperbound instead of 4 (which will be problematic if the array size is either changed and you forget to change the number) or a For each statement that will adapt itself to any array size without changing that part of the code.
Sub Output()
For each i as integer in numbers
Console.WriteLine("The result is : " & numbers(i))
Next
End Sub

As Sage Pourpre pointed out, where you are using i + 1 to reference the next element in the array, you have to make sure that i + 1 is not greater than the last index of the array. There is more than one way to do that.
Also, with the code in your question, you will have an unending loop because you haven't set the value of flag appropriately. I suggest naming that variable isSorted because it is more meaningful:
Sub Sort()
' perform a bubble sort
Dim temp As Integer
Dim isSorted As Boolean
Do
isSorted = True
For i = 0 To numbers.Length - 2
If numbers(i) > numbers(i + 1) Then
temp = numbers(i + 1)
numbers(i + 1) = numbers(i)
numbers(i) = temp
isSorted = False
End If
Next i
Loop Until isSorted
End Sub

Related

How to exctract first and last numbers in a sequence

i foung similiar topic, but i can t apply completily that solution to my needs... I want to upgrade excel workbook at my job by making it more auto-entry capable.
Mostly i use excel functions, but sometimes i need some VBA coding, which im not very familiar with. So my problem is, that i need something like this mentioned on this thread. How to get the first and last numbers in a sequence
I have box numbers in different sequince in ascening order starting from "A4" to X on
Sheet1. Example Box numbers: M004935149,M004935150,M004935151,M004935202,M004935203,M004935204,M004935205, is it possible when i copy&paste(values) to sheet2 from "A4" to X (depenting on number of boxes copied) to make a string, sentence or whatever is called in specific form in some other cells. M004935149-151 // M004935202-205. I used code from topic in link above, it can make half job done but i can t figure it out how to make entry from desired cell range and display them on worksheet, and to display values in desired format. Link of screen shoots from my example is following:
I hope that someone can help. Thanks in advance.
Check this
Option Explicit
Sub test2()
Dim ws As Worksheet
Dim arr() As String, result As String, letter As String, cellValue As String, tempLastElement As String
Dim lastColumn As Long, counter As Long
Dim firstColumn As Integer, targetRow As Integer, i As Integer
Set ws = Worksheets("Sheet1")
firstColumn = 1 'number of first column with target data
targetRow = 1 'number of row with target data
lastColumn = ws.Range(ws.Cells(targetRow, firstColumn), ws.Cells(targetRow, Columns.Count).End(xlToLeft).Columns).Count
ReDim arr(1 To lastColumn - firstColumn + 1)
letter = Left(ws.Cells(targetRow, firstColumn).Value, 1) 'if count of character in start of string will be more 1, replace 1 on to count of characters
For i = 1 To UBound(arr)
cellValue = ws.Cells(targetRow, i).Value
arr(i) = Right(cellValue, Len(cellValue) - 1) 'if count of character in start of string will be more 1, replace 1 on to count of characters
Next i
ReDim sequenceArr(1 To UBound(arr))
sequenceArr(1) = arr(1)
counter = 2
For i = 1 To UBound(arr) - 1
If CLng(arr(i)) + 1 = CLng(arr(i + 1)) Then
tempLastElement = arr(i + 1)
sequenceArr(counter) = tempLastElement
Else
counter = counter + 1
sequenceArr(counter) = arr(i + 1)
counter = counter + 1
End If
Next
ReDim Preserve sequenceArr(1 To counter)
result = ""
counter = 1
For i = 1 To UBound(sequenceArr) - 1
If counter > UBound(sequenceArr) Then Exit For
If result = "" Then
result = letter & sequenceArr(counter) & "-" & Right(sequenceArr(counter + 1), 3)
counter = counter + 2
Else
result = result & "//" & letter & sequenceArr(counter) & "-" & Right(sequenceArr(counter + 1), 3)
counter = counter + 2
End If
Next
ws.Range("D4").Value = result
End Sub
Result on

vba - loop inside a loop freezes excel

i am trying to make a loop to go through an array(47193, 4) and an array 2 named attack(41892,1). The idea here is that the attack array has the values in order from the sheet i want to later on add the values to the next column, this is why i add the values to a third array. So the loop is going to go one by one the value from attack array while looping through arr array to find the common data. i tried copying the values directly to the sheet but excel freezes a lot. Now with this way, excel still freezes at this point. Is there anything wrong with it?
Dim arr3() As Variant
Dim dee As Long
ReDim arr3(UBound(attacks, 1), 1)
For k = 0 To UBound(attacks, 1)
j = 0
For j = 0 To UBound(arr, 1)
If attacks(k, 0) = arr(j, 0) And attacks(k, 1) = arr(j, 2) Then
arr3(dee, 0) = attacks(k, 0)
arr3(dee, 1) = attacks(k, 1)
de = dee + 1
End If
Next j
Next k
Here's some code showing how to use a Dictionary:
Sub Tester()
Const SZ As Long = 10000 'size of test arrays
Dim arr1(1 To SZ, 1 To 2)
Dim arr2(1 To SZ, 1 To 2)
Dim arr3(1 To SZ, 1 To 2) '<<matches go here
Dim n As Long, m As Long, i As Long, t, dict, k
t = Timer
'fill test arrays with random data
For n = 1 To SZ
arr1(n, 1) = CLng(Rnd * 200)
arr1(n, 2) = CLng(Rnd * 200)
arr2(n, 1) = CLng(Rnd * 200)
arr2(n, 2) = CLng(Rnd * 200)
Next n
Debug.Print "Filled test arrays", Timer - t
t = Timer
'test the nested loop approach
For n = 1 To SZ
For m = 1 To SZ
If arr1(n, 1) = arr2(m, 1) And arr1(n, 2) = arr2(m, 2) Then
i = i + 1
arr3(i, 1) = arr1(n, 1)
arr3(i, 2) = arr1(n, 2)
End If
Next m
Next n
Debug.Print "Finished nested loop", Timer - t, i & " matches"
t = Timer
'create a lookup using a dictionary
Set dict = CreateObject("scripting.dictionary")
For n = 1 To SZ
k = arr1(n, 1) & "|" & arr1(n, 2)
dict(k) = dict(k) + 1
Next n
Debug.Print "Filled dictionary", Timer - t
t = Timer
i = 0
Erase arr3
'Perform the match against arr2 using the dictionary
For m = 1 To SZ
k = arr2(m, 1) & "|" & arr2(m, 2)
If dict.exists(k) Then
i = i + 1
arr3(i, 1) = arr2(m, 1)
arr3(i, 2) = arr2(m, 2)
End If
Next m
Debug.Print "Finished dictionary loop", Timer - t, i & " matches"
End Sub
Output:
Filled test arrays 0
Finished nested loop 9.101563 2452 matches
Filled dictionary 0.03125
Finished dictionary loop 0.0078125 2177 matches
Note the # of matches is slightly different - the nested loop catches duplicate matches but the Dictionary only counts unique matches. You might need to make adjustments depending on your use case.

Excel VBA - Determining Column or Row Target of Array UDF

I have a simple excel UDF for converting an array of mass values to mol fractions. Most times, the output will be a column array (n rows by 1 column).
How, from within the VBA environment, do I determine the dimensions of the target cells on the worksheet to ensure that it should be returned as n rows by 1 column versus n columns by 1 row?
Function molPct(chemsAndMassPctsRng As Range)
Dim chemsRng As Range
Dim massPctsRng As Range
Dim molarMasses()
Dim molPcts()
Set chemsRng = chemsAndMassPctsRng.Columns(1)
Set massPctsRng = chemsAndMassPctsRng.Columns(2)
chems = oneDimArrayZeroBasedFromRange(chemsRng)
massPcts = oneDimArrayZeroBasedFromRange(massPctsRng)
'oneDimArrayZeroBasedFromRange is a UDF to return a zero-based array from a range.
ReDim molarMasses(UBound(chems))
ReDim molPcts(UBound(chems))
totMolarMass = 0
For chemNo = LBound(chems) To UBound(chems)
molarMasses(chemNo) = massPcts(chemNo) / mw(chems(chemNo))
totMolarMass = totMolarMass + molarMasses(chemNo)
Next chemNo
For chemNo = LBound(chems) To UBound(chems)
molPcts(chemNo) = Round(molarMasses(chemNo) / totMolarMass, 2)
Next chemNo
molPct = Application.WorksheetFunction.Transpose(molPcts)
End Function
I understand that, if nothing else, I could have an input parameter to flag if return should be as a row array. I'm hoping to not go that route.
Here is a small example of a UDF() that:
accepts a variable number of input ranges
extracts the unique values in those ranges
creates a suitable output array (column,row, or block)
dumps the unique values to the area
Public Function ExtractUniques(ParamArray Rng()) As Variant
Dim i As Long, r As Range, c As Collection, OutPut
Dim rr As Range, k As Long, j As Long
Set c = New Collection
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' First grab all the data and make a Collection of uniques
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
For i = LBound(Rng) To UBound(Rng)
Set r = Rng(i)
For Each rr In r
c.Add rr.Value, CStr(rr.Value)
Next rr
Next i
On Error GoTo 0
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' next create an output array the same size and shape
' as the worksheet output area
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
k = 1
With Application.Caller
ReDim OutPut(1 To .Rows.Count, 1 To .Columns.Count)
End With
For i = LBound(OutPut, 1) To UBound(OutPut, 1)
For j = LBound(OutPut, 2) To UBound(OutPut, 2)
If k < c.Count + 1 Then
OutPut(i, j) = c.Item(k)
k = k + 1
Else
OutPut(i, j) = ""
End If
Next j
Next i
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' put the data on the sheet
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ExtractUniques = OutPut
End Function
You should return two dimensional arrays: n × 1 for row and 1 × n for column vectors.
So you need either
Redim molPcts(1, Ubound(chems) + 1)
or
Redim molPcts(Ubound(chems) + 1, 1)
To refer to them, you need to use both indices:
molPcts(1, chemNo + 1)
or
molPcts(chemNo + 1, 1)
If you prefer 0-based arrays, the redim should be like this:
Redim molPcts(0 To 0, 0 To Ubound(chems))
Redim molPcts(0 To Ubound(chems), 0 To 0)

Find repeating numbers in array vba

I am trying to create an array of size 50 in vba of random numbers between 0 and 20 then extracting the numbers that repeat in that array into another array.
Sub Problem10()
Dim numbers() As Double, odd() As Double, even() As Double, five() As Double, repeating() As Double, x As Integer, i As Integer, sOdd As Integer, sEven As Integer, sFive As Integer, sNumbers As Integer, sRepeating As Integer, y As Integer, listed As Boolean
sRepeating = 0
sNumbers = 50
For i = 1 To sNumbers
ReDim Preserve numbers(i)
numbers(i) = Int(20 * rnd)
'find repeating numbers
For x = 1 To i
If numbers(x) = numbers(i) And i <> x Then
'check if there are any repeated numbers already
If sRepeating > 0 And listed = False Then
For y = 1 To sRepeating
'check if the number is already listed as a repeated number
If numbers(i) = repeating(y) Then
listed = True
Else
sRepeating = sRepeating + 1
ReDim Preserve repeating(sRepeating)
repeating(sRepeating) = numbers(i)
Cells(sRepeating + 1, 4).Value = repeating(sRepeating)
listed = True
End If
Next y
End If
End If
Next x
Cells(i + 1, 5).Value = numbers(i)
Next i
End Sub
I am able to create the array with random numbers (numbers()) and then extract new arrays with even, odd numbers and multiples of 5. However, I dont know how to extract only repeating numbers.
Right now, this only finds the first repeating number and nothing else.
Consider the below example:
Option Explicit
Sub Test()
Dim i
Dim numbers(0 To 49)
Dim repeating()
Dim q
' Fill array with random numers
Randomize
For i = 0 To 49
numbers(i) = Int(20 * Rnd)
Next
' Filter repeating elements
With CreateObject("Scripting.Dictionary")
' Count each number qty
For i = 0 To 49
.Item(numbers(i)) = .Item(numbers(i)) + 1
Next
' Remove non-repeating
For Each q In .Keys()
If .Item(q) = 1 Then .Remove q
Next
' Retrieve array
repeating = .Keys()
End With
Debug.Print Join(numbers)
Debug.Print Join(repeating)
End Sub

A loop for modifying and printing an array in VBA

So, I'm trying to print modified iterations of an array of 100; for the first row I want 1 to 100 of the array, for the second 2 to 100, all the way to the 100th row with just array(100), and all of these rows starting with column A. I can print the first row just fine, but for the subsequent ones I'm not getting any output.
q = 1
For m = 1 To last_age
Sheets("Sheet1").Range(Cells(q, 1), Cells(q, UBound(Data) + 1)) = Data 'Works the first pass, but not for q>1
For p = 0 To UBound(Data) - 1
Data(p) = Data(p + 1)
Next p
If UBound(Data) > 0 Then
ReDim Data(0 To UBound(Data) - 1)
q = q + 1
End If
Next m
All my variables seem to be incrementing correctly, but after the first m loop my Data array isn't being put in the second row. Any thoughts?
Slightly different approach:
Sub Tester()
Dim data(100), i, last_age, sht As Worksheet, q, m
'fill test array
For i = 1 To 100: data(i - 1) = i: Next
Set sht = Sheets("Sheet1")
last_age = 100
q = 1
sht.Cells(q, 1).Resize(1, UBound(data) + 1) = data
For m = 2 To last_age
q = q + 1
sht.Cells(1, 1).Offset(0, m - 1).Resize( _
1, (UBound(data) + 1) - (m - 1)).Copy _
sht.Cells(q, 1)
Next m
End Sub
You are re-dimensioning your array in this line:
ReDim Data(0 To UBound(Data) - 1)
but you are not using the Preserve keyword so your data is getting deleted. Try using:
ReDim Preserve Data(0 To UBound(Data) - 1)

Resources