Generate number of arrays from one array - arrays

I am working on project in vb.net, I have an integer array for example (1,8,9,8), what I need is to generate number of arrays from it by add 1 to each item at a time so that the first array suppose to be (2,8,9,8) and the second (1,9,9,8) and so on, I tried this code:
Function myarray(ByVal arra1() As Integer, ByVal arran() As Integer, ByVal i As Integer) As Integer
For i = 0 To arra1.Length -
arran(i) = arra1(i)
arran(i) = arra1(i) + 1
Next
End Function

As much I understand from the question in current given context this may be your required solution:
Function myarray(ByVal arra1() As Integer, ByVal arran() As Integer, ByVal arrNumber As Integer) As Integer
arran=arra1.Clone()
For i As Integer = 0 To arra1.Length - 1
If i = (arrNumber - 1) Then ' IF arrNumber is 1 then +1 to index 0, If it is 2 then +1 to index 1
arran(i) = arra1(i) + 1
Else
arran(i) = arra1(i)
End If
Next
'Print the array
For i = 0 To arran.Length - 1
Console.Write(arran(i) & " ")
Next
Console.WriteLine()
Return 0
End Function
You can call it as
Dim a3() As Integer={1,8,9,8}
Dim a4() As Integer={}
myarray(a3,a4,1)
myarray(a3,a4,2)
myarray(a3,a4,3)
myarray(a3,a4,4)
When you call this function with arra1 as (1,8,9,8)
and arrNumber as 1 then arran will be (2,8,9,8),
with arrNumber as 2 then arran will be (1,9,9,8),
with arrNumber as 3 then arran will be (1,8,10,8) and
with arrNumber as 4 then arran will be (1,8,9,9)

First declare your initial array.
' declare initial array
Dim arr1 = {1, 8, 9, 8}
Then declare a jagged array for the result. Since the initial array has 4 elements, it will create a 4 arrays.
' declare a jagged array for the result
Dim result(arr1.Length - 1)() As Integer
Then you clone the initial array, and only change one of the value.
For i = 0 To arr1.Length - 1
' clone the initial array
result(i) = arr1.Clone()
' only change the element at index = i
result(i)(i) += 1
Next
Result is:
2 8 9 8
1 9 9 8
1 8 10 8
1 8 9 9
Full source:
Module Module1
Sub Main()
' declare initial array
Dim arr1 = {1, 8, 9, 8}
' declare a jagged array for the result
Dim result(arr1.Length - 1)() As Integer
For i = 0 To arr1.Length - 1
' clone the initial array
result(i) = arr1.Clone()
' only change the element at index = i
result(i)(i) += 1
Next
' print result
For i = 0 To result.Length - 1
For j = 0 To result(i).Length - 1
Console.Write(result(i)(j) & " ")
Next
Console.WriteLine()
Next
Console.ReadKey(True)
End Sub
End Module
In case you want to make a function.
Function CopyArray(ByVal source As Integer(), i As Integer) As Integer()
' clone the initial array
Dim temp = source.Clone()
' only change the element at index = i
temp(i) += 1
Return temp
End Function
You call the function inside the iteration.
For i = 0 To arr1.Length - 1
result(i) = CopyArray(arr1, i)
Next
Full source with function:
Module Module1
Sub Main()
' declare initial array
Dim arr1 = {1, 8, 9, 8}
' declare a jagged array for the result
Dim result(arr1.Length - 1)() As Integer
For i = 0 To arr1.Length - 1
result(i) = CopyArray(arr1, i)
Next
' print result
For i = 0 To result.Length - 1
For j = 0 To result(i).Length - 1
Console.Write(result(i)(j) & " ")
Next
Console.WriteLine()
Next
Console.ReadKey(True)
End Sub
Function CopyArray(ByVal source As Integer(), i As Integer) As Integer()
' clone the initial array
Dim temp = source.Clone()
' only change the element at index = i
temp(i) += 1
Return temp
End Function
End Module

Related

Combining Multiple Arrays in VBA

