Selecting columns based on specific text strings - vba

I'm trying to put together a macro which will select certain columns and paste them into a new sheet. The problem is that columns tend to be added and deleted as people see fit which throws off the absolute referencing.
I have been trying to tinker with the basic macro produced using the macro recorder but I haven't had any luck selecting columns based on their contents. I have a sheet that is generated from our database daily with changing fields. For example, I would like to select just the Part #, Cost, and Contact fields but the addition of the IDN today threw my old macro off.
So far, I've tried to use basic excel find functions like vlookup, index against a list of constants and the find function below but none seem to work. Is there something I am missing here to select the column with my desired text?
Columns(find("Part #")).Select

Firstly, be careful with .Select and it looks like you're not fully qualifying your references to the worksheet so take note of the full Workbook.Worksheet.Range type referencing below.
Here's your quick fix:
Public Sub Test()
'the Range approach
ThisWorkbook.Worksheets("Sheet1").Rows(1).Find(What:="Part #", LookAt:=xlWhole).EntireColumn.Copy
ThisWorkbook.Worksheets("Sheet2").Range("A1").PasteSpecial xlPasteValuesAndNumberFormats 'or xlPasteAll to include formulas
End Sub
Using entire column ranges isn't usually the best idea. It will work, but only as long as you remember to paste your copied column into the first row of the destination worksheet every time.
Here's the better option (IMO):
Convert your data to a Table (know as a ListObject in VBA) by clicking "format as table" on the home tab of the ribbon. It's now much easier to reference your column - just do it by name, no need to use Find.
Public Sub Test()
'the ListObject approach
ThisWorkbook.Worksheets("Sheet1").ListObjects("MyTable").ListColumns("Part #").Copy
ThisWorkbook.Worksheets("Sheet2").Range("A1").PasteSpecial xlPasteValuesAndNumberFormats 'or xlPasteAll to include formulas
End Sub
You need to know the name of your table (I called it MyTable here), which you can do by clicking on "Table" on the ribbon when your table is selected

You need to specifically find the column, then you can paste off to a destination location, such as:
Sheets("Source").Columns(Sheets("Source").Rows(1).Find("Part #").Column).Copy Sheets("Dest").Cells(1,1)

Related

How to use index match formula in VBA excel

