How do I append an initially empty string array in VBA? - excel

I am trying to add a variable number of items to a string array.
My code is in a worksheet change function:
Dim StartNums(0 To 2) As String
doneColor = RGB(175, 175, 175)
cmt = FRg.Comment.Text
rowLen = InStr(1, cmt, vbLf)
If rowLen = 0 Then
rowLen = Len(cmt)
End If
numChunks = rowLen / 32
numRows = Len(cmt) / rowLen
' For i = 1 To 12 'FRg.Comment.Shape.TextFrame.Characters.Count
' With FRg.Comment.Shape.TextFrame.Characters(i, 1)
' If .Font.Strikethrough = True Then
' .Font.Color = vbGreen
' End If
' End With
' Next i
MsgBox ("About to fill StartNums, nothing should be in it yet")
For j = 0 To numChunks - 1
MsgBox ("going to add stuff for chunk " & j)
If Not UBound(StartNums) = 2 Then
' MsgBox ("resizing an empty array")
' ReDim Preserve StartNums(3) As Variant
' Else
'MsgBox ("resizing a non-empty array")
ReDim Preserve StartNums(UBound(StartNums) + 3) As String
End If
StartNums(UBound(StartNums) - 2) = (j * 32) + 5 + (0 * 9)
StartNums(UBound(StartNums) - 1) = (j * 32) + 5 + (1 * 9)
StartNums(UBound(StartNums) - 0) = (j * 32) + 5 + (2 * 9)
Next j
Now whenever I go into the worksheet, the ReDim line has a Compile Error Array already dimensioned. I'm aware it's already dimensioned, which is why I'm ReDim-ing it.
How do I add 3 more spaces in an array?

When you initially Dim it, you can't give it the size if you plan on changing the size later:
so instead of
Dim StartNums(0 To 2) As String
You would use
Dim StartNums() As String
and then on the next line
ReDim StartNums(0 To 2)
note, you can also use this, as it's assumed to be 0 based by default.
ReDim StartNums(2)
That will accomplish the same thing, but then you can later use Redim Preserve to change the size of it.

Related

X Unique Randomize Numbers