I am currently trying to combine 46 arrays in to a single array. I have scoured the internet, to no prevail and am hoping someone here can help. I did find the below page, but I need to be able to look through each element of the new array in a nested for loop, so using the method below doesn't quite get me to my end goal.
Excel vba - combine multiple arrays into one
Basically, I need to combine my set of 46 arrays in such a way that I can then loop through each element using a nested for loop. ie.
Set of arrays:
myArray1 = (1, 2, 3, 4)
myArray2 = (5, 6, 7)
myArray3 = (8, 9)
myArray4 = (10, 11, 12, 13, 14)
.
.
.
myArray46 = (101, 102, 103)
Combine them to form new array:
myNewArray = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14... 101, 102, 103)
Loop through in nested for loop to check each element against my main array:
For i = LBound(mainArray) to UBound(mainArray)
For j = LBound(myArray) to UBound(myArray)
If mainArray(i) = myArray(j) Then
'do something
End If
Next j
Next i
Any help and/ or guidance with this is greatly appreciated!
Since you write in your comments that your end goal is to create an array of unique elements, you might be best served using a dictionary, where you can test for uniqueness as you add each element to dictionary. Something like:
Option Explicit
Function uniqueArr(ParamArray myArr() As Variant) As Variant()
Dim dict As Object
Dim V As Variant, W As Variant
Dim I As Long
Set dict = CreateObject("Scripting.Dictionary")
For Each V In myArr 'loop through each myArr
For Each W In V 'loop through the contents of each myArr
If Not dict.exists(W) Then dict.Add W, W
Next W
Next V
uniqueArr = dict.keys
End Function
Sub tester()
Dim myArray1, myArray2, myArray3, myArray4, myArray5
myArray1 = Array(1, 2, 3, 4)
myArray2 = Array(5, 6, 7, 8)
myArray3 = Array(9, 10, 11, 12, 13, 14)
myArray4 = Array(15, 16)
myArray5 = Array(1, 3, 25, 100)
Dim mainArray
mainArray = uniqueArr(myArray1, myArray2, myArray3, myArray4, myArray5)
End Sub
If you run Tester, you will see mainArray contains:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
25
100
Using your data this is how to create one array out of many:
Public Sub TestMe()
Dim myA, myB, myC, myD, myE
myA = Array(1, 2, 3, 4)
myB = Array(5, 6, 7)
myC = Array(8, 9)
myD = Array(10, 11, 12, 13, 14)
myE = Array(101, 102, 103)
Dim myCombine As Variant
Dim myNew() As Variant
Dim myElement As Variant
Dim myArr As Variant
Dim cnt As Long
myCombine = Array(myA, myB, myC, myD, myE)
For Each myArr In myCombine
For Each myElement In myArr
ReDim Preserve myNew(cnt)
myNew(cnt) = myElement
cnt = cnt + 1
Next
Next
For cnt = LBound(myNew) To UBound(myNew)
Debug.Print myNew(cnt)
Next cnt
End Sub
The "building" of the new array is facilitated through ReDim Preserve, which keeps the old values in the array whenver the dimension of the array changes. And if you want to do something with these arrays, you may use 3 nested loops (a bit slow) and have some check:
Dim cnt2 As Long
For cnt = LBound(myNew) To UBound(myNew)
For cnt2 = LBound(myCombine) To UBound(myCombine)
For Each myElement In myCombine(cnt2)
If myElement = myNew(cnt) Then
Debug.Print myElement & vbTab & " from " & vbTab & cnt2
End If
Next myElement
Next cnt2
Next cnt
This is what you get on the immediate window:
1 from 0
2 from 0
3 from 0
4 from 0
5 from 1
6 from 1
7 from 1
8 from 2
9 from 2
10 from 3
11 from 3
12 from 3
13 from 3
14 from 3
101 from 4
102 from 4
103 from 4
Alternate 'brick-by-brick' approach.
Option Explicit
Sub combineArrays()
Dim myArray1 As Variant, myArray2 As Variant, myArray3 As Variant
Dim myArray4 As Variant, myArray46 As Variant
ReDim mainArray(0) As Variant
myArray1 = Array(1, 2, 3, 4)
myArray2 = Array(5, 6, 7)
myArray3 = Array(8, 9)
myArray4 = Array(10, 11, 12, 13, 14)
'...
myArray46 = Array(101, 102, 103)
mainArray = buildMainArray(myArray1, mainArray)
mainArray = buildMainArray(myArray2, mainArray)
mainArray = buildMainArray(myArray3, mainArray)
mainArray = buildMainArray(myArray4, mainArray)
mainArray = buildMainArray(myArray46, mainArray)
ReDim Preserve mainArray(UBound(mainArray) - 1)
Debug.Print Join(mainArray, ",")
End Sub
Function buildMainArray(arr As Variant, marr As Variant)
Dim i As Long
For i = LBound(arr) To UBound(arr)
marr(UBound(marr)) = arr(i)
ReDim Preserve marr(UBound(marr) + 1)
Next i
buildMainArray = marr
End Function
The issue with using Redim Preserve to combine arrays is it can be an expensive operation, since you're basically re-creating the array everytime it's called. Since you have 46 arrays you're combining, you may very well be waiting a while.
Instead, you can loop over the arrays to figure out the total number of elements you need, dimension out your master array, then loop over the arrays again to do the actual assignment/merging. Something like this:
' encapsulates code to determine length of an individual array
' note that because arrays can have different LBounds in VBA, we can't simply use
' Ubound to determine array length
Public Function GetArrayLength(anArray As Variant) As Integer
If Not IsArray(anArray) Then
GetArrayLength = -1
Else
GetArrayLength = UBound(anArray) - LBound(anArray) + 1
End If
End Function
Public Function CombineArrays(ParamArray arraysToMerge() As Variant) As Variant
' index for looping over the arraysToMerge array of arrays,
' and then each item in each array
Dim i As Integer, j As Integer
' variable to store where we are in the combined array
Dim combinedArrayIndex As Integer
' variable to hold the number of elements in the final combined array
Dim CombinedArrayLength As Integer
' we don't initialize the array with an array-length until later,
' when we know how long it needs to be.
Dim combinedArray() As Variant
' we have to loop over the arrays twice:
' First, to figure out the total number of elements in the combined array
' second, to actually assign the values
' otherwise, we'd be using Redim Preserve, which can get quite expensive
' because we're creating a new array everytime we use it.
CombinedArrayLength = 0
For i = LBound(arraysToMerge) To UBound(arraysToMerge)
CombinedArrayLength = CombinedArrayLength + GetArrayLength(arraysToMerge(i))
Next i
' now that we know how long the combined array has to be,
' we can properly initialize it.
' you can also use the commented code instead, if you prefer 1-based arrays.
ReDim combinedArray(0 To CombinedArrayLength - 1)
' Redim combinedArray(1 to CombinedArrayLength)
' now that the combinedarray is set up to store all the values in the arrays,
' we can begin actual assignment
combinedArrayIndex = LBound(combinedArray)
For i = LBound(arraysToMerge) To UBound(arraysToMerge)
For j = LBound(arraysToMerge(i)) To UBound(arraysToMerge(i))
combinedArray(combinedArrayIndex) = arraysToMerge(i)(j)
combinedArrayIndex = combinedArrayIndex + 1
Next j
Next i
' assign the function to the master array we've been using
CombineArrays = combinedArray
End Function
To use this function, you'd do something like the following:
Public Sub TestArrayMerge()
Dim myArray1() As Variant
Dim myArray2() As Variant
Dim myArray3() As Variant
Dim myArray4() As Variant
Dim combinedArray As Variant
myArray1 = Array(1, 2, 3, 4)
myArray2 = Array(5, 6, 7)
myArray3 = Array(8, 9)
myArray4 = Array(10, 11, 12, 13, 14)
combinedArray = CombineArrays(myArray1, myArray2, myArray3, myArray4)
If IsArray(combinedArray) Then
Debug.Print Join(combinedArray, ",")
End If
End Sub
Regarding your last bit, that you're using an inner loop to combine the values in your final combined array: Your inner loop doesn't need to start at LBound(myArray). For any value of i, you've already compared it to the elements before it (e.g., when i = 2, it's already been compared to the first element). So you really just need:
For i = LBound(combinedArray) To UBound(combinedArray) - 1
For j = i + 1 To UBound(combinedArray)
' do whatever you need
Next j
Next i
Perhaps this ...
'To determine if a multi-dimension array is allocated (or empty)
'Works for any-dimension arrays, even one-dimension arrays
Public Function isArrayAllocated(ByVal aArray As Variant) As Boolean
On Error Resume Next
isArrayAllocated = IsArray(aArray) And Not IsError(LBound(aArray, 1)) And LBound(aArray, 1) <= UBound(aArray, 1)
Err.Clear: On Error GoTo 0
End Function
'To determine the number of items within any-dimension array
'Returns 0 when array is empty, and -1 if there is an error
Public Function itemsInArray(ByVal aArray As Variant) As Long
Dim item As Variant, UBoundCount As Long
UBoundCount = -1
If IsArray(aArray) Then
UBoundCount = 0
If isArrayAllocated(aArray) Then
For Each item In aArray
UBoundCount = UBoundCount + 1
Next item
End If
End If
itemsInArray = UBoundCount
End Function
'To determine the number of dimensions of an array
'Returns -1 if there is an error
Public Function nbrDimensions(ByVal aArray As Variant) As Long
Dim x As Long, tmpVal As Long
If Not IsArray(aArray) Then
nbrDimensions = -1
Exit Function
End If
On Error GoTo finalDimension
For x = 1 To 65536 'Maximum number of dimensions (size limit) for an array that will work with worksheets under Excel VBA
tmpVal = LBound(aArray, x)
Next x
finalDimension:
nbrDimensions = x - 1
Err.Clear: On Error GoTo 0
End Function
'****************************************************************************************************
' To merge an indefinite number of one-dimension arrays together into a single one-dimension array
' Usage: mergeOneDimArrays(arr1, arr2, arr3, ...)
' Returns an empty array if there is an error
' Option Base 0
'****************************************************************************************************
Public Function mergeOneDimArrays(ParamArray infArrays() As Variant) As Variant
Dim x As Long, y As Long, UBoundCount As Long, newUBoundCount As Long
Dim tmpArr As Variant, allArraysOK As Boolean
UBoundCount = 0
allArraysOK = True
For x = LBound(infArrays) To UBound(infArrays)
If Not IsArray(infArrays(x)) Or Not nbrDimensions(infArrays(x)) = 1 Then
allArraysOK = False
Exit For
End If
UBoundCount = UBoundCount + itemsInArray(infArrays(x))
Next x
If allArraysOK Then
ReDim tmpArr(0 To UBoundCount - 1)
UBoundCount = 0
For x = LBound(infArrays) To UBound(infArrays)
For y = LBound(infArrays(x)) To UBound(infArrays(x))
tmpArr(UBoundCount) = infArrays(x)(y)
UBoundCount = UBoundCount + 1
Next y
Next x
newUBoundCount = itemsInArray(tmpArr)
If newUBoundCount = UBoundCount Then
mergeOneDimArrays = tmpArr
Else
mergeOneDimArrays = Array()
End If
Erase tmpArr
Else
mergeOneDimArrays = Array()
End If
End Function
If you are working with one-dimensional arrays you could use a collection instead. It is much better at handling dynamic sizing.
You can declare a collection and then add each of the elements in the arrays to it. Then you will have one large list with all of the values.
Dim coll As New Collection
coll.Add MyArray(j)
Here is a good to collections introduction:
https://excelmacromastery.com/excel-vba-collections/

