Are my arrays incompatible? - arrays

I am trying to loop through an array that contains split strings, done via this line:
repHolder() = Split(rep, ",")
That's all fine and good, however, when I try to loop through this repHolder() array via a for loop, I am met each time with a subscript out of range error.
This makes no sense to me. When I step through the array it fails on the first element every time; this line:
If repHolder(j) = counter Then
I tried setting j to 0 and 1, both of which failed on the first sequence of the loop. This suggests to me because the array doesn't have a defined size; that I cannot loop through it this way, but that still makes little sense to me as it is still filled with elements.
Here is the entire code block of what I am trying to do:
Dim repHolder() As String
Dim strHolder() As String
Dim counter As Variant
Dim j As Integer
For Each rep In repNames()
repHolder() = Split(rep, ",")
Next rep
For Each rangeCell In repComboRange
k = 1
Do
If rangeCell.Value = repCombos(k) Then 'At this point, if rangecell = repcombos(k)
Range(rangeCell, rangeCell.End(xlToRight)).Copy
strHolder() = Split(rangeCell.Value, "/")
For Each counter In strHolder()
Stop
For j = 1 To 17
If repHolder(j) = counter Then

You are looping through repNames() and setting this new array via split (over and over again for each repName element...)
For Each rep In repNames()
repHolder() = Split(rep, ",")
Next rep
Every iteration of this loop resets repHolder() to a split of the rep element dropping whatever values were just set in that array in the previous iteration. So once it's done only the last element of RepNames() has been split into the repHolder() array.
For instance, if RepNames() looks like:
Element 0: "james,linda,mahesh,bob"
Element 1: "rajesh,sam,barb,carrie"
Element 2: ""
Then after all this iterating your repHolder array is going to be empty because there is nothing in the final element.
Stick a breakpoint (F9) on you For Each rangeCell In repComboRange line and look at your Locals pane in VBE. Check out the values that are stored in your repHolder() array at that point in time. I suspect there will be nothing in there.
The other oddball here is that you are looping 1 through 17. repHolder() will be a 0-based array so that should be 0 through 16. But... even that is nonsense since this really only makes sense as a For Each loop (or to use the uBound(repHolder) to determine how many times to loop:
For Each counter In strHolder()
Stop
For each repHolderElem in repHolder
If repHolderElem = counter Then
....
Next repHolderElem

Related

Is there a way to transfer all values from one array to another, then erase the original array?

I'm running into a problem with a block of code I'm trying to develop at my job. Essentially, I'm creating a userform in excel where folks will enter data for railcars as they get loaded at a certain location (we'll call these "spot 1, spot 2, spot 3, etc.").
Sometimes they'll have to move that car to a different spot, in which case I want them to be able to keep all the information on the railcar from the first/original entry, and then erase the data from the original spot once that's done.
To accomplish this in a more streamlined fashion, I've established arrays for each of the 5 spots that reference all the cells they're entering data into on the userform:
Dim spot1information(14)
spot1information(0) = UserForm.ProductType1.Value
spot1information(1) = UserForm.ProductID1.Value
spot1information(2) = UserForm.BatchID1.Value
etc....
Dim spot2information(14)
spot2information(0) = UserForm.ProductType2.Value
spot2information(1) = UserForm.ProductID2.Value
spot2information(2) = UserForm.BatchID2.Value
etc....
And so forth for all five spots. I don't know if this makes things more difficult, but note that these array values aren't all of the same type. For instance, index (0) will be a string, but index (10) is a DATETIME and index (12) is defined as Long.
So say that they are moving a car from spot 1 to spot 2. In short, I want the code to do the following:
Replace the values of indices 0 - 6 in spot2information (which is currently empty) with the values of indices 0 - 6 in spot1information (which the user has filled on the userform).
I'm only interested in carrying over indices 0-6 because they contain the pertinent railcar information
Empty every value of spot1information to ""
To accomplish this, I tried the following code and a few variations thereof:
If OriginalSpot.Value = 1 Then
If DestinationSpot.Value = 2 Then
For i = 0 to 6
spot2information(i) = spot1information(i)
Next
For Each i in spot1information
spot1information(i) = ""
Next
End If
End If
However, this keeps coming up with a type mismatch. I figure because the data in the spot2information array is empty, and the data in the spot1information array is not, but I'm not entirely sure of a way around this.
Update: I did what was suggested below and replaced: spot1information(i) = "" with Erase spot1information
The code now essentially works! The values of array "spot2information" are now the former values of "spot1information", with "spot1information" now empty.
The 2D array suggested below also works like a charm. New problem I've been facing is that array values are updating, but the userform isn't. (note: in the future I'll be posting this type of thing as a separate question, my apologies!)
Easier to manage this as a 2D array:
Sub Tester()
Dim spots(1 To 5, 0 To 14), n As Long, i As Long
'fill your spot arrays from the form....
For n = 1 To 5
spots(n, 0) = UserForm.Controls("ProductType" & n).Value
spots(n, 1) = UserForm.Controls("ProductID" & n).Value
spots(n, 2) = UserForm.Controls("BatchID" & n).Value
'etc etc
Next n
'swap a spot with another
Debug.Print spots(2, 1), spots(3, 1)
SwapSpots spots:=spots, fromSpot:=2, toSpot:=3
Debug.Print spots(2, 1), spots(3, 1)
End Sub
Sub SwapSpots(spots, fromSpot As Long, toSpot As Long)
Dim n As Long
For n = 0 To 6
spots(toSpot, n) = spots(fromSpot, n)
spots(fromSpot, n) = Empty 'empty the source slot value
Next n
End Sub
Assuming the DataType of the arrays is the same by Index i.e. index(0) is string for all spots, Index(2) is long for all spots, and so on.
If that is the case then this part should not produce any error:
For i = 0 to 6
spot2information(i) = spot1information(i)
Next
The error should be happening in this part more precisely in the line marked with #
For Each i in spot1information
spot1information(i) = "" '#
Next
and the reason for the error it seems to be that trying to assign a string value "" to a numeric type, given the "mismatch" error.
Using For Each i in spot1information indicates that you want to "Initiate" or Erase the entire array, therefore I suggest to use this line instead of the For…Next method.
Erase spot1information
In regards this:
But I've now run into a new problem, where the values on the userform haven't updated to reflect the new values stored in the array. Do I need to somehow "refresh" the userform?
You just updated the arrays, then you need to run the procedures used to update the values of the objects affected by both arrays in the UserForm.

LotusScript ans Two dimensional Array & subscription out or range error

Hello I have two dimensional array as below in LotusScript.
Counter = 0
While Not (ProcessingViewDoc Is Nothing )
Redim Preserve AllRecrods(Counter,0)
AllRecrods(Counter,0) = ProcessingViewDoc.Test1(0)
Redim Preserve AllRecrods(Counter,1)
AllRecrods(Counter,1) = ProcessingViewDoc.Test2(0)
Redim Preserve AllRecrods(Counter,2)
Set ProcessingViewDoc = ProcessingView.GetNextDocument(ProcessingViewDoc)
Counter = Counter +1
Wend
When It processes next document it does and reaches to counter 1 and second document it gives me error subscription out of range.
Here is global declaration of array.
Dim AllRecrods() As Variant
Here is the line when it gives error when it goes to loop second time.
Redim Preserve AllRecrods(Counter,0)
In addition to Richard's excellent answer, I would suggest a couple of things.
1) Instead of While Not (ProcessingViewDoc Is Nothing) (which contains two negatives, making it harder to read), use Do Until doc Is Nothing. It is much clearer.
2) If you use a list, you don't have to worry about redim of the array. You could make it a list of a custom data type, and if you use the UNID of the document as the key, you can quickly connect the values back to the originating document.
My code would look something like this:
--- Declarations ---
Type recordData
value1 As String
value2 As String
End Type
--- Main Code ---
Dim allRecords List As recordData
Dim unid as String
Do Until ProcessingViewDoc Is Nothing
unid = ProcessingViewDoc.UniqueID
allRecords(unid).value1 = ProcessingViewDoc.Test1(0)
allRecords(unid).value2 = ProcessingViewDoc.Test2(0)
Set ProcessingViewDoc = ProcessingView.GetNextDocument(ProcessingViewDoc)
Loop
You are using ReDim with the Preserve option and changing both of the dimensions. You can't do that.
From the documentation for the ReDim statement:
If Preserve is specified, you can change only the upper bound of the
last array dimension. Attempting to change any other bound results in
an error.
Also, the logic there is screwed up. You're doing three redims on every iteration, with the first one shrinking the second dimension back to zero on every iteration. Even if you weren't changing the first dimension, that would lose the data that you stored in AllRecrods( n ,1) because the preserve option can't keep data in a dimension that you shrink below the size that you've already used!
You should probably consider swapping your two dimensions, reversing them in your assignments, keeping the first dimension constant at 2, and eliminating two of your ReDim Preserve statements. I.e., just do one ReDim Preserve AllRecrods(2,counter) on each iteration of the loop.