i need a little bit help.
Is it possible to fill a list with random numbers and to check this list before each loop to see if the number already exists?
I think im on the wrong way with my VBA.
Sub Zufallszahlen()
Dim Rng As Range
Max = 6
Min = 1
Anzahl = 4
counter = 0
innercounter = 0
SZeile = 2
AWert = "X"
Range("C:C").Clear
Do
counter = counter + 1
ZZahl = Int((Max * Rnd) + Min)
innercounter = 0
Do
innercounter = innercounter + 1
If Cells(innercounter, 2) = ZZahl Then
ZZahl = Int((Max * Rnd) + Min)
Else
Loop Until innercounter = Anzahl
' Cells(counter, 1).Value = counter
Cells(counter, 2).Value = ZZahl
Cells(ZZahl, 3).Value = AWert
Loop Until counter = Anzahl
Range("B:B").Clear
End Sub
Use an array to check if random number has already been chosen. Repeat until a vacant array position is found.
Option Explicit
Sub Zufallszahlen()
Const MaxN = 6
Const MinN = 1
Const Anzahl = 4
Const Awert = "X"
Dim ar, n As Long, r As Long, i As Long
n = MaxN - MinN + 1
If n < Anzahl Then
MsgBox "Min to Max range must be >= " & Anzahl
Exit Sub
End If
' values in column B
Dim arB, total As Single, try As Long
arB = Range("B" & MinN).Resize(n).Value2
Do
' avoid endless loop
try = try + 1
If try > 100 Then
MsgBox "Could not solve in 100 tries", vbExclamation
Exit Sub
End If
' generate random selection
ReDim ar(1 To n, 1 To 1)
total = 0
For i = 1 To Anzahl
Do
r = 1 + Int(n * Rnd())
Loop Until ar(r, 1) = ""
ar(r, 1) = Awert
' sum col B
total = total + arB(r, 1)
Next
Range("C:C").Clear
Range("C" & MinN).Resize(n) = ar
Loop Until total >= 10 And total <= 20 ' check total in range
MsgBox "Total=" & Format(total, "0.00"), vbInformation, try & " tries"
End Sub
You can use the Scripting.Dictionary object to check.
Given it's a "Dictionary", it requires that all keys are unique.
This is a crude implementation demonstrating the random filling of that dictionary with all numbers between 50 and 100.
Public Sub DoRandomize()
Dim objUnique As Object, i As Long, lngRandom As Long
Dim lngMin As Long, lngMax As Long, dblRandom As Double
lngMin = 50: lngMax = 100
Set objUnique = CreateObject("Scripting.Dictionary")
Do While objUnique.Count <> (lngMax - lngMin) + 1
Randomize objUnique.Count
lngRandom = (Rnd(objUnique.Count) * (lngMax - lngMin)) + lngMin
If Not objUnique.exists(lngRandom) Then
Debug.Print "Adding ......... " & lngRandom
objUnique.Add lngRandom, vbNull
Else
Debug.Print "Already used ... " & lngRandom
End If
Loop
End Sub
... you'd just need to pull out the relevant parts for your implementation but you can paste that code into your project, run it and see it work for yourself.
Ty Guys thats perfect =) i use this now and it works very nice + i understand my
misconception
Sub Zufallszahlen()
Const MaxN = 29
Const MinN = 1
Const Anzahl = 4
Const Awert = "X"
Dim ar, n As Long, r As Long
n = MaxN - MinN + 1
If n < Anzahl Then
MsgBox "Min to Max range must be >= " & Anzahl
Exit Sub
End If
ReDim ar(1 To n, 1 To 1)
For i = 1 To Anzahl
Do
r = 1 + Int(n * Rnd())
Loop Until ar(r, 1) = ""
ar(r, 1) = Awert
Next
Range("C:C").Clear
Range("C" & MinN).Resize(n) = ar
End Sub
Buts not finally completed.
Can I include this part in another if?
This is intended to ensure that the values ​​of the cells to the left of the cells randomly marked with an x ​​add up to between 10 and 20, for example. Otherwise the random cells should be regenerated

Remove string after a certain character

