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
Related
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).
Length of all cells in a specific columns has to be 6 characters. If not, I have to add 0 in the beginning of each cell until cell length =6. What is the best way to do it?
Dim lastRow As Integer
Dim i As Integer
Dim sh As Worksheet
Dim cont As String
Set sh = ThisWorkbook.Worksheets("Sheet1") 'Your Sheet
With sh
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row 'Coloumn A
For i = 1 To lastRow
Cells(i, 1).NumberFormat = "#"
Do Until Len(Cells(i, 1)) = 6 'Coloumn A
cont = Cells(i, 1) 'Coloumn A
Cells(i, 1) = "0" & cont 'Coloumn A
Loop
Next i
End With
Does this work for you? You only have to edit the coloumn that you want to check, you could use a TextBox for that.
Here is a sample for column C:
Sub Make6()
Dim r As Range, r1 As Range, r2 As Range, r3 As Range
bry = Array("000000", "00000", "0000", "000", "00", "0", "")
Dim N As Long, v As String, L As Long
Set r1 = Range("C:C")
N = Cells(Rows.Count, "C").End(xlUp).Row
Set r2 = Range("C1:C" & N)
Set r3 = Range("C" & N + 1 & ":C" & Rows.Count)
r1.NumberFormat = "#"
For Each r In r2
v = r.Value
L = Len(v)
If L < 6 Then
r.Value = bry(L) & v
End If
Next r
r3.Value = "000000"
End Sub
It will fix ALL cells in column C.
Not really good at VBA here. Found and edited some code that I believe can help me.
I need this code to search 2 columns (L and M) for any string in those columns that ends with _LC _LR etc... Example: xxxxxxxx_LC .
If the cell ends with anything in the array, I need the row to be copied to a new sheet. Here is what I have:
Option Explicit
Sub Test()
Dim rngCell As Range
Dim lngLstRow As Long
Dim keywords() As String
Dim maxKeywords As Integer
maxKeywords = 6
ReDim keywords(1 To maxKeywords)
maxKeywords(1) = "_LC"
maxKeywords(2) = "_LR"
maxKeywords(3) = "_LF"
maxKeywords(4) = "_W"
maxKeywords(5) = "_R"
maxKeywords(6) = "_RW"
lngLstRow = ActiveSheet.UsedRange.Rows.Count
For Each rngCell In Range("L2:L, M2:M" & lngLstRow)
For i = 1 To maxKeywords
If keywords(i) = rngCell.Value Then
rngCell.EntireRow.Copy
Sheets("sheet1").Select
Range("L65536, M65536").End(xlUp).Offset(1, 0).Select
Selection.PasteSpecial xlPasteValues
Sheets("Results").Select
End If
Next i
Next
End Sub
Okay, the issue I think is with your variable declarations. Before I continue, I will echo #GradeEhBacon's comment that if you can't read this and understand what's going on, you may want to take some time to learn VBA before running.
This should work, AFAIK. You didn't specify which sheet has what info, so that may have to be tweaked. Try the below, and let me know what is/isn't working:
Sub Test()
Dim rngCell As Range
Dim lngLstRow As Long
Dim keywords() As String, maxKeywords() As String
Dim totalKeywords As Integer, i&
Dim ws As Worksheet, resultsWS As Worksheet
Set ws = Sheets("Sheet1")
Set resultsWS = Sheets("Results")
totalKeywords = 6
ReDim keywords(1 To totalKeywords)
ReDim maxKeywords(1 To totalKeywords)
maxKeywords(1) = "_LC"
maxKeywords(2) = "_LR"
maxKeywords(3) = "_LF"
maxKeywords(4) = "_W"
maxKeywords(5) = "_R"
maxKeywords(6) = "_RW"
lngLstRow = ws.UsedRange.Rows.Count 'Assuming "Sheet1" is what you want to get the last range of.
Dim k& ' create a Long to use as Column numbers for the loop
For k = 12 To 13 ' 12 is column L, 13 is M
With ws 'I'm assuming your Ranges are on the "Sheet1" worksheet
For Each rngCell In .Range(.Cells(1, k), .Cells(lngLstRow, k))
For i = LBound(maxKeywords) To UBound(maxKeywords)
If maxKeywords(i) = Right(rngCell.Value, 3) or maxKeywords(i) = Right(rngCell.Value, 2) Then
' rngCell.EntireRow.Copy
' ws.Range("L65536, M65536").End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
resultsWS.Cells(65536, k).End(xlUp).Offset(1, 0).EntireRow.Value = rngCell.EntireRow.Value
End If
Next i
Next rngCell
End With
Next k
End Sub
This might be what you are looking for:
==================================================
Option Explicit
Sub Test()
Dim rngCell As Range
Dim lngLstRow As Long
Dim keywords() As String
Dim maxKeywords, i, j, k As Integer
maxKeywords = 6
ReDim keywords(1 To maxKeywords)
keywords(1) = "_LC"
keywords(2) = "_LR"
keywords(3) = "_LF"
keywords(4) = "_W"
keywords(5) = "_R"
keywords(6) = "_RW"
lngLstRow = ActiveSheet.UsedRange.Rows.Count
For j = 1 To lngLstRow
For i = 1 To maxKeywords
If keywords(i) = Right(Sheets("Results").Range("L" & j).Value, Len(keywords(i))) Or _
keywords(i) = Right(Sheets("Results").Range("M" & j).Value, Len(keywords(i))) Then
k = k + 1
Rows(j & ":" & j).Copy
Sheets("sheet1").Select
Range("A" & k).Select
ActiveSheet.Paste
End If
Next i
Next j
End Sub
I have the following VBA code:
Sub test():
Dim NameValue As String, w1 As Worksheet, w2 As Worksheet
Dim i As Long, j As Long, k As Long, c As Long
Set w1 = Sheets("Sheet2"): Set w2 = Sheets("Sheet3")
GetNameValue: For i = 1 To w1.Range("A" & Rows.Count).End(xlUp).row
If w1.Range("A" & i) = "NAME:" Then
If InStr(1, NameValue, w1.Range("B" & i)) Then GoTo GetNext
j = i + 1: Do Until w1.Range("A" & j) = "DATE OF BIRTH:": j = j + 1: Loop
NameValue = Trim(NameValue & " " & w1.Range("B" & i) & "|" & w1.Range("B" & j))
c = c + 1: End If
GetNext: Next i: NameValue = NameValue & " "
For k = 1 To c
i = InStr(1, NameValue, "|"): j = InStr(i, NameValue, " ")
w2.Range("A" & k) = Left(NameValue, i - 1): w2.Range("B" & k) = Mid(NameValue, i + 1, j - i)
NameValue = Mid(NameValue, j + 1, Len(NameValue) - j)
Next k
End Sub
To break down what this code does:
1) Set the first sheet that should be searched and the second sheet (output sheet) that the results should be appended to.
2) Search the first column for a certain string "NAME:" and once found take the value in the second column, place it in the output sheet go look for "DATE OF BIRTH:". Once "DATE OF BIRTH:" is found put it beside the value for "NAME:" in the output sheet.
3) Repeat until there are no more entries.
I'm sure this is a very simple modification, but what I'd like to do is check whether a certain string exists, if it does grab the entry directly BELOW it, and then continue searching for the next string and associated entry just like the code does already.
Can anyone point me to what I would need to change in order to do this (and preferably why)?
In addition, how might I be able to extend this code to run over multiple sheets while depositing the results in a single sheet? Do I need to set up a range running over the worksheets w_1....w_(n-1) (with output sheet w_n possibly in a different workbook)?
Removed Line continuations in code:
Sub test()
Dim NameValue As String, w1 As Worksheet, w2 As Worksheet
Dim i As Long, j As Long, k As Long, c As Long
Set w1 = Sheets("Sheet2")
Set w2 = Sheets("Sheet3")
GetNameValue:
For i = 1 To w1.Range("A" & Rows.Count).End(xlUp).Row
If w1.Range("A" & i) = "NAME:" Then
If InStr(1, NameValue, w1.Range("B" & i)) Then GoTo GetNext
j = i + 1
Do Until w1.Range("A" & j) = "DATE OF BIRTH:"
j = j + 1
Loop
NameValue = Trim(NameValue & " " & w1.Range("B" & i) & "|" & w1.Range("B" & j))
c = c + 1
End If
GetNext:
Next i
NameValue = NameValue & " "
For k = 1 To c
i = InStr(1, NameValue, "|")
j = InStr(i, NameValue, " ")
w2.Range("A" & k) = Left(NameValue, i - 1)
w2.Range("B" & k) = Mid(NameValue, i + 1, j - i)
NameValue = Mid(NameValue, j + 1, Len(NameValue) - j)
Next k
End Sub
UPDATE: Just to make sure we're all on the same page about what the output would look like. Suppose we are searching for the entry below A and the entry beside C:
INPUT
A 1
B
y 3
z 4
t
d
s 7
C 8
A 1
Z
y 3
z 4
t
d
s 7
C 12
OUTPUT
B 8
Z 12
.
.
.
Assuming I understand your desire correctly, you can use the .Offset method with your current range to get to the cell below it. You would need to add a dim, so here's my stab at what you're trying to accomplish:
Sub test()
Dim NameValue As String, w1 As Worksheet, w2 As Worksheet
'new local variable
Dim newValue as string
Dim i As Long, j As Long, k As Long, c As Long
Set w1 = Sheets("Sheet2")
Set w2 = Sheets("Sheet3")
GetNameValue:
For i = 1 To w1.Range("A" & Rows.Count).End(xlUp).Row
'assuming your string is in column A
If w1.Range("A" & i) = "FIND ME" Then
newValue = w1.Range("A" & i).Offset(1,0).Value
End If
If w1.Range("A" & i) = "NAME:" Then
If InStr(1, NameValue, w1.Range("B" & i)) Then GoTo GetNext
j = i + 1
Do Until w1.Range("A" & j) = "DATE OF BIRTH:"
j = j + 1
Loop
NameValue = Trim(NameValue & " " & w1.Range("B" & i) & "|" & w1.Range("B" & j))
c = c + 1
End If
GetNext:
Next i
NameValue = NameValue & " "
For k = 1 To c
i = InStr(1, NameValue, "|")
j = InStr(i, NameValue, " ")
w2.Range("A" & k) = Left(NameValue, i - 1)
w2.Range("B" & k) = Mid(NameValue, i + 1, j - i)
NameValue = Mid(NameValue, j + 1, Len(NameValue) - j)
Next k
End Sub
Then you could do anything you desired with the newValue string, including putting it in w2 like so: w2.Range("D1").value = newValue
UPDATED ANSWER
I am now 89% sure I know what you are trying to accomplish :) thanks for your clarifying example.
To search a range for your search string, you need to set up a range you are looking in:
dim searchRange as range
dim w1,w2 as worksheet
Set w1 = Sheets("Sheet1")
Set w2 = Sheets("Sheet2")
set searchRange = w1.Range("A" & Rows.Count).End(xlUp).Row
Then you search the searchRange for both of your search strings (which I'm saying are "A" for the first and "C" for the second). As long as both strings are found in the searchRange, it will create a new Dictionary entry for the two values, having the value below "A" as the key and the value beside "C" as the item.
dim rng as range
dim valueBelowFirstSearch as string
dim resultsDictionary as object
dim i as integer
dim c, d as range
dim cAddress, dAddress as string
set resultsDictionary = CreateObject("scripting.dictionary")
with searchRange
set c = .Find("A", lookin:=xlValues)
set d = .Find("C", lookin:=xlValues)
if not c Is Nothing and not d Is Nothing then
cAddress = c.address
dAddress = d.address
resultsDictionary.add Key:=c.offset(1,0).value, Item:=d.value
Do
set c = .FindNext(c)
set d = .FindNext(d)
Loop While not c is nothing and not d is nothing and c.address <> cAddress and d.address <> dAddress
end if
end with
Now that we have all of the results in the resultsDictionary, we can now output the values into another place, which I'm choosing to be w2.
dim outRange as range
dim item as variant
set outRange = w2.Range("A1")
for each item in resultsDictionary
outRange.Value = item.key
set outRange = outRange.Offset(0,1)
outRange.Value = item.item
set outRange = outRange.Offset(1,-1)
next item
Can anyone point me to what I would need to change in order to do this
(and preferably why)?
Basically you need to change the parts of which NameValue is composed.
Originally you took the value beside the first match as w1.Range("B" & i) and now you want the value below the first match, which is w1.Range("A" & i + 1).
Originally it was:
Trim(NameValue & " " & w1.Range("B" & i) & "|" & w1.Range("B" & j))
Now you need something like this:
Trim(NameValue & " " & w1.Range("A" & i + 1) & "|" & w1.Range("B" & j))
In addition, how might I be able to extend this code to run over
multiple sheets while depositing the results in a single sheet?
(with output sheet w_n possibly in a different workbook)?
To achieve that you can e.g. create an array of Sheets and let the code run for each Sheet of this array. Note that the array might contain 1-N Sheets.
' Set array of sheets for just one sheet
Dim searchedSheets As Sheets
Set searchedSheets = Workbooks("SomeBook.xlsx").Sheets(Array("Sheet1"))
' Set array of sheets for more sheets, e.g. "Sheet1" and "Sheet2" and "Sheet3"
Dim searchedSheets As Sheets
Set searchedSheets = Workbooks("SomeBook.xlsx").Sheets(Array("Sheet1", "Sheet2", "Sheet3"))
' Finally set the second sheet where the results should be appended
' to sheet in the same workbook as the searched sheets
Dim outputSheet As Worksheet
Set outputSheet = Workbooks("SomeBook.xlsx").Worksheets("ResultSheet")
' Or set the second sheet where the results should be appended to sheet
' in a different workbook then the searched sheets belong to
Dim outputSheet As Worksheet
Set outputSheet = Workbooks("SomeOtherBook.xlsx").Worksheets("ResultSheet")
The complete code might look like this (tested with data you provided).
Option Explicit
Public Sub main()
' String to search below of it
Dim string1 As String
string1 = "A"
' String to search beside of it
Dim string2 As String
string2 = "C"
' Set the sheets that should be searched
Dim searchedSheets As Sheets
Set searchedSheets = Workbooks("SomeBook.xlsx").Sheets(Array("Sheet1", "Sheet2"))
' Set the second sheet (outputSheet sheet) that the results should be
' appended to external sheet in different book
Dim outputSheet As Worksheet
Set outputSheet = Workbooks("SomeOtherBook.xlsx").Worksheets("ResultSheet")
SearchFor string1, string2, searchedSheets, outputSheet
End Sub
Public Sub SearchFor( _
string1 As String, _
string2 As String, _
searchedSheets As Sheets, _
output As Worksheet)
Dim searched As Worksheet
Dim NameValue As String
Dim below As String
Dim beside As String
Dim i As Long
Dim j As Long
Dim k As Long
Dim c As Long
Dim rowsCount As Long
For Each searched In searchedSheets
rowsCount = searched.Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To rowsCount
' Search the first column for a 'string1'
If searched.Range("A" & i) = string1 Then
' once 'string1' was found grab the entry directly below it
below = searched.Range("A" & i + 1)
If InStr(1, NameValue, below) Then
' skip this 'below' result because it was found before
GoTo GetNext
End If
' Search the first column for a 'string2' starting at the
' position where 'below' was found
For j = i + 1 To rowsCount
If searched.Range("A" & j) = string2 Then
' once 'string2' was found grab the entry directly
' beside it
beside = searched.Range("B" & j)
Exit For
End If
Next j
' Append 'below' and 'beside' to the result and count the
' number of metches
NameValue = Trim(NameValue & " " & below & "|" & beside)
c = c + 1
End If
GetNext:
Next i
Next searched
' Write the output
NameValue = NameValue & " "
For k = 1 To c
i = InStr(1, NameValue, "|")
j = InStr(i, NameValue, " ")
output.Range("A" & k) = Left(NameValue, i - 1)
output.Range("B" & k) = Mid(NameValue, i + 1, j - i)
NameValue = Mid(NameValue, j + 1, Len(NameValue) - j)
Next k
End Sub
Note: I replaced the Do-Until loop with For-Next loop because the Do-Until might cause a Stack-Overflow :-) error if the string "DATE OF BIRTH:" does not exist in the first column. However I have tryied to keep your originall code structure so you still understand it. HTH.
Assuming that you want to find one value (Name:), then continue searching till to find the second one (Date Of Birth:)... Finally, you want to move these pair of data into another worksheet.
To achieve that, i'd suggest to use Dictionary object to get only distinct values. I strongly do not recommend to use string concatenation as you provided in your code!
Option Explicit
Sub Test()
Dim src As Worksheet, dst As Worksheet
Set dst = ThisWorkbook.Worksheets("Sheet2")
For Each src In ThisWorkbook.Worksheets
If src.Name = dst.Name Then GoTo SkipNext
NamesToList src, dst
SkipNext:
Next
End Sub
'needs reference to MS Scripting Runtime library
Sub NamesToList(ByVal srcWsh As Worksheet, ByVal dstWsh As Worksheet, _
Optional ByVal SearchFor As String = "NAME:", Optional ByVal ThenNextFor As String = "DATE OF BIRTH:")
Dim dic As Dictionary, i As Long, j As Long, k As Long
Dim sKey As String, sVal As String
On Error GoTo Err_NamesToList
Set dic = New Dictionary
i = 2
j = GetFirstEmpty(srcWsh)
Do While i < j
If srcWsh.Range("A" & i) = SearchFor Then
sKey = srcWsh.Range("B" & i)
If Not dic.Exists(sKey) Then
Do While srcWsh.Range("A" & i) <> ThenNextFor
i = i + 1
Loop
sVal = srcWsh.Range("B" & i)
dic.Add sKey, sVal
k = GetFirstEmpty(dstWsh)
With dstWsh
.Range("A" & k) = sKey
.Range("B" & k) = sVal
End With
'sKey = ""
'sVal = ""
End If
End If
SkipNext:
i = i + 1
Loop
Exit_NamesToList:
On Error Resume Next
Set dic = Nothing
Exit Sub
Err_NamesToList:
Resume Exit_NamesToList
End Sub
Function GetFirstEmpty(ByVal wsh As Worksheet, Optional ByVal sCol As String = "A") As Long
GetFirstEmpty = wsh.Range(sCol & wsh.Rows.Count).End(xlUp).Row + 1
End Function
Sample output:
Name DateOfBirth:
A 1999-01-01
B 1999-01-02
C 1999-01-03
D 1999-01-04
E 1999-01-05
The idea is this...
If the value in column A (workbook "source") is equal to the value in column Q (workbook "affected") then the values in the corresponding column F (workbook "source") is subtracted from column T (workbook "affected"). But it isn't working. I'm not receiving any error of any kind, it just doesn't do what I want. I appreciate any help.
Sub Subtract_Between_workbooks()
Dim Source As Workbook
Dim Affected As Workbook
Dim Dados As Worksheet
Dim Source_Sheet As Worksheet
Dim LastRow As Long
Dim i As Long
Dim j As Long
Dim v As Variant
Dim N As Long
Dim M As Long
Dim FinalRow As Long
Source = Workbooks.Open("\\dsapc429pfs.pactual.net\homefolder02$\wellsty\Desktop\LCA_LCI Macro Writing\ResgatesEmissões.xlsb")
Affected = Workbooks.Open("\\dsapc429pfs.pactual.net\homefolder02$\wellsty\Desktop\LCA_LCI Macro Writing\New - Macro Writing - Controle de Lastro LCA.xlsm")
Set Dados = Workbooks(Affected).Sheets("Dados")
Set Source_Sheet = Workbooks(Source).Sheets("Sheet1")
LastRow = Source_Sheet.Cells(Rows.Count, "A").End(xlUp).Row
N = Dados.Cells(Rows.Count, "Q").End(xlUp).Row
M = Source_Sheet.Cells(Rows.Count, "A").End(xlUp).Row
For Z = 1 To LastRow
If Source_Sheet.Range("B" & i) = "LCA" And Source_Sheet.Range("D" & i) = "Resgate Final Passivo Cliente" And Source_Sheet.Range("H" & i) = "9007230" Then
For i = 1 To M
v1 = Source_Sheet.Cells(i, "A").Value
v2 = Source_Sheet.Cells(i, "F").Value
For j = 1 To N
If v1 = Dados.Cells(j, "Q").Value Then
Dados.Cells(j, "T").Value = Dados.Cells(j, "T").Value - v2
Exit For
End If
Next j
Next i
Else: End If
Next Z
End Sub
You have declared source and affected as workbooks in your code. You need to use the Set keyword to make an assignment statement for objects for example:
Set Source = Workbooks.Open("\\dsapc429pfs.pactual.net\homefolder02$\wellsty\Desktop\LCA_LCI Macro Writing\ResgatesEmissões.xlsb")
It is always good to use the debugger if your code is not behaving like expected, and check the code line by line.
On top of #Jeanno answer please check your first for loop variable.
For Z = 1 To LastRow
If Source_Sheet.Range("B" & i) = "LCA" And Source_Sheet.Range("D" & i) = "Resgate Final Passivo Cliente" And Source_Sheet.Range("H" & i) = "9007230" Then
You may need to replace i with z? since i is not defined at that moment.
just wanted to let y'all know that I got it to work. I pasted the code below. I wouldn't have figured it out without the troubleshooting help. I appreciate it a lot!
Sub Subtract_Between_workbooks()
'===Para menos Resgate===Waiting on Stack overflow
Dim Source As Workbook
Dim Affected As Workbook
Dim Dados As Worksheet
Dim Source_Sheet As Worksheet
Dim LastRow As Long
Dim i As Long
Dim j As Long
Dim v As Variant
Dim N As Long
Dim M As Long
Dim FinalRow As Long
Set Source = Workbooks("ResgatesEmissões.xlsb")
Set Affected = Workbooks.Open("\\dsapc429pfs.pactual.net\homefolder02$\wellsty\Desktop\LCA_LCI Macro Writing\New - Macro Writing - Controle de Lastro LCA.xlsm")
Set Dados = Affected.Sheets("Dados")
Set Source_Sheet = Source.Sheets("Sheet1")
LastRow = Source_Sheet.Cells(Rows.Count, "A").End(xlUp).Row
N = Dados.Cells(Rows.Count, "Q").End(xlUp).Row
M = Source_Sheet.Cells(Rows.Count, "A").End(xlUp).Row
For Z = 1 To LastRow
If Source_Sheet.Range("B" & Z) = "LCA" And Source_Sheet.Range("D" & Z) = "Resgate Final Passivo Cliente" And Source_Sheet.Range("H" & Z) = "9007230" Then
For i = 1 To M
v1 = Source_Sheet.Cells(i, "A").Value
v2 = Source_Sheet.Cells(i, "F").Value
For j = 1 To N
If v1 = Dados.Cells(j, "Q").Value Then
Dados.Cells(j, "T").Value = Dados.Cells(j, "T").Value - v2
Exit For
End If
Next j
Next i
Else: End If
Next Z
End Sub