Need help from Array VBA expert. Instead of formatting each cell in a range as per code below, is it possible to get this format included in Array so that once it write back to range it is formatted at the same time of writing?
Note that each item in oArr has varying formats as shown below
The current output once I run the code below
Option Explicit
Sub Write_Array_With_Format()
Dim xArr, aArr, bArr, sArr(), oArr() As Variant, lRow, i As Long, x, A, B As Double
With Worksheets("Data") 'set data ranges to array
lRow = .Cells(Rows.Count, 2).End(xlUp).Row
xArr = .Range(.Cells(6, 2), .Cells(lRow, 2)).Value2
aArr = .Range(.Cells(6, 3), .Cells(lRow, 3)).Value2
bArr = .Range(.Cells(6, 4), .Cells(lRow, 4)).Value2
End With
ReDim sArr(LBound(xArr, 1) To UBound(xArr, 1), 1 To 1) 'String Array
sArr = Array("x A B", "A x B", "A B x", "x B A", "B x A", "B A x")
sArr = Application.Transpose(sArr)
ReDim oArr(LBound(xArr, 1) To UBound(xArr, 1), 1 To 1) 'Output Array
For i = 1 To UBound(xArr, 1)
x = xArr(i, 1): A = aArr(i, 1): B = bArr(i, 1)
If x > A And x > B And A > B Then
oArr(i, 1) = sArr(1, 1)
ElseIf x < A And x > B And A > B Then
oArr(i, 1) = sArr(2, 1)
ElseIf x < A And x < B And A > B Then
oArr(i, 1) = sArr(3, 1)
ElseIf x > A And x > B And A < B Then
oArr(i, 1) = sArr(4, 1)
ElseIf x > A And x < B And A < B Then
oArr(i, 1) = sArr(5, 1)
ElseIf x < A And x < B And A < B Then
oArr(i, 1) = sArr(6, 1)
End If
Next
With Worksheets("Data")
.Range(.Cells(6, 5), .Cells(lRow, 5)).Value2 = oArr 'write Output Array to Range
For i = 6 To lRow 'Format values
If .Range("E" & i).Value = "x A B" Then
With .Range("E" & i)
With .Characters(1, 1).Font
.Color = vbBlue
End With
With .Characters(3, 3).Font
.Underline = True
.Color = vbGreen
End With
End With
ElseIf .Range("E" & i).Value = "A x B" Then
With .Range("E" & i)
With .Characters(1, 2).Font
.Color = vbGreen
.Underline = True
End With
With .Characters(3, 1).Font
.Underline = True
.Color = vbBlue
End With
With .Characters(5, 1).Font
.Color = vbGreen
End With
End With
'And so on and so forth.............
End If
Next
End With
End Sub
Please, try using the next approach. The code will iterate between the array elements, but it is not possible to keep format in an array... It will process each array element, only incrementing its rows, according to each case definition (in a separate Sub):
Sub testCellFormat()
'Dim dict As New Scripting.Dictionary, i As Long
Dim sh As Worksheet, lastR As Long, arr, oArr, sArr, arrFin, i As Long
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.rows.count).End(xlUp).row
sh.Range("E6:E" & lastR).Font.Color = vbBlack 'just to reset the range for the second test...
sh.Range("E6:E" & lastR).Font.Underline = False
arr = sh.Range("B6:D" & lastR).Value2 'place all the range in a single aray
sArr = Array("x A B", "A x B", "A B x", "x B A", "B x A", "B A x") 'A 1 D array is good enough, too
ReDim oArr(1 To UBound(arr), 1 To 1)
For i = 1 To UBound(arr)
If arr(i, 1) > arr(i, 2) And arr(i, 1) > arr(i, 3) And arr(i, 2) > arr(i, 3) Then
oArr(i, 1) = sArr(0)
ElseIf arr(i, 1) < arr(i, 2) And arr(i, 1) > arr(i, 3) And arr(i, 2) > arr(i, 3) Then
oArr(i, 1) = sArr(1)
ElseIf arr(i, 1) < arr(i, 2) And arr(i, 1) < arr(i, 3) And arr(i, 2) > arr(i, 3) Then
oArr(i, 1) = sArr(2)
ElseIf arr(i, 1) > arr(i, 2) And arr(i, 1) > arr(i, 3) And arr(i, 2) < arr(i, 3) Then
oArr(i, 1) = sArr(3)
ElseIf arr(i, 1) > arr(i, 2) And arr(i, 1) < arr(i, 3) And arr(i, 2) < arr(i, 3) Then
oArr(i, 1) = sArr(4)
ElseIf arr(i, 1) < arr(i, 2) And arr(i, 1) < arr(i, 3) And arr(i, 2) < arr(i, 3) Then
oArr(i, 1) = sArr(5)
End If
Next
sh.Range("E" & 6).Resize(UBound(oArr), 1).value = oArr 'drop the array content
For i = 1 To UBound(oArr)
cellFormat sh.Range("E" & i + 5) 'process the necessary range (built using the iteration variable)
Next i
End Sub
Sub cellFormat(rngE As Range)
Dim T As String: T = rngE.value
Dim boolUnderscore, boolGreen, boolRed, boolBlue
If Len(T) <> 5 Then Exit Sub
Select Case left(T, 3)
Case "x A"
rngE.Characters(1, 1).Font.Color = vbBlue
With rngE.Characters(3, 3).Font
.Color = vbGreen
.Underline = True
End With
Case "A x"
rngE.Characters(1, 3).Font.Underline = True
rngE.Characters(1, 2).Font.Color = vbGreen
rngE.Characters(3, 3).Font.Color = vbBlue
rngE.Characters(5, 1).Font.Color = vbGreen
Case "A B"
rngE.Characters(1, 4).Font.Color = vbGreen
rngE.Characters(5, 1).Font.Color = vbBlue
rngE.Characters(3, 3).Font.Underline = True
Case "x B"
rngE.Characters(1, 3).Font.Underline = True
rngE.Characters(1, 1).Font.Color = vbBlue
rngE.Characters(2, 5).Font.Color = vbRed
Case "B x"
rngE.Characters(3, 5).Font.Underline = True
rngE.Font.Color = vbRed
rngE.Characters(3, 1).Font.Color = vbBlue
Case "B A"
With rngE.Characters(1, 3).Font
.Color = vbRed
.Underline = True
End With
rngE.Characters(5, 1).Font.Color = vbBlue
End Select
End Sub
I asked about the occurrences number of the same string type. If there are many, the code can be optimized (I can do that) to use a dictionary where to keep a Union range to be formatted at once, of the end. But pere every category type. If not too many cases for the same string type, not much to be gain...
According to the used algorithm, the string types used by the second sub, can be kept in an array and use them a little more efficient.
Edited:
Please, try the following optimized solution. It will firstly place the unique strings from oArr (col E:E) in a dictionary (as keys) and as items Union ranges of (built) similar cells (in E:E). Then, it will process/format the Union ranges, at once:
Sub testCellFormat()
Dim sh As Worksheet, lastR As Long, arr, oArr, sArr, arrFin, i As Long
Dim dict As Object ' New Scripting.Dictionary
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.rows.count).End(xlUp).row
sh.Range("E6:E" & lastR).Font.Color = vbBlack 'just to reset the range for the second test...
sh.Range("E6:E" & lastR).Font.Underline = False
arr = sh.Range("B6:D" & lastR).Value2 'place all the range in a single aray
sArr = Array("x A B", "A x B", "A B x", "x B A", "B x A", "B A x") 'a 1 D array is good enough, too
ReDim oArr(1 To UBound(arr), 1 To 1)
For i = 1 To UBound(arr) 'iterate between the array rows and appropriately fill oArr elements:
If arr(i, 1) > arr(i, 2) And arr(i, 1) > arr(i, 3) And arr(i, 2) > arr(i, 3) Then
oArr(i, 1) = sArr(0)
ElseIf arr(i, 1) < arr(i, 2) And arr(i, 1) > arr(i, 3) And arr(i, 2) > arr(i, 3) Then
oArr(i, 1) = sArr(1)
ElseIf arr(i, 1) < arr(i, 2) And arr(i, 1) < arr(i, 3) And arr(i, 2) > arr(i, 3) Then
oArr(i, 1) = sArr(2)
ElseIf arr(i, 1) > arr(i, 2) And arr(i, 1) > arr(i, 3) And arr(i, 2) < arr(i, 3) Then
oArr(i, 1) = sArr(3)
ElseIf arr(i, 1) > arr(i, 2) And arr(i, 1) < arr(i, 3) And arr(i, 2) < arr(i, 3) Then
oArr(i, 1) = sArr(4)
ElseIf arr(i, 1) < arr(i, 2) And arr(i, 1) < arr(i, 3) And arr(i, 2) < arr(i, 3) Then
oArr(i, 1) = sArr(5)
End If
Next
sh.Range("E" & 6).Resize(UBound(oArr), 1).Value2 = oArr 'drop the array content
'place the not formatted range in a dictionary. Keys as oArr elements and items as (Union) build range:
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(arr)
If Not dict.Exists(oArr(i, 1)) Then
dict.Add oArr(i, 1), sh.Range("E" & i + 5)
Else
Set dict(oArr(i, 1)) = Union(dict(oArr(i, 1)), sh.Range("E" & i + 5))
End If
Next
'some optimization
With Application
.ScreenUpdating = False:
.Calculation = xlCalculationManual
.EnableEvents = False
End With
For i = 1 To UBound(oArr) 'iterate between oArr rows
cellFormatDict CStr(oArr(i, 1)), sArr, dict 'format each dictionary Union ranges, at once
Next i
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
MsgBox "Ready...", vbInformation, "Job done."
End Sub
Sub cellFormatDict(strCond As String, sArr, dict As Object)
Select Case left(dict(strCond), 3)
Case left(sArr(0), 3) ' "x A"
With dict(strCond)
.Characters(1, 1).Font.Color = vbBlue
With .Characters(3, 3).Font
.Color = vbGreen
.Underline = True
End With
End With
Case left(sArr(1), 3) ' "A x"
With dict(strCond)
.Characters(1, 3).Font.Underline = True
.Characters(1, 2).Font.Color = vbGreen
.Characters(3, 3).Font.Color = vbBlue
.Characters(5, 1).Font.Color = vbGreen
End With
Case left(sArr(2), 3) ' "A B"
With dict(strCond)
.Characters(1, 4).Font.Color = vbGreen
.Characters(5, 1).Font.Color = vbBlue
.Characters(3, 3).Font.Underline = True
End With
Case left(sArr(3), 3) ' "x B"
With dict(strCond)
.Characters(1, 3).Font.Underline = True
.Characters(1, 1).Font.Color = vbBlue
.Characters(2, 5).Font.Color = vbRed
End With
Case left(sArr(4), 3) ' "B x"
With dict(strCond)
.Characters(3, 5).Font.Underline = True
.Font.Color = vbRed
.Characters(3, 1).Font.Color = vbBlue
End With
Case left(sArr(5), 3) ' "B A"
With dict(strCond)
With .Characters(1, 3).Font
.Color = vbRed
.Underline = True
End With
.Characters(5, 1).Font.Color = vbBlue
End With
End Select
End Sub
Its efficiency will be more visible in big ranges having more occurrences of the same strings (in E:E).
Please, test both versions and send feedback about the efficiency difference.
In order to rapidly create a testing environment, I created the next sub to multiply the existing (shown) testing range. Multiplying it by 500 times, I obtained a range of 3004 rows, which could be processed in about 30 seconds. Changing the format is something consuming time... Using the Union ranges looks to be the single way to make a relatively fast code for such a purpose, I think.
Related
I am pretty new with VBA and trying to write a code to import data from many files in the same folder into a new workbook. I used a dynamic array to update the data but still got the subscript out of range error. I am getting error at Arr(1, 14) = RngInt(2, 1) - Sheets("A").Range("N" & R) when debugged .Can you help me in rectifying the error so that the code may be able to function correctly? Thanks a lot. Here is my code:
Sub CopyDataBetweenWorkbooks()
Dim Arr(), R As Long, FinalRow As Long, x As Integer
Dim wbSource As Workbook
Dim shTarget As Worksheet
Dim shSource As Worksheet
Dim strFilePath As String
Dim strPath As String
' Initialize some variables and ' get the folder path that has the files
Set shTarget = ThisWorkbook.Sheets("A")
strPath = ThisWorkbook.Sheets("A").Range("Path") & "\"
' Make sure a folder was picked.
If Not strPath = vbNullString Then
' Get all the files from the folder
strfile = Dir(strPath)
While strfile <> ""
R = ThisWorkbook.Sheets("A").Range("A" & Rows.Count).End(xlUp).Row + 1
' Open the file and get the source sheet
Set wbSource = Workbooks.Open(strPath & strfile)
'Copy the data
RngInt = wbSource.Sheets("Int").Range("D5:D26")
RngExt = wbSource.Sheets("Ext").Range("D5:D26")
ReDim Arr(1 To 2, 1 To 28)
Arr(1, 1) = wbSource.Name
If RngInt(1, 1) = 0 Then
Arr(1, 2) = RngInt(2, 1)
Else
Arr(1, 2) = RngInt(1, 1)
End If
Arr(1, 3) = RngInt(4, 1)
Arr(1, 4) = RngInt(5, 1)
Arr(1, 5) = RngInt(6, 1)
Arr(1, 6) = RngInt(7, 1)
Arr(1, 7) = RngInt(17, 1) * (-1)
If RngExt(1, 1) = 0 Then
Arr(1, 8) = RngExt(2, 1)
Else
Arr(1, 8) = RngExt(1, 1)
End If
Arr(1, 9) = RngExt(4, 1)
Arr(1, 10) = RngExt(5, 1)
Arr(1, 11) = RngExt(6, 1)
Arr(1, 12) = RngExt(7, 1)
Arr(1, 13) = RngExt(17, 1) * (-1)
Arr(1, 14) = RngInt(2, 1) - Sheets("A").Range("N" & R)
Arr(1, 15) = RngInt(3, 1) - Sheets("A").Range("O" & R)
'And so on, until arr (1,27)
ThisWorkbook.Sheets("A").Range("A" & R).Resize(1, 28) = Arr
'Close the workbook and move to the next file.
wbSource.Close
strfile = Dir$()
Wend
End If
End Sub
Try,
Arr(1, 14) = RngInt(2, 1) - ThisWorkbook.Sheets("A").Range("N" & R)
Arr(1, 15) = RngInt(3, 1) - ThisWorkbook.Sheets("A").Range("O" & R)
In an effort to streamline my efforts for a data transfer macro, I have moved to arrays. I have limited experience with them, let alone vba. But I was able to build it, and have it write. The last hurdle I have is that I am unable to specify which values to write, as I only need them transferred if they are over or under a certain amount; i.e. array(dem2, 6) = >300 write, array(dem2, 6) = <300 don't write.
Long story short, I want to be able to write data for rows where that value is either >300 or <-300.
I spent several hours reading posts, trying various loops and conditions including do, do while, goto, elseif but none seem to work and cause errors to pop up.
Public wbFr As Workbook
Public wbTo As Workbook
Public wsFr As Worksheet
Public wsTo As Worksheet
Option Explicit
Public Sub MoveToLogBookArray()
'==================================
' Macro to put data in array '
' Then write to separate workbook '
'==================================
Dim FromArr() As Variant
Dim Dem1 As Long, Dem2 As Long 'Multi-dimensional
Dim lastRow As Long
Dim LR As Long
Dim i As Long
Dim j As Long
Dim test As Boolean
'==================
'Workbook with data
Set wbFr = Workbooks("Book1useV2.xlsm")
'====================
'Destination workbook
Set wbTo = Workbooks("Book2V2.xlsm")
'============
'Source sheet
Set wsFr = wbFr.Worksheets("test")
'=================
'Destination sheet
Set wsTo = wbTo.Worksheets("Sheet7")
With wsFr 'Attempt at setting bounds of array
Dem1 = Range("A2:A10").Cells.Count - 1 '# of rows
Dem2 = Range("A2:A10").Cells.Count - 3 '# of columns
ReDim FromArr(0 To Dem1, 0 To Dem2)
For Dem1 = LBound(FromArr, 1) To UBound(FromArr, 1)
For Dem2 = LBound(FromArr, 2) To UBound(FromArr, 2)
FromArr(Dem1, Dem2) = wsFr.Range("A2").Offset(Dem1, Dem2)
Next Dem2
Next Dem1
End With
'Successfully builds array
'=========================
'Find last row of destination sheet to go to next empty row
With wsTo
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
Else
lastRow = 1
End If
End With
'================================================================
With wsTo
j = lastRow + 1
For i = LBound(FromArr) To UBound(FromArr)
test = True
If FromArr(Dem2, 6) <= 300 Then test = False 'Inputting .value gives error
'do nothing '"Object required"
If FromArr(Dem2, 6) >= 300 Then test = True
If test = False Then
wsTo.Cells(j, 1) = FromArr(i, 0)
wsTo.Cells(j, 2) = FromArr(i, 1)
wsTo.Cells(j, 3) = FromArr(i, 2)
wsTo.Cells(j, 4) = FromArr(i, 3)
wsTo.Cells(j, 10) = FromArr(i, 4)
wsTo.Cells(j, 13) = FromArr(i, 5)
wsTo.Cells(j, 17) = FromArr(i, 6)
j = j + 1
End If '=======================================================
Next i 'I'm unable to write only values over 300 or under -300]
End With '=======================================================
Exit Sub
End Sub
As mentioned above, I want to be able to specify rows of values input into an array in one workbook, then write them to a separate workbook based on conditions of column 6.
With wsTo 'set break here
j = lastRow + 1
For i = LBound(FromArr) To UBound(FromArr)
test = (FromArr(Dem2, 6) <= -300) And (FromArr(Dem2, 6) >= 300) '= True/False
'tried both false/true and commented out^^^^^
'and flipping <, >
If test = True Then
'do nothing
End If
'When it writes, it writes all values or no values at all
'````````````````````````````````````````````````````````
If test = False Then 'values for 4&5 include decimals
wsTo.Cells(j, 1) = FromArr(i, 0) 'type= variant/double
wsTo.Cells(j, 2) = FromArr(i, 1) 'type= variant/string
wsTo.Cells(j, 3) = FromArr(i, 2) 'type= variant/string
wsTo.Cells(j, 4) = FromArr(i, 3) 'type= variant/string
wsTo.Cells(j, 10) = FromArr(i, 4) 'type= variant/double
wsTo.Cells(j, 13) = FromArr(i, 5) 'type= variant/double
wsTo.Cells(j, 17) = FromArr(i, 6) 'type= variant/double
j = j + 1 'On 4th iteration, value is 285 but is written anyway
End If
'=======================================================
Next i 'I'm unable to write only values over 300 or under -300]
End With '=======================================================
Exit Sub
'Values in test Column
'517.34
'733.68
'312.26
'285.00 writes regardless
'297.00 ""
'312.00
'333.00
'250.00 ""
'500.00
End Sub
And here is my finished working project thanks to Robert and Patrick.
Public wbFr As Workbook
Public wbTo As Workbook
Public wsFr As Worksheet
Public wsTo As Worksheet
Option Explicit
Public Sub MoveToLogBookArray()
'==================================
' Macro to put data in array '
' Then write to separate workbook '
'==================================
Dim FromArr() As Variant
Dim lastRow As Long
Dim i As Long
Dim j As Long
Dim test As Boolean
'==================
'Workbook with data
Set wbFr = Workbooks("Book1useV2.xlsm")
'====================
'Destination workbook
Set wbTo = Workbooks("Book2V2.xlsm")
'============
'Source sheet
Set wsFr = wbFr.Worksheets("test")
'=================
'Destination sheet
Set wsTo = wbTo.Worksheets("Sheet7")
'========================================================================
With wsFr 'Attempt at setting bounds of array
FromArr = Range("adjRange")
End With
With wsTo
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
Else
lastRow = 1
End If
End With
'===================================================================
'Be sure to verify parameters
'Check for errors
On Error GoTo ErrHandler
With wsTo
j = lastRow + 1
For i = LBound(FromArr) To UBound(FromArr)
test = False
If FromArr(i, 6) >= 300 And FromArr(i, 6) <= -300 Then test = False
If FromArr(i, 6) < 299.99 And FromArr(i, 6) > -299.99 Then test = True
If test = False Then
wsTo.Cells(j, 1) = FromArr(i, 1)
wsTo.Cells(j, 2) = FromArr(i, 2)
wsTo.Cells(j, 3) = FromArr(i, 3)
wsTo.Cells(j, 4) = FromArr(i, 4)
wsTo.Cells(j, 10) = FromArr(i, 5)
wsTo.Cells(j, 13) = FromArr(i, 6)
wsTo.Cells(j, 17) = FromArr(i, 7)
j = j + 1
End If
Next i
End With
On Error GoTo 0
'===================================================================
Exit Sub
ErrHandler:
MsgBox "An error has occurred! Please check values are in correct column. " & Err.Description
Exit Sub
End Sub
This should work. You had a typo in the index to the FromArr(). I could not run it, so let me know if it does not work.
With wsTo
j = lastRow + 1
For i = LBound(FromArr, 1) To UBound(FromArr2, 1)
If FromArr(i, 6) <= 300 Then test = False
If FromArr(1, 6) > 300 Then test = True
If test = False Then
wsTo.Cells(j, 1) = FromArr(i, 0)
wsTo.Cells(j, 2) = FromArr(i, 1)
wsTo.Cells(j, 3) = FromArr(i, 2)
wsTo.Cells(j, 4) = FromArr(i, 3)
wsTo.Cells(j, 10) = FromArr(i, 4)
wsTo.Cells(j, 13) = FromArr(i, 5)
wsTo.Cells(j, 17) = FromArr(i, 6)
j = j + 1
End If
Next i
End With
I want to alphabetically sort a 2-dimensional array results(lcol, 4) with VBA. This array contains 4 columns and variable number of rows, based on the values of the last column.
This is the code of how I populated the array :
ReDim results(lcol, 4)
For i = 1 To lcol
results(i, 1) = ThisWorkbook.Sheets(2).Range("B1").Offset(, i - 1).Value
results(i, 2) = "0"
results(i, 3) = ThisWorkbook.Sheets(3).Range("C2").Offset(i - 1, 0).Value
Next i
For Each of In ThisWorkbook.Sheets(1).Range("A1:C" & lrow2)
Set modele = of.Offset(, 1)
Set qte = of.Offset(, 2)
For Each modele2 In ThisWorkbook.Sheets(2).Range("A2:A481")
If modele2.Value = modele.Value Then
For i = 1 To lcol 'à modifier
results(i, 2) = results(i, 2) + qte.Value * modele2.Offset(, i).Value
If results(i, 2) <= results(i, 3) Then
results(i, 4) = "OK"
Else
results(i, 4) = "Rupture"
End If
Next i
Exit For
End If
Next modele2
Next of
This provides a basic (quiksort?) ascending sort on your populated array with the last column as the primary key.
dim i as long, j as long, tmp as variant
redim tmp(lbound(results, 1) to lbound(results, 1), lbound(results, 2) to ubound(results, 2))
for i = lbound(results, 1) to ubound(results, 1) - 1
if results(i, ubound(results, 2)) > results(i+1, ubound(results, 2)) or _
results(i, ubound(results, 2)) = vbnullstring then
for j = lbound(results, 2) to ubound(results, 2)
tmp(lbound(results, 1), j) = results(i, j)
next j
for j = lbound(results, 2) to ubound(results, 2)
results(i, j) = results(i+1, j)
next j
for j = lbound(results, 2) to ubound(results, 2)
results(i+1, j) = tmp(lbound(results, 1), j)
next j
end if
next i
Sorry for all the lbound and ubound but I had no idea if your array was zero-based of 1-based. The For i = 1 To lcol was not definitive. All evidence points to your arr being zero-based.
You could have SortedList object do the work
Assuming your results array is 1-based and with 4 columns, you could try the following code (UNTESTED):
Sub SortArray(results As Variant)
Dim i As Long, j As Long
With CreateObject("System.Collections.SortedList")
For i = 1 to UBound(results)
.Add results(i,4), Application.Index(result,i,0)
Next
For i = 1 To .Count
For j = 1 To 4
results(i, j) = .GetByIndex(i)(j)
Next
Next
End With
End Sub
Which you would call in your “main” sub as follows:
SortArray results
I have written a VBA Macro which works, but takes too long because the database is also very big.
I know this can be optimized via Arrays, but I am not sure how to make it.
Could someone help me please?
'Identify how many rows are in the file
finalrow = ActiveSheet.Cells(Rows.Count, 6).End(xlUp).Row
'fill the empty fields which requires the part number and description
For i = 2 To finalrow
If Cells(i, 3) = 0 Or Cells(i, 3) = "------------" Or Cells(i, 3) = "e" Or Cells(i, 3) = "111)" Or Cells(i, 3) = "ion" Then
If Cells(i, 4) = 0 Or Cells(i, 4) = "-----------" Or Cells(i, 4) = "Location" Then
Range("A" & i & ":H" & i).Select
Selection.Delete Shift:=xlUp
i = i - 1
Else
For j = 1 To 3
Cells(i, j) = Cells(i - 1, j)
Next
End If
End If
If Cells(i, 1) = 0 Then
Cells(i, 1) = Cells(i - 1, 1)
End If
If Cells(i, 4) = 0 Then
Range("A" & i & ":H" & i).Select
Selection.Delete Shift:=xlUp
i = i - 1
End If
Count = Count + 1
If Count = finalrow Then
i = finalrow
End If
Next
I combined your code with my answer to excel Delete rows from table Macro based on criteria, that I just finished posting. It is super fast. Please check out my other answer for details.
You may need to adjust the Target range. If your data starts in A1 and does not have any completely blank rows than it should work.
Sub DeleteRows()
Dim Start: Start = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Const PreserveFormulas As Boolean = True
Dim Target As Range
Dim DeleteRow As Boolean
Dim Data, Formulas, NewData
Dim pos As Long, x As Long, y As Long
Set Target = Range("A1").CurrentRegion
Data = Target.Value
If PreserveFormulas Then Formulas = Target.Formula
ReDim NewData(1 To UBound(Data, 1), 1 To UBound(Data, 2))
For x = 2 To UBound(Data, 1)
DeleteRow = True
If Data(x, 3) = 0 Or Data(x, 3) = "------------" Or Data(x, 3) = "e" Or Data(x, 3) = "111)" Or Data(x, 3) = "ion" Then
If Data(x, 4) = 0 Or Data(x, 4) = "-----------" Or Data(x, 4) = "Location" Then
DeleteRow = False
End If
End If
If Data(x, 4) = 0 Or Data(x, 4) = "-----------" Or Data(x, 4) = "Location" Then DeleteRow = False
If Not DeleteRow Then
pos = pos + 1
For y = 1 To UBound(Data, 2)
If PreserveFormulas Then
NewData(pos, y) = Formulas(x, y)
Else
NewData(pos, y) = Data(x, y)
End If
Next
End If
Next
Target.Formula = NewData
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Debug.Print "Execution Time: "; Timer - Start; " Second(s)"
End Sub
I'd start simply with this:
'Identify how many rows are in the file
finalrow = ActiveSheet.Cells(Rows.Count, 6).End(xlUp).Row
'fill the empty fields which requires the part number and description
For i = 2 To finalrow
Set ci3 = Cells(i, 3)
If ci3 = 0 Or ci3 = "------------" Or ci3 = "e" Or ci3 = "111)" Or ci3 = "ion" Then
Set ci4 = Cells(i, 4)
If ci4 = 0 Or ci4 = "-----------" Or ci4 = "Location" Then
Range("A" & i & ":H" & i).Select
Selection.Delete Shift:=xlUp
i = i - 1
Else
For j = 1 To 3
Cells(i, j) = Cells(i - 1, j)
Next
End If
End If
If Cells(i, 1) = 0 Then
Cells(i, 1) = Cells(i - 1, 1)
End If
If Cells(i, 4) = 0 Then
Range("A" & i & ":H" & i).Select
Selection.Delete Shift:=xlUp
i = i - 1
End If
Count = Count + 1
If Count = finalrow Then
i = finalrow
End If
Next
first of all, i'd like to say, i've sarched thorugh the net, but i haven't run into such a thing. i've seen collection of collections, or array of arrays, but not a collection of array.
what i want to do is, to collect ID's in collections for each District. Finally, i will join the values in the collections with Join function and ";" as delimiter, and then print them in a range of 4 column as a lookup list, for each class. For example;
Class2(0) will include 54020 and 30734, class2(1) will include 58618, class1(4) will include none, class3(7) will include 35516,34781 and 56874, and so on.
i want to loop through column C and put a select case statment to check the class and then assign the values to collections
Sub dict_coll()
Dim class1() As New Collection
Dim class2() As New Collection
Dim class3() As New Collection
Dim class4() As New Collection
Dim dict As New Scripting.Dictionary
Set dRange = range(range("a2"), range("a2").End(xlDown))
i = 0
For Each d In dRange
If Not dict.Exists(d.Value) Then
dict.Add key:=d.Value, item:=i
i = i + 1
End If
Next d
Set cRange = range(range("c2"), range("c2").End(xlDown))
For Each c In cRange
Select Case c.Value
Case "class1"
class1(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
Case "class2"
class2(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
Case "class3"
class3(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
Case Else
class4(dict(c.offset(0,-2).value)).Add c.Offset(0, 1).Value 'fails here
End Select
Next c
End Sub
and what i want to see is as foloowing:
is there any easier and proper way of what i wanna do? any help wil be appreciated.
thanks
I didnt see that sb variable defined in your code.
Anyway, for me I see a case of straightforward arrays: There is fixed dimension of classes so it good enough for me. Furthermore, you can print back to worksheet so easily.
Public Sub test()
Const strPrefix = "class"
Dim districtRange As Range, outputRange As Range, r As Range
Dim arr() As String
Dim i As Long, j As Long, x As Long, y As Long
Dim district As String, str As String, idVal As String
Dim arr2 As Variant
Application.ScreenUpdating = False
ReDim arr(1 To 5, 1 To 1)
arr(1, 1) = "District"
arr(2, 1) = "Class 1"
arr(3, 1) = "Class 2"
arr(4, 1) = "Class 3"
arr(5, 1) = "Class 4"
Set districtRange = Range(Range("A2"), Range("C2").End(xlDown))
arr2 = districtRange.Value
For x = LBound(arr2, 1) To UBound(arr2, 1)
district = arr2(x, 1)
i = Val(Mid(arr2(x, 3), Len(strPrefix) + 1))
idVal = arr2(x, 2)
j = inArray(arr, district, 1) 'returns -1 if not found
If j >= 0 Then
arr(i + 1, j) = IIf(arr(i + 1, j) = "", idVal, arr(i + 1, j) & ";" & idVal)
Else
ReDim Preserve arr(1 To 5, 1 To UBound(arr, 2) + 1)
arr(1, UBound(arr, 2)) = district
arr(i + 1, UBound(arr, 2)) = idVal
End If
Next x
Set outputRange = Range("E1")
outputRange.Resize(UBound(arr, 2), UBound(arr, 1)).Value = Application.Transpose(arr)
outputRange.Sort Key1:=Range("E1"), Header:=xlYes, Order1:=xlAscending
Application.ScreenUpdating = True
End Sub
Public Function inArray(arr As Variant, k As String, Optional rowNum As Long, Optional colNum As Long) As Long
Dim i As Long, j As Long
inArray = -1
If rowNum Then
For i = LBound(arr, 2) To UBound(arr, 2)
If arr(rowNum, i) = k Then
inArray = i
Exit Function
End If
Next i
Else
For i = LBound(arr, 1) To UBound(arr, 1)
If arr(i, colNum) = k Then
inArray = i
Exit Function
End If
Next i
End If
End Function
by the way, i've found another solution, usinf both dictionary and 3-dimension array.
Sub test()
Dim Blg As New Scripting.Dictionary
Dim Sgm As New Scripting.Dictionary
Dim Siciller() As String
ReDim Siciller(0 To 23, 0 To 3, 0 To 5)
Set alanBolge = range(range("a2"), range("a2").End(xlDown))
Set alanSegment = range(range("c2"), range("c2").End(xlDown))
i = 0
For Each d In alanBolge
If Not Blg.Exists(d.Value) Then
Blg.Add Key:=d.Value, item:=i
i = i + 1
End If
Next d
k = 0
For Each d In alanSegment
If Not Sgm.Exists(d.Value) Then
Sgm.Add Key:=d.Value, item:=k
k = k + 1
End If
Next d
'data reading
For Each d In alanBolge
Siciller(Blg(d.Value), Sgm(d.Offset(0, 2).Value), dolusay(Siciller, Blg(d.Value), Sgm(d.Offset(0, 2).Value)) + 1) = d.Offset(0, 1).Value
Next d
'output
For x = 1 To 4
For y = 1 To 24
Set h = Cells(1 + y, 5 + x)
h.Select
h.Value = sonucgetir(Siciller, Blg(h.Offset(0, -x).Value), Sgm(h.Offset(-y, 0).Value))
Next y
Next x
End Sub
Public Function dolusay(ByVal data As Variant, ByVal i1 As Integer, ByVal i2 As Integer) As Integer
Dim count As Integer
count = 0
For j = 0 To UBound(data, 3) - 1
If Len(data(i1, i2, j)) > 0 Then
count = count + 1
End If
Next
dolusay = count
End Function
Public Function sonucgetir(ByVal data As Variant, ByVal i1 As Integer, ByVal i2 As Integer) As String
sonucgetir = ""
For i = 0 To UBound(data, 3)
If Len(data(i1, i2, i)) > 0 Then
x = data(i1, i2, i) & ";" & x
sonucgetir = Left(x, Len(x) - 1)
End If
Next i
End Function