i'm trying to move words after the first space from 30th character in a string to the next row(i+1) added and remove those words moved to the next row from the current row(i). The code is giving Run-time error '5': Invalid procedure call or argument error message at Cells(i, 1).Value = Left(Cells(i, 1), InStr(30, Cells(i, 1), " ") - 1) line.
Sub TextLimit_02()
Dim i As Long
Dim CelLen As Long
For i = 1 To 50
CelLen = Len(Cells(i, 1))
If CelLen > 40 Then
Rows(i + 1).Insert
Cells(i + 1, 1).Value = Mid(Cells(i, 1), InStr(30, Cells(i, 1), " ") + 1, Len(Cells(i,
1).Value) - InStr(30, Cells(i, 1), " "))
Cells(i, 1).Value = Left(Cells(i, 1), InStr(30, Cells(i, 1), " ") - 1)
Else
End If
Next i
End Sub
Your code doesn't account for the possibility that in a string longer than 40, there are no spaces after position 30.
Breaking the logic down into steps makes it easy to detect this, and possibly add more code to deal with it
Sub TextLimit_02()
Dim i As Long
Dim CelLen As Long
Dim idx As Long
Dim CellString As String
Dim LeftPart As String
Dim RightPart As String
For i = 1 To 50
CellString = Cells(i, 1).Value2
CelLen = Len(CellString)
If CelLen > 40 Then
idx = InStr(30, CellString, " ")
If idx > 0 Then
Rows(i + 1).Insert
LeftPart = Left$(CellString, idx - 1)
RightPart = Mid$(CellString, idx + 1)
Cells(i, 1) = LeftPart
Cells(i + 1, 1) = RightPart
If idx > 40 Then
' there remains >40 characters in Cells(i, 1)
End If
Else
' No spaces after position 30. Cells(i, 1) remains longer than 40
End If
End If
Next i
End Sub
Note that if you split a lot of strings, your data may end up spanning past row 50. With For i = 1 to 50 you will miss processing some string in that case.
This code should do what you intend. Please test it and let me know what it does wrong.
Sub TextLimit_02()
' 269
Dim Txt As String ' text
Dim Tail As String ' tail end of Txt to be moved
Dim p As Long ' position of character in string
Dim R As Long ' loop counter: rows
Application.ScreenUpdating = False
' start from the end of column A to 2nd row
For R = Cells(Rows.Count, "A").End(xlUp).Row To 2 Step -1
Txt = Trim(Cells(R, "A").Value) ' remove leading/trailing blanks
p = InStr(Mid(Txt, 30), " ")
Tail = Trim(Mid(Txt, p + 30)) ' in case of double space
If Len(Tail) Then ' skip if no Tail
Cells(R, "A").Value = Left(Txt, p + 29)
Rows(R + 1).Insert
Cells(R + 1, "A").Value = Tail
End If
Next R
Application.ScreenUpdating = True
End Sub
Try this code:
Sub WrapText()
Const MIN_CARRY_POS = 30, MAX_LEN = 40, CARRY_SYMBOL = " " 'preferences
Dim cl As Range, txt As String, pos As Long, slice As Long
Application.ScreenUpdating = False
Set cl = ThisWorkbook.Worksheets(1).Range("A1")
Do 'main row loop
txt = RTrim(cl.Text)
Select Case Len(txt)
Case 0: Exit Do
Case Is > MAX_LEN
Do While Len(txt) > MAX_LEN 'a nested loop to process text that needs to be carried over
pos = InStr(MIN_CARRY_POS, txt, CARRY_SYMBOL)
If pos < 1 Then pos = MAX_LEN 'if no CARRY_SYMBOL after MIN_CARRY_POS then cut the string at MAX_LEN
slice = WorksheetFunction.Min(pos, MAX_LEN)
cl.Insert xlDown
cl.Offset(-1).Value = Mid(txt, 1, slice) 'output the head
txt = LTrim(Mid(txt, slice + 1)) 'get the rest of the txt
Loop
cl.Value = txt 'output the rest <= MAX_LEN
End Select
Set cl = cl.Offset(1) 'move to the next row
Loop
Application.ScreenUpdating = True
End Sub
Before
After

Loop over 2D Array Is not showing saved values