sorting an array of integers vb console

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

Convert negative numbers from an array to positive numbers

I have written a VBscript to find out positive numbers and negative numbers in an array. I have managed to separate and print those numbers. I now want to convert the positive numbers to negative numbers.
This is the vbscript I have written:
Option explicit
Dim arr(), i, str, number,j,k,str1,p
number=inputbox("The number of elements the array should have")
ReDim arr(number-1)
arr(0)=1
arr(1)=2
arr(2)=-4
arr(3)=6
arr(4)=-8
For i=0 to number-1
If arr(i)>0 Then
j=i
str=str&vbnewline&arr(j)&vbnewline
msgbox ("The positive numbers from the array are " &str)
end if
next
For i=0 to number-1
If arr(i)<0 Then
k=i
str1=str1&vbnewline&arr(k)&vbnewline
msgbox ("The negative numbers from the array are " &str1)
End If
Next
'ReDim preserve arr(6)
'For i= 5 to 7
'p=arr(k)*(-1)
'Next
'msgbox p
The script executes successfully until the second for loop. I am trying to convert the negative numbers from the array to positive numbers(in this case -4, -8). When I execute the commented code, I get only second number "8". I have to show all numbers(positive and converted numbers) together. How can it be done?
You are doing right, but you are using last entry of negative number in your last commented for loop, hence you are missing -4. Try something like this:
Dim arr()
ReDim arr(5)
arr(0)=1
arr(1)=2
arr(2)=-4
arr(3)=6
arr(4)=-8
For i = 0 to UBound(arr) - 1
If arr(i) < 0 Then
Msgbox arr(i)
arr(i) = arr(i)*-1 '<-- This will convert all the negative to positive
End If
Next
For i = 0 to UBound(arr) - 1
Msgbox arr(i)
Next
That's how dynamic arrays would like to be treated in VBScript:
Option Explicit
' Decent way to initialize a dynamic array
ReDim a(4) ' 5 slots 0 .. 4; no "Dim a()" == spurious creation of an abomination
a(0) = 1
a(1) = 2
a(2) = -4
a(3) = 6
a(4) = -8
WScript.Echo "a = [" & Join(a) & "]"
Dim b : b = mapFO(a, New cSignSwitch)
WScript.Echo "b = [" & Join(b) & "]"
WScript.Echo "neg [" & Join(grepFF(a, GetRef("lessZero"))) & "]"
WScript.Echo "else [" & Join(grepFF(a, GetRef("geZero"))) & "]"
' Convenient way to initialize a dynamic array of Longs
Dim c : c = mapFF(Split("1 2 -4 6 -8"), GetRef("XCLng"))
WScript.Echo "c = [" & Join(c) & "]"
mapSE c, "a(i) * a(i)"
WScript.Echo "x*x [" & Join(c) & "]"
' return new array from mapping a's elements via o.map
Function mapFO(a, o)
Dim t : t = a ' array assignment copies!
mapSO t, o
mapFO = t
End Function
' return new array from mapping a's elements via f
Function mapFF(a, f)
Dim t : t = a ' array assignment copies!
mapSF t, f
mapFF = t
End Function
' apply o.map to a's elements
Sub mapSO(a, o)
Dim i
For i = 0 To UBound(a)
a(i) = o.map(a(i))
Next
End Sub
' apply f to a's elements
Sub mapSF(a, f)
Dim i
For i = 0 To UBound(a)
a(i) = f(a(i))
Next
End Sub
' eval ev for a's elements
Sub mapSE(a, ev)
Dim i
For i = 0 To UBound(a)
a(i) = Eval(ev)
Next
End Sub
Class cSignSwitch
Function map(e)
map = e * -1
End Function
End Class
' return new array of a's elements satisfying f
Function grepFF(a, f)
ReDim t(UBound(a)) ' result can't be larger than source
Dim j : j = -1 ' assume empty result
Dim i
For i = 0 To UBound(a)
If f(a(i)) Then
j = j + 1
t(j) = a(i)
End If
Next
ReDim Preserve t(j)
grepFF = t
End Function
Function lessZero(n)
lessZero = (n < 0)
End Function
Function geZero(n)
geZero = Not lessZero(n)
End Function
Function XCLng(x)
XCLng = CLng(x)
End Function
output:
cscript 32233658.vbs
a = [1 2 -4 6 -8]
b = [-1 -2 4 -6 8]
neg [-4 -8]
else [1 2 6]
c = [1 2 -4 6 -8]
x*x [1 4 16 36 64]