VBA nested for loop won't Run

Hi I'm working on a macro in VBA for excel. I have a nested for loop in my code as seen below. The second loop does not run; even a MsgBox() command within the second loop doesn't result in any action and the program just seems to skip over the nested loop with no reported errors.
In plain English this nested loop should:
1) Take the string from the i entry in the array categories_string (first for loop).
2) iterate through the 300+ rows in column "AE" in the excel file (second for loop, "length" is the length of the column of data in "AE")
3) look for string matches and add 1 to the corresponding entry in the categories_value array which is populated with variables set to 0 (the if statement).
For i = LBound(categories_string) To UBound(categories_string)
For p = 1 To p = length
If Worksheets("sheet1").Cells(p + 2, "AE").Value = categories_string(i) Then
categories_value(i) = categories_value(i) + 1
End If
Next
Next
Change
For p = 1 To p = length
to
For p = 1 To length
Should fix you right up.
You may want to consider Scott Cranner's comment as well, especially if your categories_string is large and length is large. Or, just do it for the practice

“VBA Loop Until - Array” I want to get out as soon as the array is filled

I’m struggling with arrays.
Well, I’m trying to finish a loop when the array is assigned with values.
So, I tried a number of methods including this one;
Sub Testing()
Dim Pair1() As Variant
Dim pair11 As String
Dim NofDays As Long
pair11 = "ABC"
NofDays = Range("A2", Range("a2").End(xlDown)).Count
ReDim Pair1(1 To NofDays)
Do
If Selection = pair11 Then
Pair1 = Range(Selection.Offset(1, 0), Selection.Offset(1, 0).End(xlDown))
End If
If IsEmpty(Pair1) = False Then Exit Do
Selection.Offset(0, 1).Select
Loop Until Selection.Value = ""
End Sub
Pair1 is a dynamic array, and the size is already known while no value has been assigned to the array.
What is really annoying is this part
If IsEmpty(Pair1) = False Then Exit Do
And I put this IsEmpty function to get out of the loop, to avoid unnecessary loops.
BUT! Although the array Pair1 is not filled at all (even one value), this exits the loop.
I think there’s something that I missed out..
Anyone can help me?
thank you in advance!
Why is IsEmpty(Pair1) = False not working?
IsEmpty doesn't test whether an array is allocated (i.e., the array is populated with at least one value(s)). It tests the allocation of numeric data, i.e.,:
Dim i as Integer
Debug.Print IsEmpty(i) 'Should say "True"
i = 6
Debug.Print IsEmpty(i) 'Should now say "False"
So while the array is not Empty in this sense, that doesn't mean what you think it means :)
There are at least two ways I think of to handle this for you.
Option One Just put the Exit Do statement inside the If/Then block that allocates a value to Pair1, because at this point you know with 100% certainty that the array has been allocated a value.
If Selection = pair11 Then
Pair1 = Range(Selection.Offset(1, 0), Selection.Offset(1, 0).End(xlDown))
Exit Do
End If
Option Two Use it as an additional constraing on the Loop logic. Use the UBound function to determine the upper bound of the array. An initialized but unpopulated array with unspecified dimension should ordinarily have a UBound of -1. Once you assign values to it, the Ubound will represent the last index of the array items, so any value greater than or equal to zero will mean that there are values in the array.
Do
If Selection = pair11 Then
Pair1 = Range(Selection.Offset(1, 0), Selection.Offset(1, 0).End(xlDown))
End If
Selection.Offset(0, 1).Select
Loop While Not Selection.Value = "" OR Ubound(Pair1) >= 0
I would personally probably use Option 1 because to me it is easier to understand and I think it flows better than nested logic in the loop controller. But YMMV and either method I think should work.