My problem is that im looping over an 2d array in a function but I can't get the previously saved values.
This is my code:
Dim RowCountTemp As Integer
Dim ColumCountTemp As Integer
Dim MonthArray, ColumnArray
Dim NewTable() As String
Dim NewTableLen As Integer
NewTableLen = 0
MonthArray = Array("Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre")
ColumnArray = Array("A", "B", "C")
For Each MonthTemp In MonthArray
With Worksheets(MonthTemp).ListObjects(MonthTemp)
RowCountTemp = .DataBodyRange.Rows.Count
ColumCountTemp = .DataBodyRange.Columns.Count
For i = 1 To RowCountTemp
Dim RucTemp As String
Dim CantTemp As String
Dim Index As Integer
RucTemp = .Range(i + 1, 1)
CantTemp = .Range(i + 1, 3)
Index = Contains(NewTable, NewTableLen, RucTemp)
If Index > 0 Then
NewTable(Index, 3) = NewTable(Index, 3) + CantTemp
Worksheets("Resumen").Range(ColumnArray(2) & Index + 1) = NewTable(Index, 3)
Else
NewTableLen = NewTableLen + 1
'Added Preserve from comment but it throws subindex out of range for the second value added
ReDim Preserve NewTable(NewTableLen, 3)
NewTable(NewTableLen, 1) = .Range(i + 1, 1)
NewTable(NewTableLen, 2) = .Range(i + 1, 2)
NewTable(NewTableLen, 3) = .Range(i + 1, 3)
Worksheets("Resumen").Range(ColumnArray(0) & NewTableLen + 1) = NewTable(NewTableLen, 1)
Worksheets("Resumen").Range(ColumnArray(1) & NewTableLen + 1) = NewTable(NewTableLen, 2)
Worksheets("Resumen").Range(ColumnArray(2) & NewTableLen + 1) = NewTable(NewTableLen, 3)
End If
Next
End With
Next
And my function
Function Contains(Array2D() As String, Length As Integer, SearchFor As String) As Integer
For i = 1 To Length
Contains = 0
If Array2D(i, 1) = SearchFor Then
Contains = i
Exit For
End If
Next
End Function
For instance, when I read the first sheet, January, I save data correctly and I can see it when evaluate Array2D(i, 1), but for the next sheets, when I evaluate Array2D(i, 1), it gives me empty values for all the index stored in past sheets.
Let's suppose January sheet gets 2 rows with values aaaa and bbbb, so, the code allow me to watch aaaa stored when evaluates bbbb, but in february sheet which has aaaa, bbbb and cccc, when Array2D(i, 1) is evaluated until i = 2 for aaaa, i = 3 for bbbb and i = 4 for cccc, the values are empty "" for aaaa, but for bbbb and cccc, it shows me aaaa and bbbb respectively from february sheet so the values are being saved twice
Edit:
I have realized that every time I redeem the previous ones are deleted and the last one is saved
I added Preserve but it throws me subindex out of range for the second value added
Well, at the end i changed the code a little bit to achieve those results.
Here is
Dim RowCountTemp As Integer
Dim ColumCountTemp As Integer
Dim MonthArray, ColumnArray
Dim NewTable() As String
Dim NewTableLen As Integer
NewTableLen = 0
MonthArray = Array("Enero2020", "Febrero2020", "Marzo2020", "Abril2020", "Mayo2020", "Junio2020", "Julio2020", "Agosto2020", "Septiembre2020", "Octubre2020", "Noviembre2020", "Diciembre2020", "Enero2021", "Febrero2021", "Marzo2021", "Abril2021", "Mayo2021")
ColumnArray = Array("A", "B", "C")
For Each MonthTemp In MonthArray
With Worksheets(MonthTemp).ListObjects(MonthTemp)
RowCountTemp = .DataBodyRange.Rows.Count
ColumCountTemp = .DataBodyRange.Columns.Count
'If MonthTemp = "Abril2021" Or MonthTemp = "Mayo2021" Then
For i = 1 To RowCountTemp
Dim RucTemp As String
Dim CantTemp As String
Dim Index As Integer
RucTemp = .Range(i + 1, 1)
CantTemp = .Range(i + 1, 3)
Index = Contains(NewTable, NewTableLen, RucTemp)
If Index > 0 Then
Worksheets("Resumen").Range(ColumnArray(2) & Index + 1) = Worksheets("Resumen").Range(ColumnArray(2) & Index + 1) + CantTemp
Else
NewTableLen = NewTableLen + 1
ReDim Preserve NewTable(NewTableLen)
NewTable(NewTableLen) = .Range(i + 1, 1)
Worksheets("Resumen").Range(ColumnArray(0) & NewTableLen + 1) = NewTable(NewTableLen)
Worksheets("Resumen").Range(ColumnArray(1) & NewTableLen + 1) = .Range(i + 1, 2)
Worksheets("Resumen").Range(ColumnArray(2) & NewTableLen + 1) = .Range(i + 1, 3)
End If
Next
'End If
End With
Next
Function Contains(Array2D() As String, Length As Integer, SearchFor As String) As Integer
For i = 1 To Length
Contains = 0
If Array2D(i) = SearchFor Then
Contains = i
Exit For
End If
Next
End Function

Text in a row to a repeating column