VBA - Get index of nth largest value in an array

I want to find the index of the nth largest value in an array. I can do the following but it runs into trouble when 2 values are equal.
fltArr(0)=31
fltArr(1)=15
fltArr(2)=31
fltArr(3)=52
For i = 0 To UBound(fltArr)
If fltArr(i) = Application.WorksheetFunction.Large(fltArr, n) Then
result = i
End If
Next
n=1 ---> 3
n=2 ---> 2 (but I want this to be 0)
n=3 ---> 2
n=4 ---> 1
Uses a second array to quickly get what you want without looping through each element for every value of n
Sub test()
Dim fltArr(0 To 3)
Dim X
Dim n As Long
Dim lngPos As Long
fltArr(0) = 31
fltArr(1) = 15
fltArr(2) = 31
fltArr(3) = 52
X = fltArr
For n = 1 To 4
lngPos = Application.WorksheetFunction.Match(Application.Large(X, n), X, 0) - 1
Debug.Print lngPos
X(lngPos) = Application.Max(X)
Next
End Sub
Edit:
Public Sub RunLarge()
Dim n%, i%, result%, count%
Dim fltArr(3) As Integer
Dim iLarge As Integer
fltArr(0) = 31:
fltArr(1) = 15:
fltArr(2) = 31:
fltArr(3) = 52
n = 1
Debug.Print " n", "iLarge", "result"
While n <= 4
count% = n - 1
iLarge = Application.WorksheetFunction.Large(fltArr, n)
For i = 0 To UBound(fltArr)
If fltArr(i) = iLarge Then
result = i
count% = count% - 1
If count% <= 0 Then Exit For
End If
Next
Debug.Print n, iLarge, result
n = n + 1
Wend
End Sub
result:
n iLarge result
1 52 3
2 31 0
3 31 2
4 15 1
It's a bit "dirty" but seeing as you're in Excel...
' Create a sheet with codename wsTemp...
For i = 0 To UBound(fltArr)
wsTemp.cells(i,1) = i
wsTemp.cells(i,2) = fltArr(i)
Next
with wsTemp
.range(.cells(1,1),.cells(i,2)).sort(wsTemp.cells(1,2),xlDescending)
end with
Result = wsTemp.cells(n,1)
Then you could also expand the sort to "sort by value then by index" if you wanted to control the "which of two equal 2nds should i choose" thing...
Perhaps this:
Public Sub RunLarge()
Dim fltArr() As Variant, X As Long
fltArr = Array(31, 15, 31, 52) 'Create the array
For X = 1 To 4 'Loop the number of large values you want to index
For i = LBound(fltArr) To UBound(fltArr) 'Loop the array
If fltArr(i) = Application.WorksheetFunction.Large(fltArr, 1) Then 'Find first instance of largest value
result = i
fltArr(i) = -9999 'Change the value in the array to -9999
Exit For
End If
Next
Debug.Print result
Next
End Sub
As it finds the first instance of the large number it replaces it with -9999 so on the next sweep it will pick the next instance of it.
Here's code for finding the nth largest item in collection. All you need to do is to write a function that would return it's index.
Sub testColl()
Dim tempColl As Collection
Set tempColl = New Collection
tempColl.Add 57
tempColl.Add 10
tempColl.Add 15
tempColl.Add 100
tempColl.Add 8
Debug.Print largestNumber(tempColl, 2) 'prints 57
End Sub
and the function itself, the easiest I could come up with.
Function largestNumber(inputColl As Collection, indexMax As Long)
Dim element As Variant
Dim result As Double
result = 0
Dim i As Long
Dim previousMax As Double
For i = 1 To indexMax
For Each element In inputColl
If i > 1 And element > result And element < previousMax Then
result = element
ElseIf i = 1 And element > result Then
result = element
End If
Next
previousMax = result
result = 0
Next
largestNumber = previousMax
End Function

