I'm struggling with what I believe is a loop problem. I'm more of a "backyard mechanic" with Excel VBA so please excuse my simple question.
I can't share the workbook due to proprietary information unfortunately but I have the code I use with some field name changes.
Background: I have a column that I take 1 cell at a time and feed it into a pivot table field and run reports. The way I have it now, I delete the current Row which returns the reference back to cell A2. Think of it like a programming Pez dispenser. Awful and very brute force I know. The row delete operation takes a lot of system resources and I'd like to optimize it. I've tried reading through here and a few other websites for the past couple hours but I can't make heads or tails of what I'm coming across.
Any help would be very greatly appreciated!
Sub AutoReport()
Dim strPage As String
Worksheets("HomePage").Select
Beginning:
'Sets the name in Home Page to the name in Feederlist cell F2
With Sheet1
strPage = Worksheets("FeederList").Range("A2")
Worksheets("HomePage").PivotTables("PivotTable1").PivotFields("UNIQUE ID"). _
CurrentPage = strPage
End With
**Do a Bunch of Stuff**
' Feeds the next input into the machine
MoveToNext:
Worksheets("FeederList").Activate
Worksheets("FeederList").Range("A2").EntireRow.Delete
If Worksheets("FeederList").Range("A2") = "" Then
MsgBox "All Reports have been created.", vbInformation + vbOKOnly
Exit Sub
Else
GoTo Beginning
End If
End Sub
the acceptance is the first step to learning. In general, the stackoverflow community encourages to invigorate the technical and logical parts of the brain by providing the hints to solution and not the exact solution. However, as you are very new, I am starting with giving off hints and later on the code to rectify your issue. You are going very well with your code, however some minor tweaks will optimize your code significantly.
Worksheets("HomePage").Select
Dim lstRow As Long
Dim rngCell As Range
Dim rngSelection As Range
'Let's find the last row with data in column A.
'So that we only traverse the required range without the need of
'deleting previous cells while using the For loop.
lstRow = Worksheets("FeederList").Range("A" & Application.Rows.Count).End(xlUp).Row
Set rngSelection = Worksheets("FeederList").Range("A2:A" & lstRow)
For Each rngCell In rngSelection.Cells
'Ignore all the cells with blank value or else the pivot table will throw the error
If Trim(rngCell.Value) <> vbNullString Then
'Sets the name in Home Page to the name in Feederlist cell F2
strPage = rngCell.Value
Worksheets("HomePage").PivotTables("PivotTable1").PivotFields("UNIQUE ID"). _
CurrentPage = strPage
' **Do a Bunch of Stuff**
End If
Next
rngSelection.Clear ' Optional - if the range really needs to be cleared
MsgBox "All Reports have been created.", vbInformation + vbOKOnly
You need to understand the changes and ask any further questions that you might have. Below are the changes made to the original code.
Introduced calculation of last cell in Column A having some data
Introduced For Loop instead of using labels eliminating the need of deleting rows
Related
For example, on some sites,
I already study & try on Macro for insert HPageBreak Present on Every occurrence on particular text, with loop every cell of single column, but my requirement is every 2nd occurrence of particular txt
https://answers.microsoft.com/en-us/msoffice/forum/all/excel-macro-that-will-insert-horizontal-macros/9976e30a-8aae-4bec-84e8-43b35b113ec2
https://answers.microsoft.com/en-us/msoffice/forum/all/insert-dynamic-page-breaks-with-vba-for-excel/85790a6a-ef93-4354-8ad5-3cc5e4399285
https://www.extendoffice.com/documents/excel/1774-excel-insert-page-break-every-row.html
I already use Below code to put Hpagebreak on every found
but now requirement raise as every 2nd occurrence of "DISPATCH JAN TO NOV-22"
Sub Insert_Pagebreak_On_EveryFoundok()
Dim MYCOLUMN As Range
Dim MyCell As Range
'For Each MyCell In Range("G2:G" & Rows.Count).End.xlUp))
'For Each MyCell In Range Cells(Rows.Count, 2).End(xlUp).row 2
ActiveSheet.Range("G" & Rows.Count).End(xlUp).row))
ActiveWindow.view = xlPageBreakPreview
Set MYCOLUMN = ActiveSheet.Range("F2:F" & ActiveSheet.Range("F" & Rows.Count).End(xlUp).row)
For Each MyCell In MYCOLUMN
MyCell.Select
'If MyCell.Value Like "*Page 1 of 1*" Then
If MyCell.Value Like "*DISPATCH JAN TO NOV-22*" Then
ActiveCell.EntireRow.Select
ActiveWindow.SelectedSheets.HPageBreaks.Add
ActiveCell.offset(1, 0)
Else
ActiveCell.offset(1, 0).Select
End If
Next
ActiveWindow.view = xlNormalView
End Sub
This Loop check every cell that take more time, but I believe if use Range.Find method, then it can be more robust.
I am not knowing very well all aspects of VBA, but I daily use VBA in my many types of daily routine work & without it, I can't complete my work on time.
Currently I manually select 2 sets of data, adjust rows height to fit on A4, select -set-click print area & then print, and after print that I select below further 2 sets & do same thing, till sheet's data end,
painfully pass whole my day, just for print 2 data set on 1 A4 page.
There are need to beware for Hidden rows which hides for reason (not requirement in print). so, condition is only visible rows should be count for 2nd occurrence.
I attached Screen shot of whole scenario for reference.
[Plese Refer This Image as my situation]
enter image description here
Hundreds of data sets on this worksheet.
There should be 2 sets of data as pair require on every A4 size page.
so obviously page break requires on every 2nd occurrence of particular text.
If, that happen successfully, I am ready to manually adjust rows height to fit 2 sets in A4 page, so I get whole sheet ready for print in one go.
Hope, I try my best to describe my situation if require further, please mention.
Can anyone help regarding this?
I really appreciate & will be thankful forever.
Regards,
Chirag Raval
At my job I have automated a very manual task and to the point they have wanted to expand it to the other department where they work a little differently. So the goal of this is I want to be able to filter column "A" and then filter another column based off of phrases that I already have in place as well. The data in column "A" I would have a source in another sheet, but it would have around potentially 200-400 possibilities to look for. After it filters column A I then want it go to column "AG" and then do another loop filtering based off of provided key phrases that the analyst would select based on a data validation. Once it filters those two criteria I then have the codes in place to generate the spreadsheets for the analyst. The code below is an example of the first block, I have 4 other codes that are pretty much the same they just generate different templates, I had to do multiple codes cause I didn't know how to do a loop based off of a source.
Sub Generate_Consolidated_Update_Reports()
Dim EDI As String
EDI = Environ("USERPROFILE") & "\desktop\foldername\foldername2\foldername3\filename " &
Format(Now(), "MM.DD.YY")
Workbooks("Master(where the filtering happens)").Activate
'The next line of code I am just doing the second filtering which is based off of a data validation
'I created, essentially the analyst would just select a key phrase and then that would
'prompt the code to generate the template, I haven't figured out how to do it based off of
'the source that's why I just have the specific name in the key range
'The first step to the code would need to be looking for anything in column "A", but like
'I said that could be anywhere from 200-400 possibilities. I have access to it, though and have
'it listed in "Sheet2" along with the phrases in column "AG" as well.
ActiveWorkbook.Worksheets("Master").Sort.SortFields.Add Key:=Range("AG:AG" _
), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Selection
.autofilter Field:=33, Criteria1:="Send to EDI Team - Account Level"
.Offset(1, 0).Select
End With
'The next line of code will be seeing if it is empty, I realized that it would work once,
'but if there ever was data the next line of looking for empty would always
'just filter anyways and keep creating the template even though there was no data
'so I did this route where if it saw it was empty I called another code, would be awesome
'if I could figure that out too!
'This then goes on repeat down the line, it's 5 different codes, so it isn't clean. :/
'so I only did one so you wouldn't see a ton of fluff!
Dim Lr As Long
Lr = Range("A" & Rows.Count).End(xlUp).Row
If Lr > 1 Then
Workbooks.Add
ActiveWorkbook.SaveAs EDI & ".XLS"
Sheets("Sheet1").Select
Sheets("Sheet1").Name = "EDI Account Update"
'Redacted code, just fluff on creating the template for the analyst
'Next line is just doing the code to show all the data again
'Then within that if statement to then call another sub that is essentially the same process
'If the code doesn't find it it goes to else where it then just calls the other sub
If ActiveSheet.AutoFilterMode Then ActiveSheet.ShowAllData
Range("A1").Select
Call Create_EDI_Update_GroupLevel
Else
ActiveSheet.ShowAllData
Range("A1").Select
Call Create_EDI_Update_GroupLevel
End If
End Sub
The reason I have to also call other subs is because each criteria they select will generate a completely different template based on company policies and such.
Really sorry if this isn't clear, I am still learning coding and just having a hard time trying to explain exactly what I needed. Thank you so much for your time.
I wanted to add a comment but not enough reputation yet.
One question I have is: Are you filtering on one phrase among 200-400 or can it be multiple?
I can't be sure, however an Advanced Filter might help you in this case. It is not possible for me to go into all the details. There is a very good tutorial on Youtube about this (where I learned myself): VBA Advanced Filter - The FASTEST way to Copy and Filter Data
You can also use "Worksheet_Change" event to fill in the filter. Couple it with:
If Not Application.Intersect(Target,rngRange) is Nothing Then
' Your code here
' Where rngRange is a Range object that you want the change event to catch
End If
Another note is, you can use (considering data starts at "A1") Range("A1").CurrentRegion if you don't have any completely empty rows within your data instead of .End(xlUp). Actually you can use CurrentRegion in any cell that is inside the data range. Check out the behaviour by pressing CTRL + * after selecting a cell.
Dim rngData as Range
Set rngData = ws.Range("A1").CurrentRegion
' ws can be a worksheet object you can set alike range above or worksheet codename
' or Worksheet("SheetName")
' Then rngData.Row will give the top row of rngData
' rngData.Column will give you you the leftmost column
' rngData.Rows.Count will give the number of rows
' rngData.Columns.Count will give the number of columns
' rngData.resize() and rngData.Offset() helps a lot too, can be used in same line
' i.e., rngData.offset(2,0).resize(1,1) : move the range 2 rows down, resize to 1 row,1 column
' Do whatever with rngData
' After you are done clean up, doesn't matter most of the time in VBA but this is better
' from what I have read/learned from others
Set rngData = Nothing
This may not be the exact answer you are looking for, but may get you started. The video is 20 minutes long, see if it can be used in your case.
Edit 1: To further clarify
rngData part is more a general approach. You haven't said anything about being able to use Advanced Filtering but here how I would do it.
Assume, we have 3 sheets. wsData, wsFilter, wsReport.
wsData would be the sheet where data is entered.
wsFilter would only hold the filter criteria.
wsReport would be the sheet that you will export.
wsData, as an example assume row 1 is the header row.
wsFilter would be only 2 columns (with headers corresponding to A and AG), corresponding to Column A and AG.
On wsReport, you only have to clear contents and enter the column headers from wsData that you would want to appear on the customized report.
You will need 3 ranges, 1 for each worksheet. i.e., rngData, rngFilter, rngReport
The code to creating the report is easy as:
rngData.AdvancedFilter xlFilterCopy, rngFilter, rngReport
(I won't get into how to decide on the ranges as the video does it better than I would be able to).
Then next step should be: How to enter the filter criteria?
I am tearing my hair out on this one.
I am trying to add the value from a userform textbox to a table.
However Excel is constantly crashing on me as soon as it runs the code below.
The error message i get is
runtime error -2147417848 "Method 'Value'of object 'Range' failed
then excel crashes
I have tried Option explicit to check i wasnt missing a variable or it was declared incorrectly, i have tried deleting the table and starting again, i have started a new workbook, i have change the table name, i have tried 4/5 different methods of adding the data to the table (Simple range offset, databodyrange(X,1), resizing the table etc). All crash when adding the value (which by the way is just text like mike/harry etc)
The workbook, has about 10 forms and they all work perfectly (they add data to tables etc), it is just this one causing issues
If i manually add data to the table it auto extends and have no issues
any help is appreciated.
Sub Enterprise_Update()
Dim lst As ListObject
Set lst = Sheets("Data Labels").ListObjects("Enterprises")
For Each ctrl In Enterprise_Setup.Controls
If ctrl.Name Like "Enterprise Name Value 1*" Then
z = z + 1
End If
Next ctrl
With lst.Sort
.SortFields.Clear
.Apply
End With
With lst
LstRw = .ListRows.Count
End With
Select Case LstRw
Case Is = 1
lst.DataBodyRange(LstRw, 1).Offset(1, 0).Value = Enterprise_Setup.Controls("Enterprise Name Value 1" & x)
Case Else
For x = 1 To z
sLookFor = CStr(Enterprise_Setup.Controls("Enterprise Name Value 1" & x))
Set oLookin = lst.DataBodyRange
Set oFound = oLookin.Find(what:=sLookFor, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=True)
If Not oFound Is Nothing Then
GoTo err:
Else
With lst
LstRw = .ListRows.Count
End With
End If
r = Enterprise_Setup.Controls("Enterprise Name Value 1" & x).Value
Sheets("Data Labels").Select
Range(lst.Range.Cells(1).Address).End(xlDown).Offset(1, 0).Select
ActiveCell.Value = r
'lst.DataBodyRange(X).Value = r
err:
Next
End Select
With lst.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("Enterprises[Enterprises]"), Order:=xlAscending
.Header = xlYes
.Apply
End With
End Sub
I'm looking at this portion of your code and have some comments on it.
If Not oFound Is Nothing Then
GoTo err
Else
LstRw = Lst.ListRows.Count
End If
r = Enterprise_Setup.Controls("Enterprise Name Value 1" & x).Value
Sheets("Data Labels").Select
Range(Lst.Range.Cells(1).Address).End(xlDown).Offset(1, 0).Select
ActiveCell.Value = r
For one, GoTo err? Err is VBA's error object. VBA is very good at avoiding errors from intrusions on its naming prerogative but I still think its asking for trouble. Note that the final colon is required to identify the label but not the destination statement.
Using labels to jump back and forth in the code isn't good practice. Nor is it needed in this case. A simple If Not oFound Is Nothing Then should do the job if you extend the End If to the point where you have the label. Anyway, what's the point of LstRw = Lst.ListRows.Count? You took that measure before the Select Case statement. Has it changed?
But most of all, I question why you would jump if you found what you were looking for and process when you didn't. This is the best candidate for the error you see.
Selecting another sheet is not good practice and not required, either. You can read from and write to the sheet without selecting it. If you are selecting the sheet from a user form Excel might consider this cause for divorce. Of course, selecting a cell is equally unnecessary.
Range(Lst.Range.Cells(1).Address).End(xlDown).Offset(1, 0) probably works, although I think it's adventurous. But selecting that cell may be problematic when done from within a user form. The form sort of claims right of first attention. I doubt that you can activate that cell from where you are.
You can write to it, however. Sheets("Data Labels").Cells(54, "A").Value = r would be a great success, I feel confident. The contortions you undertake to describe the cell's coordinates aren't necessary. The column seems to be Lst.Range.Column and the row would be Lst.Range.Row + LstRw or perhaps Lst.DataBodyRange.Row + LstRw.
However, there might be additional problems resulting from the location of the cell in a table or just under a table where adding a row might create conflict with data existing there. If the table is required to expand as a result of writing to that cell Excel would normally just over-write whatever might exist but it's a point worth considering if all other options have been exhausted.
I hope this analysis will help you find and correct the error.
I solved the issue by a less than pretty way. I managed to extend the table, and code around the blank rows, ie count none blanks, dynamic named ranges etc
I'm trying to build a formula:
=BDS(Bonds!J2& " ISIN","ISSUE_UNDERWRITER","Headers","Y")
In one sheet that takes a unique identifier from another table.
These formula builds me a table. After it builds me the table, I need to take the next row in the other sheet:
=BDS(Bonds!J3& " ISIN","ISSUE_UNDERWRITER","Headers","Y")
Then insert that formula a the end of the previous table built by the previous formula.
What I tried was getting the last row and then offsetting it by one, but I'm trying to figure out how to loop through it.
This is what i have tried:
Sub Formula2()Formula2 Macro
Range("A1").Select
ActiveCell.FormulaR1C1 = _
"=BDS(Bonds!R[1]C[9]& "" ISIN"",""ISSUE_UNDERWRITER"",""Headers"",""Y"")"
lRow = Cells(Rows.Count, 1).End(xlUp).Select
Selection.Offset(1, 0).Select
ActiveCell.FormulaR1C1 = _
"=BDS(Bonds!R[-53]C10& "" ISIN"",""ISSUE_UNDERWRITER"",""Headers"",""Y"")"
Range("A57").Select
End Sub
Image of Table, Im trying to iterate through the ISIN Column. It is column "J"
Although selection and .select are used by the macro recorder, they cause big problems when developing code. It's worth your time to learn how to replace them with range objects. So, while I'm not directly answering your question, I'm trying to give you the tools to do so.
I've shown an example below to illustrate (although I do not work with the BDS() function so I'm undoubtedly getting the details wrong). The main point is that if you learn to move around using the range object you'll be much better off.
Sub formula()
Dim r As Range, sh As Worksheet, bondR As Range, bondSh as Worksheet
set sh = ActiveSheet
set r = sh.range("A1")
Set bondSh = Worksheets("Bonds")
Set bondR = bondSh.Range("J1")
For i = 1 To 10
r.formula = "=BDS(bondR.offset(i,0) & "" ISIN"",""ISSUE_UNDERWRITER"",""Headers"",""Y"")"
Set r = r.Offset(i, 0)
Next i
End Sub
Here I'm defining one range object, r, to track the location on the active sheet, and another, bondR, for the location on the "Bonds" sheet. Once the initial locations of these ranges are defined, you can manipulate them using the .offset(row,col) function as I've done with the simple for-loop, moving down 1 row (but 0 columns) in each loop.
Feel free to ask questions.
I am trying to filter a table on the first worksheet ("Data") for each of the items that appear in a table on the second worksheet ("Hosts"), and then paste the filtered results in separate worksheets, each named after the corresponding item on the table.
My understanding of VBA is very basic and I have tried to put together a collage of codes from other users, but it doesn't seem to work properly for me:
The first loop creates worksheets based on the items on the "Hosts" table, but for some reason it adds an extra sheet before the ones I need and calls it "Sheet1"
The second loop simply doesn't work
Are two loops really necessary, or is it possible to combine the two?
This is the code I have so far:
Sub test()
Dim AllData As Worksheet
Dim HostList As Worksheet
Dim DataRange As Range
Dim FilterColumn As Long
Set AllData = ThisWorkbook.Worksheets("Data")
Set HostList = ThisWorkbook.Worksheets("Hosts")
Set DataRange = AllData.Range(Range("A1"), Range("A1").SpecialCells(xlLastCell))
Dim HostValues As Range
For Each HostValues In HostList.ListObjects("Table1").Range
With ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
On Error Resume Next
ActiveSheet.Name = HostValues.Value
If Err.Number = 1004 Then
Debug.Print HostValues.Value & "already used as a sheet name"
End If
On Error GoTo 0
End With
Next HostValues
For Each HostValues In HostList.ListObjects("Table1").Range
AllData.Activate
FilterColumn = 18
DataRange.AutoFilter Field:=FilterColumn, Criteria1:=HostValues
DataRange.SpecialCells(xlCellTypeVisible).Copy
Sheets(HostValues.Text).Range("A1").PasteSpecial xlPasteValues
Selection.Sort Key1:=Range("V:V"), Order1:=xlAscending, Header:=xlGuess
AllData.Activate
Cells.AutoFilter
Next HostValues
End Sub
Some kind soul out there please help me!
There's quite a bit to do here, but I'll give it a shot.
The first loop creates worksheets based on the items on the "Hosts" table, but for some reason it adds an extra sheet before the ones I need and calls it "Sheet1"
My guess here is that Hosts contains a duplicate entry or something is causing the sheet rename section to fail. I would check the debug window for that. Or change
Debug.Print HostValues.Value & "already used as a sheet name"
to
msgBox HostValues.Value & "already used as a sheet name"
That will make a popup happen, should make it easier to see when the error happens. Something else you can try, comment out the two OnError statements with a ' single quote. Then when an error is raised you can hit debug and work through what the program is upset with.
The second loop simply doesn't work
I'm not sure on this one. When you use a For Each many times changing the collection it is operating on will give you some sort of problem. You've asked the computer to do something for every cell in this column, and then you change the values of the column. That's just a guess.
Are two loops really necessary, or is it possible to combine the two?
You can combine the two, after creating the sheet for the Host you can move its data over to it.
Notes
The approach with filtering may be giving you undue complexity try writing a loop without the filters and checking if the Host has a sheet, if it does move the data. If it does not create it and move the data.
You do not need the With/End With block at all.
On Error Resume Next is dangerous. It has its uses, take a look at this for more information or handling errors.
Godspeed.