Could someone explain what is happening here? Preferably line for line. I am having a hard time wrapping my head around what is happening with this bit.
a = Application.Transpose(a)
For i = 1 To UBound(a, 2)
If UCase(a(1, i)) Like "*" & temp & "*" Or _
UCase(a(2, i)) Like "*" & temp & "*" Then
n = n + 1
For ii = 1 To UBound(a, 1)
a(ii, n) = a(ii, i)
Next
End If
Next
I am also experiencing a "type mismatch" error for the above. See full Sub below.
Private Sub TextBox_Search_Change()
Select Case True
Case OptionButton_User_Name.Value
Dim a, i As Long, ii As Long, n As Long, temp As String
If Len(Me.TextBox_Search.Value) Then
temp = UCase(Me.TextBox_Search.Value)
With Sheets("ToolData")
a = Union(.Range("B:B"), .Range("F:F"), .Range("G:G")).Value
End With
a = Application.Transpose(a)
For i = 1 To UBound(a, 2)
If UCase(a(1, i)) Like "*" & temp & "*" Or _
UCase(a(2, i)) Like "*" & temp & "*" Then
n = n + 1
For ii = 1 To UBound(a, 1)
a(ii, n) = a(ii, i)
Next
End If
Next
If n > 0 Then
ReDim Preserve a(1 To UBound(a, 1), 1 To n)
Me.ListBox_History.Column = a
End If
Else
With Sheets("ToolData")
Me.ListBox_History.List = Union(.Range("B:B"), .Range("F:F"), .Range("G:G")).Value
End With
End If
Case Else
End Select
You cannot use .Value on a multi-area range: you will only get values from the first column (B).
I would refactor the True portion of your If statement as
temp = UCase(Me.TextBox_Search.Value)
Dim rngValues As Variant
With Sheets("ToolData")
rngValues = .Range("B2", .Range("B" & .Rows.Count).End(xlUp)).Resize(, 6).Value
End With
For i = 1 To UBound(rngValues, 1)
'Check columns B & F for matching values
If UCase(rngValues(i, 1)) Like "*" & temp & "*" Or _
UCase(rngValues(i, 5)) Like "*" & temp & "*" Then
'Store columns B, F & G for displaying in the ListBox
n = n + 1
ReDim Preserve a(1 To 3, 1 To n)
a(1, n) = rngValues(i, 1)
a(2, n) = rngValues(i, 5)
a(3, n) = rngValues(i, 6)
End If
Next
'If anything found, replace the ListBox contents. Otherwise leave it as it was.
If n > 0 Then
Me.ListBox_History.Column = a
End If
thus getting rid of the code which is reading into memory all 1048576 rows of your sheet, and the need of the Transpose (which won't work on large volumes of data).
You will also need to change the False leg of your If, perhaps in a similar fashion.
Related
I was doing a project earlier that consisted of many index/matches. I personally love nested loops, so I wanted to see if I could implement it this time around. My nested loop consists of i and j, where my third and most outer loop is my x variable, which gets placed inside my (Application.index). I've never done three loops so I'm not sure if this is possible. The error I am getting is, "application-defined or object-defined error".
Thanks,
SD
Dim i%, j%
Dim j&
Dim myArr%(0 To 5)
'myArr = Array(2, 1, 17, 18, 6, 16)
'For x = LBound(myArr) To UBound(myArr)
myArr(0) = 2
myArr(1) = 1
myArr(2) = 17
myArr(3) = 18
myArr(4) = 6
myArr(5) = 16
For x = 0 To 5
For i = 2 To shSS.Range("A1048576").End(xlUp).Row
For j = 1 To 16
Sheets("Sheet1").Cells(i, j) = Application.IfError(Application.Index(shRoster.Columns(x), Application.Match(shSS.Range("D" & i + 1), shRoster.Columns(4), 0)), "-")
If shSS.Cells(i, 11) = "Internal" Then
shCV.Cells(i, 10) = "Y"
Else
shCV.Cells(i, 10) = "N"
End If
Next j
Next i
Next x
Instead of this:
Sheets("Sheet1").Cells(i, j) = _
Application.IfError(Application.Index(shRoster.Columns(x), _
Application.Match(shSS.Range("D" & i + 1), shRoster.Columns(4), 0)), "-")
you can do something like:
Dim m
m = Application.Match(shSS.Range("D" & i + 1), shRoster.Columns(4), 0)
If Not IsError(m) Then
Sheets("Sheet1").Cells(i, j).Value = shRoster.Columns(x).Cells(m)
Else
Sheets("Sheet1").Cells(i, j).Value = "'-"
End If
So I've been playing around with Excel VBA to see what I can do with it. Currently, I'm stuck on one problem. My code is this:
Sub Validate_Input_Click()
Dim temp As String
For Row = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(Row, 2), Cells(Row, 12))) = 0 Then
temp = ""
For col = 2 To 12
If Cells(Row, col) <> "" Then
If temp <> "" Then temp = temp & "_"
temp = temp & Cells(Row, col)
End If
Next col
Cells(Row, 1) = temp
End If
Next Row
End Sub
This works exactly as I want it to. What I'm trying to do now is, lets say in a few cells of columns B through E have Text with a dash then more text, for example:
Test - Testing
What I want to do along with concatenating is, Grab everything to the left of that dash in each individual cell. So it would look something like,
Running_This_Test_In_Some_Kind_Of_Format
instead of:
Running_This_Test - Testing_In_Some_Kind_Of_Format
I've tried creating an integer and creating a Left statement but keeps giving me not enough memory errors or using wrong argument, not sure what I'm doing incorrectly. So any help would be much appreciated!
You can replace
temp = temp & Cells(Row, col)
with
pos = InStr(1, Cells(Row, col), "-", vbTextCompare) 'find the position of dash
If pos Then 'if dash position exists
temp = temp & Trim(Left(Cells(Row, col), pos - 1)) 'take left part of that string and trim to get rid of spaces
Else
temp = temp & Cells(Row, col) 'else do it as you did it before
End If
There is no need to check for empty cell again as you are already checking them with CountBlank.
What about this?
Sub Validate_Input_Click()
Dim temp As String, str As String
Dim iRow As Long, Col As Long
For iRow = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(iRow, 2), Cells(iRow, 12))) = 0 Then
temp = ""
For Col = 2 To 12
str = Trim(Split(Cells(iRow, Col), "-")(0))
If temp = "" Then
temp = str
Else
temp = temp & "_" & str
End If
Next Col
Cells(iRow, 1) = temp
End If
Next iRow
End Sub
Some slight alterations made... probably not the cleanest solution, but a solution nonetheless:
Sub Validate_Input_Click()
Dim temp As String, nextstring As String
Dim i As Long
For Row = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(Row, 2), Cells(Row, 12))) = 0 Then
temp = ""
For col = 2 To 12
If Cells(Row, col) <> "" Then
If InStr(Cells(Row, col), "-") > 0 Then
For i = 1 To Len(Cells(Row, col))
If Mid(Cells(Row, col), i, 1) = "-" Then
nextstring = Left(Cells(Row, col), i - 2)
Exit For
End If
Next i
Else
nextstring = Cells(Row, col)
End If
If temp <> "" Then temp = temp & "_"
temp = temp & nextstring
End If
Next col
Cells(Row, 1) = temp
End If
Next Row
End Sub
In messing around with the code I think I found another solution to my own problem. The code looks like:
Sub Validate_Input_Click()
Dim temp As String
Dim s As String
For Row = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(Row, 2), Cells(Row, 12))) = 0 Then
temp = ""
For col = 2 To 12
If Cells(Row, col) <> "" Then
s = temp
If temp <> "" Then temp = Split(s, " - ")(0) & "_"
temp = temp & Cells(Row, col)
End If
Next col
Cells(Row, 1) = temp
End If
Next Row
End Sub
Would this be a viable solution as well? Or would something else work better like the answer above from #dwirony?
Or the following. It will be fast as uses array, typed functions, used range and compared with vbNullString.
Option Explicit
Public Sub Concat()
Application.ScreenUpdating = False
Dim arr(), wb As Workbook, ws As Worksheet, i As Long, j As Long, concatString As String
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet9") 'Change as required
With ws
arr = Intersect(.Range("B:E"), .UsedRange)
For i = LBound(arr, 1) To UBound(arr, 1)
concatString = vbNullString
For j = LBound(arr, 2) To UBound(arr, 2)
If InStr(1, arr(i, j), "-") > 0 Then concatString = concatString & Left$(arr(i, j), InStr(1, arr(i, j), "-") - 1)
Next j
.Cells(i, 1) = Join$(Split(Trim$(concatString), Chr$(32)), "_")
Next i
End With
Application.ScreenUpdating = True
End Sub
Data:
I'm creating a program to randomly assign volunteers to different positions. There are 8 different spots on each date that need to be assigned to different volunteers. I have the code working except I can't seem to figure out how to get rid of duplicates. I added some code that prevents two spots in a row from being duplicates but that's as far as I got.
This is what the sheet looks like: (Notice that on the first date Connor Reilley is listed for two spots of the same position and Pierce Lewin is signed up for two different positions. That has to be changed).
Here is my code:
For j = 2 To jRows
Assign:
If wksCal.Range("C" & j) = "1st Reader" Or wksCal.Range("C" & j) = "2nd Reader" Then
Lector:
iRand = Int((iRows - 2) * Rnd())
If strLec(iRand) = "" Then
GoTo Assign
End If
wksCal.Range("D" & j) = strLec(iRand)
If wksCal.Range("D" & j) = wksCal.Range("D" & j - 1) Then
GoTo Lector
End If
ElseIf wksCal.Range("C" & j) = "EM" Then
EM:
iRand = Int((iRows - 2) * Rnd())
If strEM(iRand) = "" Then
GoTo Assign
End If
wksCal.Range("D" & j) = strEM(iRand)
If wksCal.Range("D" & j) = wksCal.Range("D" & j - 1) Then
GoTo EM
End If
ElseIf wksCal.Range("C" & j) = "Altar Server" Then
Server:
iRand = Int((iRows - 2) * Rnd())
If strAS(iRand) = "" Then
GoTo Assign
End If
wksCal.Range("D" & j) = strAS(iRand)
If wksCal.Range("D" & j) = wksCal.Range("D" & j - 1) Then
GoTo Server
End If
End If
Next j
a suggestion about how to manage the list of volunteers
With wkscal
Dim a()
ReDim a(irows) ' volunteers index list
For s = 1 To 3 ' 3 dates to fill
With .Cells((s - 1) * 8 + 1, 1)
' fill volunteers index list
For i = 1 To irows
a(i) = i
Next
'
For i = 1 To 8 '8 positions to fill per date
'choose an index in the list of remaining valid index
q = Application.RandBetween(1, irows - i + 1)
.Range("D" & i) = strlec(a(q))
' remove the last chosen index from the list
a(q) = a(irows - i + 1)
Next i
End With
Next s
End With
I have a code that takes a list of Airline flightlegs and matches them up to give me full lines of flight. The code works but..... it takes a very long time (45-60 min for just 35,000 rows) due to the amount of data it has to go through. This is compounded and the overall code takes about 2 hours to run. Is there a faster method to get the same results?
Here is my current code that really bogs down the entire process:
Sub BuildingLines()
'strings together segments into trip
Dim i As Long
Dim z As Long
Dim T As Long
Dim c As Long
Dim a As Long
Dim f As Long
Dim l As Long
Dim g As Long
Dim y As String
Dim b As String
Set ref = Sheets("Ref")
With Sheets("MoveData")
z = .Cells(1, 1).End(xlDown).Row
x = .Cells(2, 1).End(xlToRight).Column
Range(.Cells(1, 3), (.Cells(z, x + 3))).Clear
z = .Cells(1, 1).End(xlDown).Row
g = ref.Cells(24, 1).End(xlDown).Row
For a = 24 To g
If ref.Cells(a, 2) = "" Then GoTo nexta
f = ref.Cells(a, 2)
c = ref.Cells(a, 3)
l = ref.Cells(a, 4)
Set LegTable = Range(.Cells(f, 1), .Cells(l, 1))
For i = f To l
Application.StatusBar = "Progress: Step 5 of 20 - Building lines for " & ref.Cells(a, 1) & " (" & (a - 23) & " A/C types of " & (g - 23) & ") : " & (i - f) + 1 & " Legs of " & c & " analyzed. (" & Format(((i - f) + 1) / c, "Percent") & ")"
DoEvents
'On Error GoTo NextI
If IsError(Application.Match(.Cells(i, 2), LegTable, 0)) Then
GoTo nexti
Else
y = Application.Match(.Cells(i, 2), LegTable, 0) + f - 1
.Cells(i, 1).End(xlToRight).Offset(0, 1).Value2 = .Cells(y, 2)
Do
'On Error GoTo NextI
If IsError(Application.Match(.Cells(y, 2), LegTable, 0)) Then
GoTo nexti
Else
b = Application.Match(.Cells(y, 2), LegTable, 0) + f - 1
h = .Cells(b, 2)
.Cells(i, 1).End(xlToRight).Offset(0, 1) = h
y = b
End If
Loop
nexti:
End If
b = ""
y = ""
Next i
nexta:
Next a
End With
End Sub
The data is all string data of about 50+ chars.
Thank you for any recomendations.
Thank you very much A.S.H. with your help, I not only learned alot about using arrays, but also ended up cutting my runtime from about 90 minutes to just over 3 minutes. This is my final working code that used a combination of your suggestions.
Sub BuildingLines()
'strings together segments into trip
Dim i As Long
Dim z As Long
Dim c As Long
Dim f As Long
Dim l As Long
Dim LegTable As Range
Dim TurnTable As Range
Dim FirstTurn() As Variant
Dim NextTurn() As Variant
Dim y As String
Dim b As String
Dim FTtext As String
Dim wb As Workbook
Dim ref As Worksheet
Set wb = ThisWorkbook
Set ref = wb.Sheets("Ref")
With Sheets("MoveData")
z = .Cells(1, 1).End(xlDown).Row
x = .Cells(2, 1).End(xlToRight).Column
Range(.Cells(1, 3), (.Cells(z, x + 3))).Clear
z = .Cells(1, 1).End(xlDown).Row
g = ref.Cells(24, 1).End(xlDown).Row
For a = 24 To g
If ref.Cells(a, 2) = "" Then GoTo NextA
f = ref.Cells(a, 2)
c = ref.Cells(a, 3)
l = ref.Cells(a, 4)
Set LegTable = Range(.Cells(f, 1), .Cells(l, 1))
Set TurnTable = Range(.Cells(f, 1), .Cells(l, 2))
FirstTurn = TurnTable
.Sort.SortFields.Clear
.Sort.SortFields.Add Key:=LegTable, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With .Sort
.SetRange TurnTable
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
For i = f To l
Application.StatusBar = "Progress: Step 5 of 20 - Building lines for " & ref.Cells(a, 1) & " (" & (a - 23) & " A/C types of " & (g - 23) & ") : " & (i - f) + 1 & " Legs of " & c & " analyzed. (" & Format(((i - f) + 1) / c, "Percent") & ")"
DoEvents
y = 0
b = 0
y = Application.Match(.Cells(i, 2), LegTable, 1)
If .Cells(i, 2) <> FirstTurn(y, 1) Then GoTo NextI
NextLeg = NextLeg + 1
ReDim Preserve NextTurn(0, 1 To NextLeg)
NextTurn(0, NextLeg) = FirstTurn(y, 2)
Do
FTtext = FirstTurn(y, 2)
On Error GoTo errhdlr
b = Application.WorksheetFunction.Match(FTtext, LegTable, 1)
If FTtext <> FirstTurn(b, 1) Then GoTo NextI
NextLeg = NextLeg + 1
ReDim Preserve NextTurn(0, 1 To NextLeg)
NextTurn(0, NextLeg) = FirstTurn(b, 2)
y = b
Loop
errhdlr:
Resume NextI
NextI:
If NextLeg > 0 Then Range(.Cells(i, 3), .Cells(i, NextLeg + 2)).Value = NextTurn
Erase NextTurn
NextLeg = 0
Next i
Set LegTable = Nothing
Set TurnTable = Nothing
Erase NextTurn
Erase FirstTurn
NextA:
Next a
End With
End Sub
I first tried using just the arrays, but the Match function was WAY SLOWER in the arrays. So I ended up using the Match to find the index and then grabbed the data from the array to build my second array which then became my output. I can't wait to adapt my new found knowledge with the rest of this project and cut my runtime from 2+ hours to just minutes!! Thanks, again!!!
I assume that formatting time and date in an array isn't possible? If yes how would you change the format of a column in a ListBox? This is the part that I'm working with.
Private Sub TextBox_Search_Change()
Select Case True
Case OptionButton_User_Name.Value
temp = UCase(Me.TextBox_Search.Value)
Dim a()
Dim rngValues As Variant
With Sheets("ToolData")
rngValues = .Range("B2", .Range("B" & .Rows.Count).End(xlUp)).Resize(, 11).Value
End With
For i = 1 To UBound(rngValues, 1)
'Check columns B & F for matching values
If UCase(rngValues(i, 1)) Like "*" & temp & "*" Then
'(i, Colunm being searched)
'Store columns B, F & G for displaying in the ListBox
n = n + 1
ReDim Preserve a(1 To 8, 1 To n)
'ListBox (Colunms 1-...)
a(1, n) = rngValues(i, 1)
a(2, n) = rngValues(i, 2)
a(3, n) = rngValues(i, 5)
a(4, n) = rngValues(i, 6)
a(5, n) = rngValues(i, 7)
a(6, n) = rngValues(i, 8)
a(7, n) = rngValues(i, 9)
a(8, n) = rngValues(i, 10)
'ListBox = rngValues(B,+Colunms on sheet)
End If
Next
'If anything found, replace the ListBox contents. Otherwise leave it as it was.
If n > 0 Then
Me.ListBox_History.Column = a
Me.ListBox_History.Column(3, Me.ListBox_History.ListCount - 1) = Format("hh:mm")
End If
Case Else
At the bottom I added this bit as a test Me.ListBox_History.Column(3, Me.ListBox_History.ListCount - 1) = Format("hh:mm") But it doesn't format and instead displays hh:mm in row 1 of the ListBox.
So it should work to format values in an array if they are not numbers.
Try:
a(1, n) = Format(rngValues(i, 1), "hh:mm")
If this is not working you could cylce trough the column like that:
With ListBox1
For i = 0 To .ListCount - 1
.List(i, 0) = (Format(.List(i, 0), "hh:mm"))
Next i
End With