bad number of element in dynamic array with for each loop

I don't understand why for each loop in vba doesn't return the good number of element when i use dynamic array.
For exemple, my array size is 4, and i have 5 iteration in for each loop ...
Public Sub test()
Dim t_direction() As String
Dim t_nextDirection() As String
Dim myDirection As Variant
Dim test As Integer
Var = 0
ReDim t_direction(4)
t_direction(0) = "N"
t_direction(1) = "S"
t_direction(2) = "E"
t_direction(3) = "W"
t_nextDirection = randomizeArray(t_direction)
For Each myDirection In t_nextDirection
Var = Var + 1
Next myDirection
MsgBox (UBound(t_nextDirection))
MsgBox (Var)
End Sub
Public Function randomizeArray(ByVal t_array As Variant) As String()
Dim i As Integer
Dim j As Integer
Dim tmp As String
Dim numItems As Integer
numItems = UBound(t_array) - 1
' Randomize the array.
For i = 0 To numItems
' Pick a random entry.
j = Rand(0, numItems)
' Swap the numbers.
tmp = t_array(i)
t_array(i) = t_array(j)
t_array(j) = tmp
Next i
'MsgBox (UBound(t_array))
randomizeArray = t_array
End Function
Public Function Rand(ByVal Low As Long, _
ByVal High As Long) As Integer
Rand = Int((High - Low + 1) * Rnd) + Low
End Function
At the moment you are creating a 5 element array with
ReDim t_direction(4)
as the first element occurs as t_direction(0)
You should either
create a 4 element array ReDim t_direction(3) (ie 0 to 3) and then use numItems consistent with that, or
create a 4 element array ReDim t_direction with a base of 1 (ie 1 to 4) and then use numItems consistent with that (ie numItems = UBound(t_array)). The Option Base 1 below forces the first element to be 1 (which is then ensured anyow by using ReDim t_direction(1 To 4)
The code below uses the later approach. It returns 4 and 4 rather than your current 4 and 5
Option Base 1
Public Sub test()
Dim t_direction() As String
Dim t_nextDirection() As String
Dim myDirection As Variant
Dim test As Integer
Var = 0
ReDim t_direction(1 To 4)
t_direction(1) = "N"
t_direction(2) = "S"
t_direction(3) = "E"
t_direction(4) = "W"
t_nextDirection = randomizeArray(t_direction)
For Each myDirection In t_nextDirection
Var = Var + 1
Next myDirection
MsgBox (UBound(t_nextDirection))
MsgBox (Var)
End Sub
Public Function randomizeArray(ByVal t_array As Variant) As String()
Dim i As Integer
Dim j As Integer
Dim tmp As String
Dim numItems As Integer
numItems = UBound(t_array)
' Randomize the array.
For i = 1 To numItems
' Pick a random entry.
j = Rand(1, numItems)
' Swap the numbers.
tmp = t_array(i)
t_array(i) = t_array(j)
t_array(j) = tmp
Next i
'MsgBox (UBound(t_array))
randomizeArray = t_array
End Function
Public Function Rand(ByVal Low As Long, _
ByVal High As Long) As Integer
Rand = Int((High - Low + 1) * Rnd) + Low
End Function
ReDim t_direction(4) actually declares t_direction as 0 To 4
Its better to be explicit:
ReDim t_direction(0 To 3)
In the absence of a specified lower bound (using the To clause), then the default lower bound is used.
This default can be set to 0 or 1 by using Option Base {0|1} at module level.
In the absence of Option Base then the default default is 0
Notes:
In VBA you are not limited to 0 or 1 as the lower bound, you can use any value you want.
To iterate over an array use
For i = LBound(arr) To UBound(arr)
To calculate the number of items in an array use
numItems = UBound(arr) - LBound(arr) + 1
This way you are not making any assumptions on what the lower bound is

Resources