VBA Code optimizing [closed] - vba

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have the below code, but is very slow. Is there a way to improve it? I'm a beginner with VBA and would appreciate your help. What it does is it goes through a table and looks up in each worksheet for criteria to match and give values accordingly. Criteria differ by line in the initial range:
Sub TAB_REF_SETUP()
Dim TC As Integer
Dim TR As Integer
Dim C As Integer
Dim C2 As Integer
Dim R As Integer
Dim R2 As Integer
Dim TC2 As Integer
Dim TR2 As Integer
Dim CELL2 As Range
Dim CELL As Range
Dim RNG2 As Range
Dim RNG As Range
Dim WKS As Worksheet
Dim a As String
Dim xrow As Integer
Dim ycol As Integer
Dim CEllrow As Integer
Dim cellcol As Integer
Dim mincol As Integer
Dim mfrcol As Integer
Dim schrefc As Integer
Dim RBC As Integer
Dim RTC As Integer
Dim b As String
Dim CPC As Integer
Dim D As String
Dim AR As String
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'Application.ScreenUpdating = False
Application.AutoCorrect.AutoFillFormulasInLists = False
Application.CellDragAndDrop = False
Application.Calculation = xlCalculationManual
If ActiveSheet.AutoFilterMode = True Then
ActiveSheet.ShowAllData
Else
End If
C = Range("1:1").Find("Dist Classification").Column
If Range("1:1").Find("Schedule A Ref") Is Nothing Then
Columns(C + 1).Insert
Columns(C + 2).Insert
Columns(C + 3).Insert
Cells(1, C + 1).Value = "Schedule A Ref"
Cells(1, C + 2).Value = "Contract Name"
Cells(1, C + 3).Value = "Lookup Value"
schrefc = Range("1:1").Find("Schedule A Ref").Column
GoTo CellFill
Else
schrefc = Range("1:1").Find("Schedule A Ref").Column
If MsgBox("Ref Tab Exists. Do you want to proceed with further check?", vbYesNo, "Perform Further Check") = vbYes Then
If MsgBox("This will re-write column ""Schedule A Ref"". Do you wish to continue ?", vbYesNo, "Are you sure?") = vbYes Then
CellFill:
TC = Range("A1").End(xlToRight).Column
TR = Range("A1").End(xlDown).Row
Cells(1, TC + 1) = "Applicable Rebate"
Cells(1, TC + 2) = "Applicable Rebate Type"
Cells(1, TC + 3) = "Applicable Contract Price"
Cells(1, TC + 4) = "Actual Rebate $ for Line"
Cells(1, TC + 5) = "Rebate Owed"
Set RNG = Range(Cells(2, schrefc), Cells(Range("a1").End(xlDown).Row, schrefc))
mincol = Range("1:1").Find("MIN").Column
mfrcol = ActiveSheet.Range("1:1").Find("Mfr Name").Column
For Each CELL In RNG
CEllrow = CELL.Row
For Each WKS In Worksheets
If Not WKS.Range("1:1").Find("Schedule") Is Nothing And Not WKS.Range("1:3").Find(Cells(CEllrow, mfrcol)) Is Nothing And (InStr(1, WKS.Name, "fort", vbTextCompare) = 0 And InStr(1, WKS.Name, "report", vbTextCompare) = 0 And InStr(1, WKS.Name, "data", vbTextCompare) = 0) Then
C2 = WKS.Range("1:5").Find("Contract Name").Column
R2 = WKS.Range("1:5").Find("Contract Name").Row
TR2 = WKS.Range("1:5").Find("Contract Name").End(xlDown).Row
TC2 = C2
Set RNG2 = WKS.Range(WKS.Cells(R2 + 1, C2), WKS.Cells(TR2, C2))
xrow = WKS.Range("1:5").Find("SCC&Tab").Row
ycol = WKS.Range("1:5").Find("SCC&Tab").Column
RBC = WKS.Range("1:5").Find("Applicable Rebate").Column
RTC = WKS.Range("1:5").Find("Applicable Rebate Type").Column
CPC = WKS.Range("1:5").Find("Applicable Contract Price").Column
a = "=iferror(vlookup([#[Lookup Value]],indirect([#[Schedule A Ref]])," & RBC & ",false),iferror(vlookup([#[Dist Mfr. Item ID]]&[#[Contract Name]],indirect([#[Schedule A Ref]])," & RBC & ",false),""""))"
b = "=iferror(vlookup([#[Lookup Value]],indirect([#[Schedule A Ref]])," & RTC & ",false),iferror(vlookup([#[Dist Mfr. Item ID]]&[#[Contract Name]],indirect([#[Schedule A Ref]])," & RTC & ",false),""""))"
D = "=iferror(vlookup([#[Lookup Value]],indirect([#[Schedule A Ref]])," & CPC & ",false),iferror(vlookup([#[Dist Mfr. Item ID]]&[#[Contract Name]],indirect([#[Schedule A Ref]])," & CPC & ",false),""""))"
For Each CELL2 In RNG2
If InStr(1, CELL2, Cells(CEllrow, C), vbTextCompare) > 0 Then
Filler:
CELL.Value = "''" & WKS.Name & "'!" & WKS.Cells(xrow, ycol).Address & ":" & Cells(RNG2.End(xlDown).Row, RNG2.End(xlUp).End(xlToRight).Column).Address
Cells(CEllrow, C + 2).Value = CELL2
Cells(CEllrow, C + 3).Value = "=[#[Min]]&[#[Contract Name]]"
Cells(CEllrow, TC + 1) = a
Cells(CEllrow, TC + 2) = b
Cells(CEllrow, TC + 3) = D
If Cells(CEllrow, TC + 2).Value = "%D" Then
AR = "=[#[Applicable Rebate]]*[#[Applicable Contract Price]]*[#[case qty]]"
ElseIf Cells(CEllrow, TC + 2).Value = "$" Then
AR = "=[#[Applicable Rebate]]*[#[case qty]]"
ElseIf Cells(CEllrow, TC + 2).Value = "%P" Then
AR = "=[#[Applicable Rebate]]*[#[Total Vol]]"
Else
AR = "0"
End If
Cells(CEllrow, TC + 4) = AR
Cells(CEllrow, TC + 5) = "=[#[Actual Rebate $ for Line]]-[#[Committed - Rebate]]"
ElseIf InStr(1, CELL2, "nat", vbTextCompare) > 0 Then
GoTo Filler:
Else
End If
Next
Else
End If
Next
Next
Else
Exit Sub
End If
Else
Exit Sub
End If
End If
Application.AutoCorrect.AutoFillFormulasInLists = True
Application.Calculation = xlCalculationAutomatic
Application.CellDragAndDrop = True
Application.ScreenUpdating = True
SecondsElapsed = Round(Timer - StartTime, 2)
'Notify user in seconds
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub

Must do:
Uncomment from the top this:
Application.ScreenUpdating = False
A good idea to do:
Change all integer to long
Rewrite it in a way that you do not use goto statements. Install this -> http://www.oaltd.co.uk/indenter/indentpage.asp and indent. Or as mentioned in the comments, use the RubberDuck indenter.

The slowest part seems to be looping through cells. Use this instead:
Dim vData as Variant
Dim arrayIndex1 as Long, arrayIndex2 as Long
vData = Range(Cells(2, schrefc), Cells(Range("a1").End(xlDown).Row, schrefc))
For arrayIndex1 = lbound(vData) to ubound(vData)
For arrayIndex2 = lbound(vData,2) to ubound(vData,2)
'vData(arrayIndex1,arrayIndex2)
Next arrayIndex2
Next arrayIndex1
vData(arrayIndex1,arrayIndex2) is array counterpart of cells(row,col). By default arrays start from 0, so first arrayIndex1 will equal 0. To change default value to 1, use Option Base 1 at the top of the code.
Use With statement for multiple identical objects for better code clarity - and when inside a loop, also performance, for example instead of:
xrow = WKS.Range("1:5").Find("SCC&Tab").Row
ycol = WKS.Range("1:5").Find("SCC&Tab").Column
RBC = WKS.Range("1:5").Find("Applicable Rebate").Column
RTC = WKS.Range("1:5").Find("Applicable Rebate Type").Column
CPC = WKS.Range("1:5").Find("Applicable Contract Price").Column
use:
With WKS.Range("1:5")
xrow = .Find("SCC&Tab").Row
ycol = .Find("SCC&Tab").Column
RBC = .Find("Applicable Rebate").Column
RTC = .Find("Applicable Rebate Type").Column
CPC = .Find("Applicable Contract Price").Column
End With
Also try declaring variables like Dim TC As Long, TR As Long, C as Long so that declarations are not half of code's lines. Operating system converts integer to long anyway, so don't use integers. Use for example Cells(CEllrow, C).value instead of Cells(CEllrow, C).

Related

VBA code Adding a cell contains date and a cell contains a number, getting mismatch error

Hi I am Trying to add to cells together and compare them against another cell but I get a type mismatch.
first cell is a date, the one being added is a number"as in number of days" and the third one being compared is a date also.
but I get type mismatch.
my code is below
Sub Macro1()
Macro1 Macro
Dim wks As Worksheet
Set wks = ActiveSheet
Dim x As Integer
Dim p As Integer
Dim rowRange As Range
Dim colRange As Range
Dim LastCol As Long
Dim LastRow As Long
LastRow = wks.Cells(wks.Rows.Count, "A").End(xlUp).Row
Set rowRange = wks.Range("A1:A" & LastRow)
For i = 7 To 189
p = 0
For q = 8 To LastRow
If [aq] = [si] Then
If [cq] + [ui] >= [xi] Then
[oq] = 1
Else
p = p + [dq]
[qq] = 0
End If
End If
Next q
Next i
End Sub
[cq] is a cell that contains date
[ui] is a cell that contains number
[xi] is a cell that contains date
Try it as cells(q, "A") = cells(i, "S").
For i = 7 To 189
p = 0
For q = 8 To LastRow
'If [aq] = [si] Then
If cells(q, "A") = cells(i, "S") Then
'If [cq] + [ui] >= [xi] Then
If cells(q, "C") + cells(i, "U") >= cells(i, "X") Then
'[oq] = 1
cells(q, "O") = 1
Else
'p = p + [dq]
p = p + cells(q, "D")
'[qq] = 0
cells(q, "Q") = 0
End If
End If
Next q
Next i
You need to use the "DateAdd" function. Instructions here: https://www.techonthenet.com/excel/formulas/dateadd.php
Example:
Sub add_dates()
Dim dateOne As Date
Dim dateTwo As Date
Dim lngDays As Long
dateOne = "1/1/2018"
lngDays = 2
dateTwo = "1/3/2018"
Dim result As Boolean
If DateAdd("d", lngDays, dateOne) >= dateTwo Then
MsgBox ("Greater than or equal to")
Else
MsgBox ("Less than")
End If
End Sub

Speed Up Matching program in Excel VBA

I am writing a VBA code on excel using loops to go through 10000+ lines.
Here is an example of the table
And here is the code I wrote :
Sub Find_Matches()
Dim wb As Workbook
Dim xrow As Long
Set wb = ActiveWorkbook
wb.Worksheets("Data").Activate
tCnt = Sheets("Data").UsedRange.Rows.Count
Dim e, f, a, j, h As Range
xrow = 2
Application.ScreenUpdating = False
Application.Calculation = xlManual
For xrow = 2 To tCnt Step 1
Set e = Range("E" & xrow)
Set f = e.Offset(0, 1)
Set a = e.Offset(0, -4)
Set j = e.Offset(0, 5)
Set h = e.Offset(0, 3)
For Each Cell In Range("E2:E" & tCnt)
If Cell.Value = e.Value Then
If Cell.Offset(0, 1).Value = f.Value Then
If Cell.Offset(0, -4).Value = a.Value Then
If Cell.Offset(0, 5).Value = j.Value Then
If Cell.Offset(0, 3).Value = h.Value Then
If (e.Offset(0, 7).Value) + (Cell.Offset(0, 7).Value) = 0 Then
Cell.EntireRow.Interior.Color = vbYellow
e.EntireRow.Interior.Color = vbYellow
End If
End If
End If
End If
End If
End If
Next
Next
End Sub
As you can imagine, this is taking a lot of time to go through 10000+ lines and I would like to find a faster solution. There must be a method I don't think to avoid the over looping
Here are the condition :
For each line, if another line anywhere in the file has the exact same
:
Buyer ID (col. E)
`# purchased (col. F)
Product ID (col.A)
Payment (col. J)
Date purchased (col. H)
Then, if the SUM of the Amount (col. L) the those two matching line is
0, then color both rows in yellow.
Note that extra columns are present and not being compared (eg- col. B) but are still important for the document and cannot be deleted to ease the process.
Running the previous code, in my example, row 2 & 5 get highlighted :
This is using nested dictionaries and arrays to check all conditions
Timer with my test data: Rows: 100,001; Dupes: 70,000 - Time: 14.217 sec
Option Explicit
Public Sub FindMatches()
Const E = 5, F = 6, A = 1, J = 10, H = 8, L = 12
Dim ur As Range, x As Variant, ub As Long, d As Object, found As Object
Set ur = ThisWorkbook.Worksheets("Data").UsedRange
x = ur
Set d = CreateObject("Scripting.Dictionary")
Set found = CreateObject("Scripting.Dictionary")
Dim r As Long, rId As String, itm As Variant, dupeRows As Object
For r = ur.Row To ur.Rows.Count
rId = x(r, E) & x(r, F) & x(r, A) & x(r, J) & x(r, H)
If Not d.Exists(rId) Then
Set dupeRows = CreateObject("Scripting.Dictionary")
dupeRows(r) = 0
Set d(rId) = dupeRows
Else
For Each itm In d(rId)
If x(r, L) + x(itm, L) = 0 Then
found(r) = 0
found(itm) = 0
End If
Next
End If
Next
Application.ScreenUpdating = False
For Each itm In found
ur.Range("A" & itm).EntireRow.Interior.Color = vbYellow
Next
Application.ScreenUpdating = True
End Sub
Before
After
I suggest a different approach altogether: add a temporary column to your data that contains a concatenation of each cell in the row. This way, you have:
A|B|C|D|E
1|Mr. Smith|500|A|1Mr. Smith500A
Then use Excel's conditional formatting on the temporary column, highlighting duplicate values. There you have your duplicated rows. Now it's only a matter of using a filter to check which ones have amounts equal to zero.
You can use the CONCATENATE function; it requires you to specify each cell separately and you can't use a range, but in your case (comparing only some of the columns) it seems like a good fit.
Maciej's answer is easy to implement (if you can add columns to your data without interrupting anything), and I would recommend it if possible.
However, for the sake of answering your question, I will contribute a VBA solution as well. I tested it on dataset that is a bit smaller than yours, but I think it will work for you. Note that you might have to tweak it a little (which row you start on, table name, etc) to fit your workbook.
Most notably, the segment commented with "Helper column" is something you most likely will have to adjust - currently, it compares every cell between A and H for the current row, which is something you may or may not want.
I've tried to include a little commentary in the code, but it's not much. The primary change is that I'm using in-memory processing of an array rather than iterating over a worksheet range (which for larger datasets should be exponentially faster).
Option Base 1
Option Explicit
' Uses ref Microsoft Scripting Runtime
Sub Find_Matches()
Dim wb As Workbook, ws As Worksheet
Dim xrow As Long, tCnt As Long
Dim e As Range, f As Range, a As Range, j As Range, h As Range
Dim sheetArr() As Variant, arr() As Variant
Dim colorTheseYellow As New Dictionary, colorResults() As String, dictItem As Variant
Dim arrSize As Long, i As Long, k As Long
Dim c As Variant
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Data")
ws.Activate
tCnt = ws.UsedRange.Rows.Count
xrow = 2
Application.ScreenUpdating = False
Application.Calculation = xlManual
' Read range into an array so we process in-memory
sheetArr = ws.Range("A2:H" & tCnt)
arrSize = UBound(sheetArr, 1)
' Build new arr with "helper column"
ReDim arr(1 To arrSize, 1 To 9)
For i = 1 To arrSize
For k = 1 To 8
arr(i, k) = sheetArr(i, k)
arr(i, 9) = CStr(arr(i, 9)) & CStr(arr(i, k)) ' "Helper column"
Next k
Next i
' Iterate over array & build collection to indicate yellow lines
For i = LBound(arr, 1) To UBound(arr, 1)
If Not colorTheseYellow.Exists(i) Then colorResults = Split(ReturnLines(arr(i, 9), arr), ";")
For Each c In colorResults
If Not colorTheseYellow.Exists(CLng(c)) Then colorTheseYellow.Add CLng(c), CLng(c)
Next c
Next i
' Enact row colors
For Each dictItem In colorTheseYellow
'Debug.Print "dict: "; dictItem
If dictItem <> 0 Then ws.ListObjects(1).ListRows(CLng(dictItem)).Range.Interior.Color = vbYellow
Next dictItem
End Sub
Function ReturnLines(ByVal s As String, ByRef arr() As Variant) As String
' Returns a "Index;Index" string indicating the index/indices where the second, third, etc. instance(s) of s was found
' Returns "0;0" if 1 or fewer matches
Dim i As Long
Dim j As Long
Dim tmp As String
ReturnLines = 0
j = 0
tmp = "0"
'Debug.Print "arg: " & s
For i = LBound(arr, 1) To UBound(arr, 1)
If arr(i, 9) = s Then
j = j + 1
'Debug.Print "arr: " & arr(i, 9)
'Debug.Print "ReturnLine: " & i
tmp = tmp & ";" & CStr(i)
End If
Next i
'If Left(tmp, 1) = ";" Then tmp = Mid(tmp, 2, Len(tmp) - 1)
'Debug.Print "tmp: " & tmp
If j >= 2 Then
ReturnLines = tmp
Else
ReturnLines = "0;0"
End If
End Function
On my simple dataset, it yields this result (marked excellently with freehand-drawn color indicators):
Thanks everybody for your answers,
Paul Bica's solution actually worked and I am using a version of this code now.
But, just to animate the debate, I think I also found another way around my first code, inspired by Maciej's idea of concatenating the cells and using CStr to compare the values and, of course Vegard's in-memory processing by using arrays instead of going through the workbook :
Sub Find_MatchesStr()
Dim AmountArr(300) As Variant
Dim rowArr(300) As Variant
Dim ws As Worksheet
Dim wb As Workbook
Set ws = ThisWorkbook.Sheets("Data")
ws.Activate
Range("A1").Select
rCnt = ws.Cells.SpecialCells(xlCellTypeLastCell).Row
For i = 2 To rCnt
If i = rCnt Then
Exit For
Else
intCnt = 0
strA = ws.Cells(i, 1).Value
strE = ws.Cells(i, 5).Value
strF = ws.Cells(i, 6).Value
strH = ws.Cells(i, 8).Value
strL = ws.Cells(i, 10).Value
For j = i To rCnt - 1
strSearchA = ws.Cells(j, 1).Value
strSearchE = ws.Cells(j, 5).Value
strSearchF = ws.Cells(j, 6).Value
strSearchH = ws.Cells(j, 8).Value
strSearchL = ws.Cells(j, 10).Value
If CStr(strE) = CStr(strSearchE) And CStr(strA) = CStr(strSearchA) And CStr(strF) = CStr(strSearchF) And CStr(strH) = CStr(strSearchH) And CStr(strL) = CStr(strSearchL) Then
AmountArr(k) = ws.Cells(j, 12).Value
rowArr(k) = j
intCnt = intCnt + 1
k = k + 1
Else
Exit For
End If
Next
strSum = 0
For s = 0 To UBound(AmountArr)
If AmountArr(s) <> "" Then
strSum = strSum + AmountArr(s)
Else
Exit For
End If
Next
strAppenRow = ""
For b = 0 To UBound(rowArr)
If rowArr(b) <> "" Then
strAppenRow = strAppenRow & "" & rowArr(b) & "," & AmountArr(b) & ","
Else
Exit For
End If
Next
If intCnt = 1 Then
Else
If strSum = 0 Then
For rn = 0 To UBound(rowArr)
If rowArr(rn) <> "" Then
Let rRange = rowArr(rn) & ":" & rowArr(rn)
Rows(rRange).Select
Selection.Interior.Color = vbYellow
Else
Exit For
End If
Next
Else
strvar = ""
strvar = Split(strAppenRow, ",")
For ik = 1 To UBound(strvar)
If strvar(ik) <> "" Then
strVal = CDbl(strvar(ik))
For ik1 = ik To UBound(strvar)
If strvar(ik1) <> "" Then
strVal1 = CDbl(strvar(ik1))
If strVal1 + strVal = 0 Then
Let sRange1 = strvar(ik - 1) & ":" & strvar(ik - 1)
Rows(sRange1).Select
Selection.Interior.Color = vbYellow
Let sRange = strvar(ik1 - 1) & ":" & strvar(ik1 - 1)
Rows(sRange).Select
Selection.Interior.Color = vbYellow
End If
Else
Exit For
End If
ik1 = ik1 + 1
Next
Else
Exit For
End If
ik = ik + 1
Next
End If
End If
i = i + (intCnt - 1)
k = 0
Erase AmountArr
Erase rowArr
End If
Next
Range("A1").Select
End Sub
I still have some mistakes (rows not higlighted when they should be), the above code is not perfect, but I thought it'd be OK to give you an idea of where I was going before Paul Bica's solution came in.
Thanks again !
If your data is only till column L, then use below code, I found it is taking less time to run....
Sub Duplicates()
Application.ScreenUpdating = False
Dim i As Long, lrow As Long
lrow = Cells(Rows.Count, 1).End(xlUp).Row
Range("O2") = "=A2&E2&F2&J2&L2"
Range("P2") = "=COUNTIF(O:O,O2)"
Range("O2:P" & lrow).FillDown
Range("O2:O" & lrow).Copy
Range("O2:O" & lrow).PasteSpecial xlPasteValues
Application.CutCopyMode = False
For i = 1 To lrow
If Cells(i, 16) = 2 Then
Cells(i, 16).EntireRow.Interior.Color = vbYellow
End If
Next
Application.ScreenUpdating = True
Range("O:P").Delete
Range("A1").Select
MsgBox "Done"
End Sub

2 codes together with vba ? for each statement too big

I need to to complete a task for my work. I am new to VBA-Excel so I am kind of stuck. This is also my first post so i am sorry in advance.
As you can see, this is a code I made so that i can get my invoices from a list. There are still some things missing like formatting all of it. But for me the most important part is to combine another worksheet with this code like the same exact code. I need a loop that does me 1st this code and then the second code which is similar.
Something like:
For each ID that is the same of the 2 lists do me a pdf file with all the invoices and all the sums.
The problem is that i get lost in all the coding because i have the feeling my for each statement is getting like 3 pages long which cannot be correct i assume.
My code as is:
Sub Schleife()
Dim Zeile As Long
Dim ZeileMax As Long
Dim n As Long
Dim m As Long
Dim a As Long
Dim strSpalte As String
Dim strSpalte1 As String
Dim strBereich As String
Dim L As Long
Dim R As Long
Dim AR As Range
Dim Le As Long
Dim i As Long
Dim arrBlätter() As String
Dim leereZelle As Long
Dim strSpalte2 As String
Dim strSpalte3 As String
Dim strBereich2 As String
'Worksheets
Dim dl As Worksheet: Set dl = ActiveWorkbook.Sheets("DatenLadevorgänge")
Dim lv As Worksheet: Set lv = ActiveWorkbook.Sheets("Ladevorgänge")
Dim üb As Worksheet: Set üb = ActiveWorkbook.Sheets("Übersicht")
Dim de As Worksheet: Set de = ActiveWorkbook.Sheets("DatenERoaming")
Dim ge As Worksheet: Set ge = ActiveWorkbook.Sheets("Geräte")
Dim ch As Worksheet: Set ch = ActiveWorkbook.Sheets("Chips")
Dim eR As Worksheet: Set eR = ActiveWorkbook.Sheets("eRoaming")
Dim mv As Worksheet: Set mv = ActiveWorkbook.Sheets("MEC-Verträge")
Dim lastrow As Long
Application.ScreenUpdating = True
leereZelle = Columns(11).Find(What:="", Lookat:=xlWhole, Searchdirection:=xlNext).Row
With Tabelle1
If .Cells(leereZelle, 11) = "" Then üb.Cells(1, 1).Value = mv.Cells(leereZelle, 1).Value
End With
lv.Select
lv.Range("A10:O100000").ClearContents
üb.Range("A53:M100000").ClearContents
With dl
ZeileMax = .UsedRange.Rows.Count 'Fkt zur Aufuschung aller SmartCables'
n = 10
For Zeile = 2 To ZeileMax
If .Cells(Zeile, 13).Value = lv.Range("A1").Value Then
.Range(dl.Cells(Zeile, 2), dl.Cells(Zeile, 12)).Copy _
Destination:=lv.Range(lv.Cells(n, 2), lv.Cells(n, 12))
n = n + 1
End If
Next Zeile
lastrow = Cells(Rows.Count, 2).End(xlUp).Row
'Sortieren
.Range("B10:L" & lastrow).Sort Key1:=.Range("B10:B" & lastrow), _
Order1:=xlAscending, Key2:=.Range("C10:C" & lastrow), Order2:=xlAscending
dl.Range("B10", dl.Range(dl.Cells(10, 2), dl.Cells(n, 13))).RemoveDuplicates Columns:=Array(1, 2), Header:=xlNo
End With
L = Range("B65000").End(xlUp).Row
strBereich = "A10:O" & L
strSpalte = "B"
strSpalte1 = "O"
If Range("B10") = "" Then
Cells(10, 2).Value = "Keine Ladevorgänge vorhanden"
Else
lv.Range(strBereich).Sort _
Key1:=Range(strSpalte & "1"), Order1:=xlAscending, Key2:=Range(strSpalte1 & "1"), Order2:=xlAscending, _
Header:=xlNo
lv.Range("I" & L + 1) = WorksheetFunction.Sum(Range("I10:I" & L))
lv.Range("J" & L + 1) = WorksheetFunction.Sum(Range("J10:J" & L))
lv.Range("K" & L + 1) = WorksheetFunction.Sum(Range("K10:K" & L))
lv.Range("L" & L + 1) = WorksheetFunction.Sum(Range("L10:L" & L))
lv.Range("B" & L + 1).Value = "Gesamtsumme"
End If
lv.Range("C1").Value = "$B$2:$L$" & L + 1
üb.Select
With ge
ZeileMax = .UsedRange.Rows.Count
n = 66
For Zeile = 2 To ZeileMax
If ge.Cells(Zeile, 1).Value = üb.Cells(1, 1) Then
.Rows(Zeile).Copy Destination:=üb.Rows(n)
n = n + 1
End If
Next Zeile
End With
R = Range("B65000").End(xlUp).Row
strBereich = "A53:M" & R
strSpalte = "B"
üb.Range(strBereich).Sort Key1:=Range(strSpalte & "1"), Order1:=xlAscending, Header:=xlNo
mv.Select
With mv
.Cells(leereZelle, 11).Value = "ja"
üb.Cells(36, 10).Value = .Cells(leereZelle, 10).Value
End With
End Sub

VBA loop with arrays duplicating output

I'm new to using arrays (and VBA in general) and I'm trying to incorporate a series of arrays into a module that formats SPSS syntax output in worksheets in a single workbook. Below is my code, which works, but is duplicating the results that are found. I think it has something to do with the order of my loops but I can't seem to figure out how to fix it. Any thoughts would be greatly appreciated.
Sub FindValues()
Call CreateSummary
'This code will build the initial summary file
Dim ws As Excel.Worksheet
'Application.ScreenUpdating = False
MsgBox ("It will take a moment for data to appear, please be patient if data does not immediately appear")
Dim LastRow As Long
Dim i As Integer
Dim i2 As Integer
Dim x As Integer
Dim y As Integer
Dim CopiedRows As Integer
Dim LocationA(4) As String
Dim LocationB(4) As String
Dim LocationC(4) As String
Dim LocationD(4) As String
Dim VariableA(4) As Integer
Dim VariableB(4) As Integer
Dim ColumnA(4) As String
Dim ColumnB(4) As String
Dim n As Long
'Find DateTime Info
LocationA(1) = "Date_Time"
LocationB(1) = "Quarter"
LocationC(1) = "N"
LocationD(1) = "Minimum"
VariableA(1) = 1
VariableB(1) = 1
ColumnA(1) = "B"
ColumnB(1) = "C"
LocationA(2) = "Dur*"
LocationB(2) = "Methodology_ID"
LocationC(2) = "Mean"
LocationD(2) = "N"
VariableA(2) = 1
VariableB(2) = 1
ColumnA(2) = "C"
ColumnB(2) = "D"
LocationA(3) = "WebTimeout"
LocationB(3) = "Methodology_ID"
LocationC(3) = "Mean"
LocationD(3) = "N"
VariableA(3) = 1
VariableB(3) = 1
ColumnA(3) = "C"
ColumnB(3) = "D"
'LocationA(4) = "Crosstabulation"
'LocationB(4) = "Quarter"
'LocationC(4) = "N"
'LocationD(4) = "Minimum"
'VariableA(4) = 1
'Find OSAT Data
'LocationA(2) = "*Report*"
'LocationB(2) = "*CallMonth*"
'LocationC(2) = "Mean*"
'LocationD(2) = "*Overall*"
'VariableA(2) = 2
For Each ws In Application.ThisWorkbook.Worksheets
'Starting row
i = 1
'Find LastRow
LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
If ws.Name <> "Run Macros" Then
Do While i <= LastRow
For x = 1 To 3
If ws.Range("A" & i).Value Like LocationA(x) And ws.Range("A" & i + 1).Value Like LocationB(x) And ws.Range(ColumnA(x) & i + VariableA(x)).Value Like LocationC(x) And ws.Range(ColumnB(x) & i + VariableB(x)).Value Like LocationD(x) Then
CopiedRows = 0
i2 = i
Do While ws.Range("A" & i2 + 1).Borders(xlEdgeLeft).LineStyle = 1 And ws.Range("A" & i2 + 1).Borders(xlEdgeLeft).Weight = 4
i2 = i2 + 1
CopiedRows = CopiedRows + 1
Loop
n = Sheets("Summary").Cells(Rows.Count, "A").End(xlUp).Row + 4
ws.Rows(i & ":" & i + CopiedRows).Copy Sheets("Summary").Range("A" & n)
On Error Resume Next
End If
Next x
i = i + 1
Loop
End If
Next
'Application.ScreenUpdating = True
End Sub
This works if anyone want to reuse this code...
For x = 1 To 3 Step 1
For Each ws In Application.ThisWorkbook.Worksheets
'Starting row
i = 1
'Find LastRow
LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
Do While i <= LastRow
If ws.Name <> "Run Macros" Or ws.Name <> "Summary" Then
If ws.Range("A" & i).Value Like LocationA(x) And ws.Range("A" & i + 1).Value Like LocationB(x) And ws.Range(ColumnA(x) & i + VariableA(x)).Value Like LocationC(x) And ws.Range(ColumnB(x) & i + VariableB(x)).Value Like LocationD(x) Then
CopiedRows = 0
i2 = i
Do While ws.Range("A" & i2 + 1).Borders(xlEdgeLeft).LineStyle = 1 And ws.Range("A" & i2 + 1).Borders(xlEdgeLeft).Weight = 4
i2 = i2 + 1
CopiedRows = CopiedRows + 1
Loop
n = Sheets("Summary").Cells(Rows.Count, "A").End(xlUp).Row + 4
ws.Rows(i & ":" & i + CopiedRows).Copy Sheets("Summary").Range("A" & n)
Exit For
On Error Resume Next
End If
End If
i = i + 1
Loop
Next
Next x

Trying to extract data from curly braces but not working

I need to sync up the values in the curly braces {} found in column C and put them against the user id in column F as seen below.
E.g. on the Emails sheet
becomes this on a new sheet
Sub CopyConditional()
Dim wshS As Worksheet
Dim WhichName As String
Set wshS = ActiveWorkbook.Sheets("Emails")
WhichName = "NewSheet"
Const NameCol = "C"
Const FirstRow = 1
Dim LastRow As Long
Dim SrcRow As Long
Dim TrgRow As Long
Dim wshT As Worksheet
Dim cpt As String
Dim user As String
Dim computers() As String
Dim computer As String
On Error Resume Next
Set wshT = Worksheets(WhichName)
If wshT Is Nothing Then
Set wshT = Worksheets.Add(After:=wshS)
wshT.Name = WhichName
End If
On Error GoTo 0
If wshT.Cells(1, NameCol).value = "" Then
TrgRow = 1
Else
TrgRow = wshT.Cells(wshT.Rows.Count, NameCol).End(xlUp).Row + 1
End If
LastRow = wshS.Cells(wshS.Rows.Count, NameCol).End(xlUp).Row
For SrcRow = FirstRow To LastRow
cpt = wshS.Range("C" & SrcRow).value
user = wshS.Range("F" & SrcRow).value
If InStr(cpt, ":") Then
cpt = Mid(cpt, InStr(1, cpt, ":") + 1, Len(cpt))
End If
If InStr(cpt, ";") Then
computers = Split(cpt, ";")
For i = 0 To UBound(computers)
If computers(i) <> "" Then
wshT.Range("A" & TrgRow).value = user
wshT.Range("B" & TrgRow).value = Mid(Left(computers(i), Len(computers(i)) - 1), 2)
TrgRow = TrgRow + 1
End If
Next
Else
computer = cpt
If computer <> "" Then
wshT.Range("A" & TrgRow).value = user
wshT.Range("B" & TrgRow).value = Mid(Left(computer, Len(computer) - 1), 2)
TrgRow = TrgRow + 1
End If
End If
Next SrcRow
End Sub
I managed to resolve it with the above code but there are 3 niggling issues:
1) The first curly brace is always copied, how do I omit this so something like {Computer1 looks like Computer 1
2) Where there are two computers in a row, then the output looks something like this:
when it should really be split into two different rows i.e.
User 1 | Computer 1
User 1 | Computer 2
3) If there is text after the last curly brace with text in it e.g. {Computer1};{Computer2};Request submitted then that text is added as a new row, I don't want this, I want it to be omitted e.g.
should just be:
User 1 | Computer 1
User 1 | Computer 2
How do I go about rectifying these issues?
Try this:
Sub Collapse()
Dim uRng As Range, cel As Range
Dim comps As Variant, comp As Variant, r As Variant, v As Variant
Dim d As Dictionary '~~> Early bind, for Late bind use commented line
'Dim d As Object
Dim a As String
With Sheet1 '~~> Sheet that contains your data
Set uRng = .Range("F1", .Range("F" & .Rows.Count).End(xlUp))
End With
Set d = CreateObject("Scripting.Dictionary")
With d
For Each cel In uRng
a = Replace(cel.Offset(0, -3), "{", "}")
comps = Split(a, "}")
Debug.Print UBound(comps)
For Each comp In comps
If InStr(comp, "Computer") <> 0 _
And Len(Trim(comp)) <= 10 Then '~~> I assumed max Comp# is 99
If Not .Exists(cel) Then
.Add cel, comp
Else
If IsArray(.Item(cel)) Then
r = .Item(cel)
ReDim Preserve r(UBound(r) + 1)
r(UBound(r)) = comp
.Item(cel) = r
Else
r = Array(.Item(cel), comp)
.Item(cel) = r
End If
End If
End If
Next
Next
End With
For Each v In d.Keys
With Sheet2 '~~> sheet you want to write your data to
If IsArray(d.Item(v)) Then
.Range("A" & .Rows.Count).End(xlUp).Offset(1, 0) _
.Resize(UBound(d.Item(v)) + 1) = v
.Range("B" & .Rows.Count).End(xlUp).Offset(1, 0) _
.Resize(UBound(d.Item(v)) + 1) = Application.Transpose(d.Item(v))
Else
.Range("A" & .Rows.Count).End(xlUp).Offset(1, 0) = v
.Range("B" & .Rows.Count).End(xlUp).Offset(1, 0) = d.Item(v)
End If
End With
Next
Set d = Nothing
End Sub
Above code uses Replace and Split Function to pass your string to array.
a = Replace(cel.Offset(0, -3), "{", "}") '~~> standardize delimiter
comps = Split(a, "}") '~~> split using standard delimiter
Then information are passed to dictionary object using User as key and computers as items.
We filter the items passed to dictionary using Instr and Len Function
If InStr(comp, "Computer") <> 0 _
And Len(Trim(comp)) <= 10 Then
As I've commented, I assumed your max computer number is 99.
Else change 10 to whatever length you need to check.
Finally we return the dictionary information to the target worksheet.
Note: You need to add reference to Microsoft Scripting Runtime if you prefer early bind
Result: I tried it on a small sample data patterned on how I see it in you SS.
So assuming you have this data in Sheet1:
Will output data in Sheet2 like this:
I use a custom parse function for this type of operation:
Sub CopyConditional()
' some detail left out
Dim iRow&, Usern$, Computer$, Computers$
For iRow = ' firstrow To lastrow
Usern = Sheets("Emails").Cells(iRow, "F")
Computers = Sheets("Emails").Cells(iRow, "C")
Do
Computer = zParse(Computers) ' gets one computer
If Computer = "" Then Exit Do
' Store Computer and Usern
Loop
Next iRow
End Sub
Function zParse$(Haystack$) ' find all {..}
Static iPosL& '
Dim iPosR&
If iPosL = 0 Then iPosL = 1
iPosL = InStr(iPosL, Haystack, "{") ' Left
If iPosL = 0 Then Exit Function ' no more
iPosR = InStr(iPosL, Haystack, "}") ' Right
If iPosR = 0 Then MsgBox "No matching }": Stop
zParse = Mid$(Haystack, iPosL + 1, iPosR - iPosL - 1)
iPosL = iPosR
End Function
1) Use the Mid function to drop the first character:
str = "{Computer1"
str = Mid(str,2)
now str = "Computer1"
2) You can use the Split function to separate these out and combine with the Mid function above
str = "{Computer1}{Computer2}"
splt = Split(str,"}")
for a = 0 to Ubound(splt)
result = Mid(splt(a),2)
next a
3) Add a conditional statement to the above loop
str = "{Computer1}{Computer2}"
splt = Split(str,"}")
for a = 0 to Ubound(splt)
if Left(splt(a),1) = "{" then result = Mid(splt(a),2)
next a
Use this loop and send each result to the desired cell (in the for-next loop) and you should be good to go.