Run a macro multiple times down the sheet - vba

I have a table that takes user input data and fills in the rest of the table from the data. So far I have created a macro that automatically fills in the table from the users data.
I want to have the user input the amount of times they want to run the macro (as each table displays a days worth of input) but every time the macro runs it just fills in the same space and doesn't display multiple days worth of tables.

Not knowing your macro, let's assume you have a generic macro that stores user input in a column:
Sub InputMacro()
Range("B1:B100").Value = InputBox("What do you want to store in column B")
End Sub
If you want to loop over this, you first need to parameterize the macro, i.e. make the part flexible that you want to change it in each loop step. E.g. if you want to run over multiple columns, you could do this:
Sub InputMacro_new(intColNumber As Integer, Optional lngRowNumber As Long = 100)
Cells(1, intColNumber).Resize(lngRowNumber) = _
InputBox("What do you want to store in column " & _
Split(Cells(1, intColNumber).Address, "$")(1))
End Sub
This will now accept a parameter intColNumber, i.e. you can call InputMacro_New 2 to fill column B. Note that I also provided an optional parameter lngRowNumberthat by default is 100. You do not need to provided this parameter - but if you want you can override the default, e.g. InputMacro_New 2, 50)
Now you can create a loop in another macro that calls your macro:
Sub MyLoop()
Dim intCol As Integer, varMaxCols As Variant
varMaxCols = InputBox("How many columns do you want to fill?")
If Not IsNumber(varMaxCols) Then Exit Sub
ElseIf varMaxCols < 1 Or varMaxCols > 255 Then Exit Sub
For intCol = 1 To varMaxCols
InputMacro_new intCol
Next intCol
End Sub
Of course, if you want you can also combine both into one larger macro - but esp. for larger macros it is best practice to split it into smaller procedures.

Related

VBA: Finding the row number by looking up a user defined value (date)

I am trying to use VBA to look up the row value that corresponds to a user defined date on one of my work sheets so that I am able to edit all data on that row.
As a bit of context:
I have several time series data sets that all have different start and end dates with a good portion of overlap in the middle. I want to chart these using user defined date parameters, however, because of non-uniform start dates, the chart is impossible to dynamically rebase.
I was hoping to use a macro to clone the data on one sheet, overwrite the line of values that corresponds to the user defined start date, and then calculate return values based on percentage change figures (I already have in a different sheet).
If I can dynamically o/w the row that corresponds to the UD start date of the date range, I can replace it with a one and all my calculations will effectively rebase.
Any and all feedback would be great!
EDIT
Lucas,
I am having two issues; firstly, what I have inexpertly cobbled together doesn't work when I protect the sheets (not insurmountable); secondly, it doesn't work :). Here is my work:
Sub Rebase()
Dim UDStartVal
Dim UDStartLoc As Range
Dim UDRow As Integer
'
' Rebase Macro
' A macro to rebase the chart to the user defined start date.
'
'
Sheets("Cumulative Monthly Returns").Select
Cells.Select
Selection.Copy
Sheets("Chart Numbers").Select
Range("A1").Select
ActiveSheet.Paste
' Lookup to change the value of the cells corresponding to the user defined start date to 0, effectivley rebasing the portfolo.
Worksheets("Cumulative Period Returns").Activate
UDStartVal = Cells(4, 2).Value
Set UDStartLoc = Range("A:A").SpecialCells(xlCellTypeVisible).Find(UDStartVal)
Set UDRow = UDStartLoc.Row
Stop
End Sub
Here's some code that I use to find the row of an entry based on quote numbers on a sheet that gets resorted and re-filtered constantly.
Private Sub FindQuote(partNum as String)
Dim quoteRow as Range
Set quoteRow = Range("A:A").SpecialCells(xlCellTypeVisible).Find(partNum)
then when I want to do something that uses the range of that row I use quoteRow.Row
If Not quoteRow Is Nothing Then
quoteNum = Cells(quoteRow.Row, "P").Value
Cells(quoteRow.Row, "Q").Value = "Found"
Else
MsgBox "No quote was found"
End If
End Sub
Did you need help with the part where you clone your sheet?

How to delete unselected columns from range