Largest word in an Array

Hey i'm having problems creating a simple button for a programme which finds the largest word in an array and puts it into a textbox. I've done most of the coding (I hope) was wondering if somebody could help me actually with the code that finds the largest text in the array as I am struggling the most with that.
Private Sub btnLongName_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLongName.Click
Dim LongName As String
Dim LengthOfLongestName As Integer
Dim UltimateName As String
For i As Integer = 0 To 5
LongName = Members(i).Name
LengthOfLongestName = Len(LongName)
If Members(i).Name.Length > LengthOfLongestName Then
End If
Next i
txtResult.Text = "The longest name is " & UltimateName & " ."
End Sub
End Class
Thanks for your time - its for college homework, struggling big time on it :(
edit: I've edited the code
Since this is homework, I won't write the code for you; instead I'll try to give you some hints that will point you in the right direction.
Declare a variable of an appropriate type to hold the <longest value so far>, initialize it with the "shortest" value for that type.
Loop through all the values in the array (perhaps with a For or For Each loop)
Pseudo-code for the inside your loop:
If the Length of <the value being checked> exceeds _
the Length of the <longest value so far> Then
Assign <the value being checked> to the <longest value so far>
End If
When the loop finishes, the <longest value so far> will be the longest value in the array.
Notes
You can use MSDN as a reference on how to use a For loop or a For Each loop (If you haven't learned For loops yet, you can also use a Do Loop)
<the value being checked> will be different on each iteration through the loop; it should correspond to each consecutive value in your array. You can verify that this is working by setting a breakpoint.
You can get the length of a string by saying myString.Length
If you've learned about Functions, consider writing a function that takes an array as a parameter, and returns the longest value in the array.
There are certainly ways you could do this with LINQ, but I don't think that is the goal of the assignment ;-]
In response to Edit 1:
Your If statement needs to be inside of some sort of loop (For, For Each, Do, etc) I think this is the key concept that you are missing.
Instead of comparing LongName.Length to LengthOfLongestName, you need to compare the length of an entry in your array to LengthOfLongestName
You're on the right track with Members(0).Name.Length, but you can't just check element 0; you have to check every element in the array.
Given your current code, you'll probably be assigning <An entry in your array>.Name to LongName
The last index in a one-dimensional array is <array>.Length - 1 or <array>.GetUpperBound(0).
The following doesn't really address anything in your assignment, but I hope it will give you some ideas on how to go through all the items in your list:
' A For loop that does a message box for each of the numbers from 0 to 5 '
For i as Integer = 0 To 5
MessageBox.Show(i)
Next i
' Code that does a message box with the names of the 2nd, 3rd and last '
' entries in Members '
' (Remember that the first item is at 0, the second item is at 1, etc...) '
MessageBox.Show(Members(1).Name)
MessageBox.Show(Members(2).Name)
MessageBox.Show(Members(Members.GetUpperBound()).Name)
In response to Edit 2:
You're getting warmer...
You should only update LongName and LengthOfLongName if the current value is the longest you've seen so far (i.e. they should be assigned inside of the If statement)
You have to go to the last index of the array, not 5. See above (the response to your first edit) on how to get that last index.
You don't really need the UltimateName variable; you can just use LongName ;-]
You might want to use <stringVariable>.Length instead of Len(<stringVariable>) to be consistent.
What you are missing is a loop that checks each member, and putting the If statement inside it and make it compare the length of the name to the longest name that you have found so far. If the name is longer, put it in the variable for the longest found, and update the length variable.
You can either initialise the variables with the name of the first member and loop from the second member and on, or you can initialise the variables with an empty string and loop all the members. Personally I prefer the latter one.

Resources