I'm trying to include this index match formula in my VBA script. However, I'm having issues getting it to apply the proper formula to each line. This code adds a new column at AK and then inserts the formula.
Sub test()
Columns("AK:AK").Insert Shift:=xlToRight
Range("AK:AK").Value = "=INDEX('[BCHP Group Info Updated.xlsx]Pay to Provider (Edit) (2)'!$J:$J,MATCH(AG1,AG:AG,0))"
End Sub
The issue comes in where "MATCH(AG1" would only apply to row one and I need it to continue down for the rest of the data(MATCH(AG2,MATCH(AG3, etc.)
Is there another to reference another worksheet and match the data in order to fill this column? Or perhaps an easier way to do this with an If then statement?
Thanks

Search xlsx for a value, if found replace with different value

So I'm either being too specific, or not searching well enough, because I can't find anything that answers my question. So I came here for help. Here is the situation:
I have an excel sheet, let's call it "CustomerCodeReference", that has a column (A) of Customer Codes (I.E. A2001, A2002, B3900, Q2838, etc, these are NOT necessarily in order) About 3000 of them, and in the next column over (B), I have the group that code represents (I.E. Accounts Primary, Accounts Secondary, Admin Group, User Group, just different names and etc.)
Now, from our company server I can export a spreadsheet of reports from customers, but the problem is, they are labelled by customer code, + a report serial number. The sheet exports as several columns, but one of the columns (G) contains the Customer code and serial number, and each row is a report, sometimes hundreds depending on the date range set. So keeping with the example, let's say it's a report from "Accounts Primary" It's labelled A2001234567 (where everything after the customer code of 'A2001' is the report serial number) sometimes, the report may be from several customers, so that column may have more than one code+SN in it per row.
Given that I have thousands of these codes and groups, is there some macro I can create that every time I export the spreadsheet of reports, I can maybe copy over the "CustomerCodeReference" sheet, and have it automatically search the column of customer codes and SNs, then either replace the code with the actual name, or place the actual name in another (empty) row further back. So I can basically easily reference whose report it is without having to look up the code each time?
I realize I will need to do this in VBA, as there is no formula I can think of that will work.
I have some pro's I think going for me:
-I already have the Master code list, so even though there are thousands of codes, they are all listed in Column A, and the actual name of group they reference is in column B.
-The codes are consistent, a letter, followed by 4 numbers, so always 5 characters long.
-When pulling the report, it always names the worksheet "Customer Reports" so it's easy to reference
These are constants. So I need the actual customer name to either replace the code (while leaving the serial number intact) or if easier, add the actual name to the next empty column on the same row. I also might need to share this with coworkers, so basically just send them the "CustomerCodeReference" sheet and when they add it to all their pulled spreadsheets, it does the same thing. (Macros will be enabled, so no worries there)
Is this too complicated an idea? or can I pull it off? Thanks in advance for the help!
EDIT: I apologize, I complete forgot to attach any sort of code. Here is what I have come up with in VBA, but not sure if I am on the right track as it does not complete the replacement, and I can't quite get it to add values in next available empty cell.
Sub replaceStringInCell()
'declaring my sheet I want to change change customer codes in
Dim CustomerCodes As Range
'declaring strings I will be replacing and with what I will be replacing them
Dim ReportNumbers As Range
Dim CustomerNames As Range
'identifying column I am working to replace, also trying to shoot for next empty column
Set CustomerCodes = PulledReports.Worksheets("Customer Reports").Range("G:G")
'specifying my strings
ReportNumbers = PulledReports.Worksheets("Customer Reports").Range("G:G")
myReplacementString = PulledReports.Worksheets("Customer Code Reference").Range("A:A")
'replace string in cell
CustomerCodes.Value = Replace(Expression:=CustomerCodes.Value, Find:=ReportNumbers, Replace:=CustomerNames)
End Sub
This should do the trick:
Sub stack_overflow()
Dim cust As Worksheet
Dim ref As Worksheet
Set cust = ActiveWorkbook.Worksheets("Customer Reports")
Set ref = ActiveWorkbook.Worksheets("Customer Code Reference")
'Finding next empty column
Dim column As Integer
column = cust.UsedRange.Columns.column + 1
'Filling this columns
For Each cell In cust.Range("G2:G" & cust.Cells(Rows.Count, "G").End(xlUp).Row)
cust.Cells(cell.Row, column).Value = Application.WorksheetFunction.VLookup(Left(cell.Value, 5), _
ref.Range("A2:B" & ref.Cells(Rows.Count, "B").End(xlUp).Row), 2, False)
Next cell
End Sub

Saving cell Presets in a dropdown list

At the moment I have created a spreadsheet which takes a bunch of inputs, runs them through a list of formulas, and then spits out the results onto a "report" worksheet.
I've been manually saving each of these reports as separate CSVs but I was hoping for a better method moving forward as it is getting quite tiring to have to open 10 CSVs when i do my monthly reports.
I am looking for a way to start saving all of these reports into a "database". My hope to to have one cell be for an user entry name and for two buttons. One to save the current report under the name entered by the user, and two to remove old records. I would then be able to revisit old entries by selecting them in the dropdown.
I've dabbled with VBA and Macros in the past but this is a little more complicated than what I've dealt with in the past. Looking for some help/direction.
Thanks for your time!
Depending on how your reports need to be used, you might find it satisfactory to simply make your data into one big Excel Table ( Insert Tab > Table ). When you do this, Excel will automatically fill-down any formulas that you enter in a column, and also show the formula using the headers instead of A1-style references.
I use this format, adding Y under Remove from Active List on each line that is already done. Then whenever I save the file or look at it for today's status, I filter out what's old and just look at the new. The other filters enable copy-pasting or printing whatever arrangement I like.
The filters and other things in the table can be referenced in VBA as Sheets("ThisSheet").ListObjects(1), which is an object with a number of useful properties and methods.
For VBA information, read more here: https://www.thespreadsheetguru.com/blog/2014/6/20/the-vba-guide-to-listobject-excel-tables
This is my code for auto-filtering the table to hide inactive items at time of save. You add it at ThisWorkbook in the VBA editor:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Sheets("Sheet1").Activate
SelectedCell = ActiveCell.Address 'this saves your screen selection for after the filtering
ActiveSheet.ListObjects(1).Range(1, 1).Select
If ActiveSheet.ListObjects(1).AutoFilter.FilterMode = True Then
ActiveSheet.ListObjects.Item(1).AutoFilter.ShowAllData
End If
A = ActiveSheet.ListObjects(1).Range.Rows(1).Find("Remove from List").Column - _
ActiveSheet.ListObjects(1).Range.Column + 1
ActiveSheet.ListObjects(1).Range.AutoFilter field:=A, Criteria1:="="
Range(SelectedCell).Select
End sub

Keeping column formulas in a table header

Is it possible to have the formulas that I need applied on columns be saved or applied to a column header or some kind of metadata so that as and when I add new rows to my Excel table the Formulas get applied to the columns?
Scenarion:
I am creating a template Table, which will have no rows at first.
On a separate sheet (or same sheet for that matter) once the user selects the number of rows to be generated in the table, I dynamically add rows to the table using VBA.
The idea is I may not have any rows in the table at beginning OR user may have deleted rows manually.
When I programmatically add new rows, I want the Formulas applied on the cells as well. Most of the formulas I am using are either of the three types:
Structured table reference, Excel functions like SUM, AVERAGE etc and custom function names.
Updated:
Here is what I have tried:
1> tried applying the formula to the header itself.
Result: The header it self changes with #REF! error. I think the behavior is correct. So it's a no-go option.
2> Tried creating one row and apply the formula to the row. That works, but the problem is, I do not want a dummy row to begin with.
3> Using VBA code to add row to the table using
ActiveWorkbook.Worksheets("Sheet3").ListObjects("Table2").ListRows.Add AlwaysInsert:=True
inside a for loop.
The new rows retain the visual style sheets, but does not seem to retain the formulas. Just blank cells.
Could the fomrmulas be in header cell commnets?
And then with VBA add the formula for the current row:
Sub test()
Dim headerCells As Range
Set headerCells = Range("B2:E2")
OnNewRow 3, headerCells
End Sub
Sub OnNewRow(newRow As Integer, headerCells As Range)
Dim headerCell As Range, targetCell As Range, formulaFromComment As String
For Each headerCell In headerCells
formulaFromComment = GetFormulaFromComment(headerCell)
If (formulaFromComment = "") Then _
GoTo NextHeaderCell
Set targetCell = Intersect(headerCells.Worksheet.Rows(newRow), _
headerCell.EntireColumn)
AddFormula newRow, targetCell, formulaFromComment
NextHeaderCell:
Next
End Sub
Sub AddFormula( _
newRow As Integer, _
targetCell As Range, _
formula As String)
formula = Replace(formula, "{ROW}", newRow)
targetCell.formula = formula
End Sub
Function GetFormulaFromComment(headerCells As Range) As String
' TODO
GetFormulaFromComment = "=SUM($C${ROW}:$E${ROW})"
End Function
Just use tables.
If you highlight cells and choose Insert Table from the ribbon, it doesn't just give you formatting and filters. It also, if you build them the right way, stores column formulas once per column instead of once per cell. Also, the formulas are more readable!
For formulas, you can't use cell addresses if you want it to be a single column formula unless they are absolute. (E.g. $A$1, not A1.) Instead, you use [ColumnTitle] for the entire column (where "ColumnTitle" is the actual title of that column) and [#ColumnTitle] for the column value in the same row. So if "Cost" was the title of column B, "RunningTotal" was the title of column C and your formula for C6 was therefore =B6+C5, you'd instead use a formula of =[#Cost]+OFFSET([#RunningTotal],-1,0)] which is longer but much easier to read/maintain/debug, and if you change a column title then the formulas change too! No VBA required. Given this, plus being able change columns for the entire columns at once, plus being able to refer to other columns in other tables without worrying about cell addresses (e.g. MAX(Table1[Cost])), plus being able to style the tables so easily, plus the integration with Power-Query, and VBA support. (See learn.microsoft.com.) Whether VBA or otherwise, add a row to your table and the columns with a single column formulas will automatically carry over into the new row.
Not sure about Table templates or VBA but perhaps there is another option by using =ARRAYFORMULA()
For example, say you had a header row and 3 columns and wanted your last column to be the product of the first two. In cell C2 you could enter the following:
=ARRAYFORMULA(A2:A*B2:B)
This has three benefits:
Skips the first row completely
Effectively applies the formula to every row which is useful if you later decide to insert a row (your question)
Only one location to modify the formula for every single row
Although, it may not be immediately obvious where how/where the cells are being calculated. (hint: ctrl+~ may help)

External Data Pull: Pull more specific data or change macro based on data

This question is somewhat difficult to explain, so bear with me.
I am pulling data from a large table for my company and am trying to create a macro to make this data easier to read/understand. The data that is on the site changes every day based on what caused certain failures in our plant, which causes my macro to analyze data that isn't there or wrong cells (due to rows getting shifted/moved/added/removed). Because I don't think that was really clear, here is an example:
The macro says to select cells J5, J13, and J25. These were, when I was creating the macro, the values I wanted to be put in a list. However, when I pulled the data and ran the macro today, these values were in different spots on my sheet (the value for cell J13 is now in J12). This completely messes up all of the analysis and renders my macro / data pull useless.
Is there a way to have the macro select the data more intelligently? Perhaps have it check for the group name, then select the value from the cell next to it? I wish I could word this better... Thanks if you've gotten this far!
Simply put... yes. Here's a code exert for looking for a groupname and getting the adjacent cell:
Dim Group1Range As Range
'Look in ThisWorkbook
With ThisWorkbook
'Look in Sheet1
With .Sheets(1)
'Look in Column I
With .Columns("I:I")
'Find the text Group1
Set Group1Range = .Find(What:="Group1").Offset(0, 1)
End With
End With
End With
'Indicate the address of the found range
Debug.Print Group1Range.Address
End Sub
Now here are ways that you can improve your question:
Explain how you know that cell J13 is no longer valid, and that J12 is now.
Give us some sample data.
Give us your code.
Tell us what your end result would be, possibly with an example.