I am new to VBA and am trying to delete unwanted columns loaded from a .csv file. I am importing a large amount of data but then I ask the user what columns they want to keep going by "ID num.". There are a lot of columns with different ID no. and I want to ask the user what they want to keep and delete the rest.
The problem is I need to delete all the other columns the user didn't want but I still need to keep the first 6 columns and the last two columns as that is different information.
Here is what I have so far:
Sub Select()
'the below will take the users inputs
UserValue = InputBox("Give the ID no. to keep seperating with a comma e.g"12,13,14")
'the below will pass the user inputs to the example to split the values
Call Example(UserValue)
End Sub
Sub Example(UserValue)
TestColArray() = Split(UserValue, ",")
For Each TestCol In TestColArray()
' keep all the columns user wants the delete the rest except the first 6 columns and last 2
Next TestCol
End Sub
That is what I have so far, it is not much but the user could put in a lot of columns with different ID number in the input box the way the Excel sheet is laid out all the ID no.s are in row 2 and the first 6 and last 2 columns are blank of row 2 since the ID no. does not apply. I hope that helps.
try this (commented) code:
Option Explicit '<--| use this statament: at the cost of having to declare all used variable, your code will be much easier to debug and maintainable
Sub MySelect()
Dim UserValue As String
'the below will take the users inputs
UserValue = Application.InputBox("Give the ID no. to keep seperating with a comma e.g: ""12,13,14""", Type:=2) '<--| use Type:=2 to force a string input
'the below will pass the user inputs to the example to split the values
Example UserValue '<--| syntax 'Call Example(UserValue)' is old
End Sub
Sub Example(UserValue As String)
Dim TestCol As Variant
Dim cellsToKeep As String
Dim firstIDRng As Range, lastIDRng As Range, IDRng As Range, f As Range
Set firstIDRng = Range("A2").End(xlToRight) '<-- first ID cell
Set lastIDRng = Cells(2, Columns.Count).End(xlToLeft) '<-- last ID cell
Set IDRng = Range(firstIDRng, lastIDRng) '<--| IDs range
cellsToKeep = firstIDRng.Offset(, -6).Resize(, 6).Address(False, False) & "," '<--| initialize cells-to-keep addresses list with the first six blank cells at the left of first ID
For Each TestCol In Split(Replace(UserValue, " ", ""), ",") '<--| loop through passed ID's
Set f = IDRng.Find(what:=TestCol, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False) '<--| search for the current passed IDs range
If Not f Is Nothing Then cellsToKeep = cellsToKeep & f.Address(False, False) & "," '<--| if the current ID is found then update cells-to-keep addresses list
Next TestCol
cellsToKeep = cellsToKeep & lastIDRng.Offset(, 1).Resize(, 2).Address(False, False) '<--| finish cells-to-keep addresses list with the firts two blank cells at the right of last ID
Range(cellsToKeep).EntireColumn.Hidden = True '<-- hide columns-to-keep
ActiveSheet.UsedRange.EntireColumn.SpecialCells(xlCellTypeVisible).EntireColumn.Delete '<--| delete only visible rows
ActiveSheet.UsedRange.EntireColumn.Hidden = False '<-- unhide columns
End Sub
it's assumed to be working with currently active worksheet
A simple google search produces this. On the first page of results too. Perhaps this will suit your needs.
If the data set that needs to be deleted is really large (larger than the ranges you want to keep too.) Then perhaps only select the columns you want to have whilst you import the csv? This stackoverflow question shows how to import specific columns.
EDIT:
So from what I believe the OP is stating as the problem, there is a large csv file that is being imported into excel. After importing there is alot of redundant columns that should be deleted. My first thought would be to only import the needed data (columns) in the first place. This is possible via VBA by using the .TextToColumns method with the FieldInfo argument. As stated above, the stackoverflow question linked above provides a means of doing so.
If the selective importing is not an option, and you are still keen on making an inverse of the user selection. One option would be to create 2 ranges (one being the user selected Ranges and the second being the entire sheet), you could perform an intersect check between the two ranges and delete the range if there is no intersection present (ie. delete any cell that is not part of the users selection). This method is provided by the first link I supplied and is quite straight forward.

Filtering out Area codes in Excel by VBA

need some assistance. It's been a very long time since I've done scripting/coding and really rusty now.
Issue: I have a spreadsheet(Excel Visual Basic) that contains call(s) over a month(Over 30k records). My goal is to filter out calls that have specific area codes (toll free, local calls...etc) and calculate how much they owe from calling long distance.
Additionally, this is a retirement home and the end-user(s) do not enter phone numbers correctly. I need to either strip 3-4 digits at the start of numbers..
Example: 1800-XXX-XXXX or 800-XXX-XXXX | 1605-XXX-XXXX
Here's the code I currently have, I'm lost on how to incorporate the 3-4 digits in the area and have it parse each record in Column H by the first 3-4 digits
Phone numbers are stored in Column H
Array TFL will store all the area codes I need to filter out.
Sub CleanEntry()
Dim i As Integer
Dim TFL As Variant
TFL = Array("1800", "1877")
For i = Sheet1.UsedRange.Rows.Count To 1 Step -1
If Left(Cells(i, "H"), 4) Like TFL(i) Then
Sheet1.Rows(i).EntireRow.Delete
End If
Next
End Sub
I'd personally use a regular expression for this:
Sub CleanEntry()
Dim i As Integer
Dim filter As New RegExp
filter.Pattern = "^1?(8(77|00))|605" 'Or whatever else you need to match.
For i = Sheet1.UsedRange.Rows.Count To 1 Step -1
If filter.Test(Trim$(Cells(i, "H").Value)) Then
Sheet1.Rows(i).EntireRow.Delete
End If
Next
End Sub
Note: You'll need to add a reference to Microsoft VBScript Regular Expressions 5.5.

Incrementing the numeric part of an alphanumeric criteria to search multiple columns and print records with Excel VBA

I should note that there are related solutions to my question online but I've been unable to implement them into my own situation.
We have an .mdb database of all the products that we make. I've managed to take two criteria (Order type and Box), and print all records containing those two criteria to Excel. What I need in addition to that now is to print 30 boxes in one go as a basis for a bigger template. The labeling of these boxes usually increment (e.g. P1, P2...P30), and I'm struggling to see how I can increment the numeric portion of it to fit it into my code. Ideally, I'd like for the user to input the first and last box numbers in excel to represent the entire range (P1 and P30) and use those two values.
Sub Dan()
Dim order As String
Dim title As String 'initialize title
Dim palette As String 'intialize comment
Dim finalpalette As String
Dim finalrow As Integer 'initialize bottom-most row
Dim i As Integer
Dim Cntr As Integer
Dim LR As Integer
'Clears the contents of the last macro run
With Sheets("ALL.txt")
.Range(.Cells(6, 2), .Cells(725, 8)).ClearContents 'equates to (D2:F26)/ row, column ;Erase Columns for next macro
End With
title = Sheets("Sheet2").Range("B1").Value
palette = Sheets("Sheet2").Range("B2").Value
finalrow = Sheets("Sheet1").Range("A2").End(xlDown).Row
For i = 3 To finalrow
If Cells(i, 1) = title And Cells(i, 2) = palette Then
Cells(i, 5).Copy 'Copy ID
Sheets("ALL.txt").Range("B734").End(xlUp).Offset(1, 0).PasteSpecial
Range(Cells(i, 11), Cells(i, 14)).Copy
Sheets("ALL.txt").Range("C734").End(xlUp).Offset(1, 0).PasteSpecial
Range(Cells(i, 9), Cells(i, 10)).Copy
Sheets("ALL.txt").Range("G734").End(xlUp).Offset(1, 0).PasteSpecial
End If
Next i
End Sub
The variable I'm looking to adjust is 'palette'. I originally used it to match records to one Box (P1). What I need is to able to match records from 30 boxes (P1 to P30) in the loop. The variable 'palette' is just taking the static value of whatever is in cell B2 at the moment. I'm thinking there should be some way to type the first and last box into two cells to establish a range for the macro to iterate, or to write all the box numbers into a column and have 'palette' move down a cell each loop to take in a new Box value.
In an attempt to grab data from a column that has all 30 boxes written into 30 cells, I tried the following line of code
End If
palette = Sheets("Sheet2").Range("B2").Offset(, 1)
Next i
but it does not seem to be grabbing any value. It should be grabbing values from cells B2 to B31.
Here is some code that I changed (still no clue as to why you're breaking this up into 3 parts, seems like excel VBA is an extra step that complicates it).
thisworkbook.worksheets(1).cells(i,5) Use full references when learning VBA
let me know if this works, I don't know enough about your situation to know exactly what you need, other than what I can see you're trying to do.
Sub Dan()
Dim Order As String
Dim Title As String 'initialize title
Dim Palette As String 'intialize comment
Dim Fpalette As String
Dim Frow As Integer 'initialize bottom-most row
Dim i As Integer
Dim Cntr As Integer
Dim LR As Integer
Dim wsALL As Worksheet
'Clears the contents of the last macro run
With Sheets("ALL.txt")
.Range(.Cells(6, 2), .Cells(725, 8)).ClearContents 'equates to (D2:F26)/ row, column ;Erase Columns for next macro
End With
Title = Sheets("Sheet2").Range("B1").Value
Palette = Sheets("Sheet2").Range("B2").Value
Frow = Sheets("Sheet1").Range("A2").End(xlDown).Row
Set wsALL = Sheets("ALL.txt")
i = 2
Do While i < Frow
i = i + 1
If ThisWorkbook.Worksheets("Sheet1").Cells(i, 1) = Title And ThisWorkbook.Worksheets("Sheet1").Cells(i, 2) = Palette Then
Sheets("Sheet1").Cells(i, 5).Copy Destination:=wsALL.Range("B734").End(xlUp).Offset(1, 0)
'wsALL.Range("B734").End(xlUp).Offset(1, 0).PasteSpecial
Sheets("Sheet1").Range(Cells(i, 11), Cells(i, 14)).Copy Destination:=wsALL.Range("C734").End(xlUp).Offset(1, 0)
'wsALL.Range("C734").End(xlUp).Offset(1, 0).PasteSpecial
Sheets("Sheet1").Range(Cells(i, 9), Cells(i, 10)).Copy Destination:=wsALL.Range("G734").End(xlUp).Offset(1, 0)
'wsALL.Range("G734").End(xlUp).Offset(1, 0).PasteSpecial
End If
Loop
End Sub
Ignore the Below, I was going to make this way more complicated than necessary. Looking at your code, be sure to reference using
Hi Joshua,
I'm not sure I completely understand what you're trying to accomplish, adding in more details such as the first macro may help in getting you a specific answer. I think possibly VBA in Excel may not be the best way. A VBA in Access sounds possible solution. But this may be of help to you.
I know you said for an end user, It would be much more complicated on your part but I've had great success using microsoft query to import data, with the correct ODBC driver "Access Database Engine" http://www.microsoft.com/en-us/download/details.aspx?id=13255 it works great now and I use it to get data from flat files then send it to SQL based on a query, but I fought with it to get it to work you will rip your hair out and it wouldn't be portable to an end user
Having a user enter a value into a specific cell could work, i.e. put a value in A1 and VBA can check that value using:
Alpha = Cells(1,1).Value
pStart = Cells(2,1).Value 'A2
pEnd = pStart + 30
In order to prevent any issues with spaces this could be done as:
set pStart = Trim(ActiveCell(2,1).Value)
Or another way is to use data validation and give users a drop down list. https://support.office.com/en-ca/article/Create-or-remove-a-drop-down-list-5a598f31-68f9-4db7-b65e-58bb342132f7
Here is the code if for either way. Notice I've made some edits, most are not essential changes, just how I write VBA. When you use the copy -> paste command it avoids the clipboard if you say .Copy Destination:= Another comment, this would be so easy in Access simply write an SQL statement and use the append feature. You say that you have a macro before this, and after this, I would say make it one (very powerful and nice) SQL statement what is run through a user form.

Macro query spread over multiple-sheets

Wording my question is slightly tricky so I've included screen-shots to make this easier. I have 2 separate spreadsheets which are currently not linked together in anyway. What I've been asked to do is:
For the drop-downs which have a * next to them, have this * drop-down get converted into a acronym (I.e. If it's Home Visit *, then this will be converted to HV), and have it automatically entered into Cell Position X. Please refer to Image 1 then Image 2)
So the user would click on Sheet one, select the relevant drop-down field and then assign how much time that task took. The second sheet would then update itself with this information - it would insert the users name, program and activities. This is where it gets very tricky. Based off the drop-down selection, if it is asterisked (*), then based off the field-type it will convert it into a set acronym which would then be placed in one of the data fields based off the entry date that has been provided.
I designed both spread-sheets and they have macros in the background, but I can't seem to work out how to best perform this. Would you suggest a transpose function which checks firstly the date criteria and then an INDEX(MATCH) function to match the criteria against a pre-defined name-range which converts Home Visit etc. to HV automatically? I'm also unsure of how to insert delimiters for each new entry that is read. If anyone can provide help I would be very grateful.
I'm not 100% sure I understand your question, but here goes:
What about adding a Worksheet_Change event to look for changes in the drop-down's cell, and then converting it to an acronym?
Place the following code inside the sheet of interest:
Private Sub Worksheet_Change(ByVal Target As Range)
'If Cell A1 is changed, put the acronym into A2
If Target.Row = 1 And Target.Column = 1 Then
Cells(2, 1) = GetAcronym(Target.Value)
End If
End Sub
Function GetAcronym(TheText As String) As String
Dim result As String
Dim x As Long
'Always grab the first letter
result = Mid(TheText, 1, 1)
'Get the other letters
For x = 2 To Len(TheText) - 1
If Mid(TheText, x, 1) = " " Then result = result & Mid(TheText, x + 1, 1)
Next x
GetAcronym = UCase(result)
End Function