I have an excel table where there are part codes in a column and for every part code, there are 3-4 subsections (1100-1400) with information which I need to attach to the part code in a column view.
The number of created rows depends on if there is data entered into subsection 1400. 1100-1300 has always information and needs to be converted into a table.
I don't even know from where to start so currently I have no code to show
I added a picture of how the data is represented and what the result should look like:
You could do it like that
Option Explicit
Sub TransformA()
Dim rg As Range
Dim lastRow As Long, lineNo As Long, i As Long, j As Long
Dim shInput As Worksheet, shResult As Worksheet
Dim vDat As Variant, resDat As Variant
Dim subSection As String
' Make sure you run the code with the data in the Activesheet
Set shInput = ActiveSheet
' And you have data which starts in row 4 with the heading in row 3
' otherwise adjust accordingly
lastRow = shInput.Range("A4").End(xlDown).Row
Set rg = shInput.Range("A4:I" & lastRow)
vDat = rg
ReDim resDat(1 To UBound(vDat, 1) * 4, 1 To 4)
lineNo = 1
For j = 1 To UBound(vDat, 1)
For i = 0 To 2
Select Case i
Case 0: subSection = "1100"
Case 1: subSection = "1200"
Case 2: subSection = "1300"
End Select
resDat(lineNo + i, 1) = vDat(j, 1)
resDat(lineNo + i, 2) = subSection
resDat(lineNo + i, 3) = vDat(j, 2 + 2 * i)
resDat(lineNo + i, 4) = vDat(j, 3 + 2 * i)
Next
i = 3
subSection = "1400"
If Len(vDat(j, 2 + 2 * i)) = 0 And Len(vDat(j, 3 + 2 * i)) = 0 Then
lineNo = lineNo + 3
Else
resDat(lineNo + i, 1) = vDat(j, 1)
resDat(lineNo + i, 2) = subSection
resDat(lineNo + i, 3) = vDat(j, 2 + 2 * i)
resDat(lineNo + i, 4) = vDat(j, 3 + 2 * i)
lineNo = lineNo + 4
End If
Next
' Output the result to a new sheet
Set shResult = Sheets.Add
With shResult
.Cells(1, 1).Value = "Part Code"
.Cells(1, 2).Value = "Subsection"
.Cells(1, 3).Value = "Time"
.Cells(1, 4).Value = "Text"
End With
shResult.Range("A2").Resize(UBound(resDat, 1), 4) = resDat
End Sub

Trying to fix a Do While loop in VBA

thanks in advance for taking the time to help. I have built a Do While loop in VBA that for some reason breaks when j = 1. I have in cells C3:C7 these values: 13,14,14,13,14.
Here's the short script:
Dim i, j, n As Integer
Dim List(0) As Integer
i = o
j = 0
n = 0
Do While Cells(i + 3, 3) <> ""
If Cells(i + 3, 3) > 13 Then
List(j) = i + 3
j = j + 1
Cells(i + 3, 4) = "Noted"
i = i + 1
ElseIf Cells(i + 3, 3) = 13 Then
Cells(i + 3, 4) = "Skipped"
i = i + 1
Else
i = i + 1
End If
Loop
For n = j To n = 0
Rows(List(n)).Delete
Next
Thanks again!
Your intent is sound, but there are quite a few errors. See commented code below for details
Sub Demo()
' ~~ must explicitly type each variable. Use Long
Dim i As Long, j As Long, n As Long
Dim List() As Long '<~~ dynamic array
i = 3 '<~~ eliminate the klunky +3
j = 0
n = 0
ReDim List(0 To 0) '<~~ initialise dynamic array
Do While Cells(i, 3) <> vbNullString
If Cells(i, 3) > 13 Then
ReDim Preserve List(0 To j) '<~~ resize array
List(j) = i
j = j + 1
Cells(i, 4) = "Noted"
ElseIf Cells(i, 3) = 13 Then
Cells(i, 4) = "Skipped"
End If
i = i + 1 '<~~ simplify, its called in each if case anyway
Loop
' j will end up 1 greater than size of array
If j > 0 Then '<~~ only execute if we found some rows to delete
For n = j - 1 To 0 Step -1 '<~~ For loop syntax
Rows(List(n)).Delete
Next
End If
End Sub

Resources