Keeping column formulas in a table header - vba

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)

Related

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

get multiple column names (header) in table associated with particular value in to a cell

i need to get multiple column names (header) in table associated with particular value in to a cell
as i explained, i need to get the heading names corresponding to value "n" to column E.
i used the formula
=INDEX((A$1:D$1),MATCH("n",A2:D2,0))
here. but it only give one column name.
i am open to vba scripts also. but i think it doesn't need vba. just improve the the above formula, may be. i tried and failed. any help. thank you guys
if you are really "open" to vba, I'll use one simple UDF like:
Function HeatherNames(rg As Range, rf As String) As String
For Each cell In rg
If cell = rf Then HeatherNames = HeatherNames & Cells(1, cell.Column).Value & "-"
Next cell
HeatherNames = Left(HeatherNames, Len(HeatherNames) - 1)
End Function
you can use it in the column E `=HeatherNames(A2:D2;"n") now you can select the arg.1 (range) and type (or referring to another cell) the arg.2
Assuming you have Excel 2010 or later, in E2:
=IF(COLUMNS($A:A)>COUNTIF($A2:$D2,"n"),"",INDEX($1:$1,AGGREGATE(15,6,COLUMN($A2:$D2)/($A2:$D2="n"),COLUMNS($A:A))))
Copy to the right and down as required.
It would actually be slightly more efficient (and certainly if your dataset in reality is quite large) to have the initial IF clause held within its own cell, such that it is calculated for each row only once, rather than for each instance of the formula within that row. So a better set-up would be, in E2:
=COUNTIF($A2:$D2,"n")
copied down. Then, in F2:
=IF(COLUMNS($A:A)>$E2,"",INDEX($1:$1,AGGREGATE(15,6,COLUMN($A2:$D2)/($A2:$D2="n"),COLUMNS($A:A))))
copied to the right and down again.
Regards

Lookup function in multiple sheets data

I have multiple sheets of data and I want to make it in one sheet (All of them are in the same workbook). Link to the excel file.
I tried to use Hlookup function in excel file, something like below:
=HLOOKUP("University",Sheet1!$A$1:$G$2, 2, FALSE).
But, since I have more than 100 sheets of data, I want to find a way to drag the function and auto generate the function below the 2nd row. I have tried to use indirect function by setting a reference column in front as below but cannot deal with it.
=HLOOKUP("University", 'INDIRECT(A3)'!$A$1:$G$2, 2, FALSE)
My next option is VB code. But, I am new to VB. Anybody can help on it?
Place your individual sheet names in column H of the Summary sheet and the row number in column I (as helper columns) and write this formula in cell A2 of the summary sheet.
=IFERROR(HLOOKUP(A$1,INDIRECT($H2&"!A1:G"&$I2),$I2,0),)
and drag to column F and down for as many sheet rows combos you have. I used 10 rows but you can obviously make it longer or shorter as neeed.
When you are done you can filter on 0 in column A and remove any lines with no data.
If your sheet names have spaces in them, you'll need to adjust the INDIRECT formula to this:
INDIRECT("'"&$H2&"'!A1:G"&$I2)
best way would be "defined names" + INDIRECT + HLOOKUP (or LOOKUP) like:
defined names
name: SList
formula: =MID(TRANSPOSE(GET.WORKBOOK(1))&T(NOW()),FIND("]",TRANSPOSE(GET.WORKBOOK(1))&T(NOW()))+1,255)
formula in cells: (this in A2 then simply autofill to G2 and thenn everything down) (you'll get a row with 0's between the sheets, which can be filtered out or deleted later (copy/paste values))
=IFERROR(HLOOKUP(A$1,INDIRECT("'"&INDEX(SList,COUNTIF($A$1:$A1,0)+2)&"'!$A:$G"),$H2,0),"")
Set H2 to 2 and for H3: (autofill down from H3)
=MAX(($H2+1)*($A2>0),2)
works perfectly for me LINK
No manual typing of sheetnames or something like that (only Column H:H as helper). Youll get rows's with 0's every time a new sheet is selected which can be filtered out. (or if you copy/paste values also can be deleted)
the +2 at ...st,COUNTIF($A$1:$A1,0)+2)&... simply tells to start with sheet 2 (if summary is the first). You may change it to +1 if you want to lookup starting with the first sheet.
Assuming you already have all 100+ sheet names typed out in column A, this will work whether or not you have spaces in the sheet names:
=HLOOKUP("University", OFFSET(INDIRECT(ADDRESS(1,1,1,1,A2)),0,0,2,7),2,FALSE)

Directly copying values of evaluated Excel functions

I know that I can copy a column containing evaluated formulas, paste it into an adjacent column, and specify to paste values (i.e., a text string), so that the value sticks and I can then select, copy, and edit the value text as I desire. However, I would like a more direct way to produce an editable column of values.
In order of preference, I would be satisfied if I could either:
(1) simply copy the values directly from the formula-containing cells by specifying a copy mode/option instead of having to specify a paste mode/option (so that if I want to paste the values outside Excel, for example, I can do so directly) - I believe this is not possible but would like to know if I have overlooked something
(2) write the formulas in a way that "throws" the evaulated values to other cells, overwriting whatever is in those other cells as needed, but passing no trace of the formula itself to the other cells
(3) use a worksheet macro that runs in the background and automatically copies values from column A into column B whenever column A values are updated or
(4) perform some operation on the formula column that removes the formulas but leaves the (editable) values (with the obvious drawback that you can't reuse the formulas once they are gone).
Which of these are possible?
The following Event Macro:
Private Sub Worksheet_Calculate()
Dim r As Range
Application.EnableEvents = False
For Each r In Columns(1).SpecialCells(xlFormulas).Cells
r.Offset(0, 1).Value = r.Value
Next r
Application.EnableEvents = True
End Sub
will monitor calculations in column A. Whenever a calculation is made, all the values of the formulas in column A will be copied to column B
Note:
the data will not be copied
macros must be enabled
all the formula results will be copied, even un-changed results

VBA Hide Row in table if Specified Column is Empty

I am trying to add to a macro I have that will hide every row that has no text in a column named Authorization. Please see the code I have below, I thought this may be on the right track but it does not hide any rows.
Cells.EntireRow.Hidden = False
For Each cell In Range("Authorization").End(xlUp)
If cell = "" And cell.Offset(1, 0) = "" Then cell.EntireRow.Hidden = True
Next cell
Edited to add how to define a dynamic named range
It is the fact that you have set the whole column to the name "Authorisation" that I think makes your code freeze, because the whole column is 1 million rows (if you have 2007 or above), and the code will still check even blank rows, so its doing it 1 million times. 1 Option is to rather than set it to the whole column, you could use a "Dynamic Named Range" which will expand and grow as data is added. There are several different formulas to do this, but based on the fact your data may contain blanks, this version of the formula will expand down to the last populated row in the column. My example uses colum A as you havent specified what column you are using, so change A to suit your needs.
You need to open the Names manager, from the Formulas tab
From the dialog box, find your "Authorisation" name.
Select it and you should see its current formula at the bottom of the dialog box, replace that with the following formula:
=OFFSET(Sheet1!$A$3,0,0,MATCH("*",Sheet1!$A:$A,-1)-2,1)
In the above formula:
Sheet1 is my sheet, replace it with yours a needed
$A$3 is the starting row of the name, so based on your comments, have set this as column A row 3
0,0, Are defaults you should not need to change
$A$A$ is the column it is counting values, so change as required
-1 is a default, leave as is
-2 is subtracting 2 from the count because we are starting on row 3, so if you change the starting row, change this
the last 1, defined how many columns your named range covers, in your example it is just 1, so this should not need changing.
Once you have defined the name in this way, the code below should work a lot quicker as it will only loop through down to the last row of entered data. There is one possible issue I can see with this and that is if the very last cell in column A is blank, but the rest of the row isn't, this will miss out the last row. I could fix this by using a different column to count what constitutes the last row, but need to know whicj column would always have a value in it.
< Original answer and code>
not sure you code matches the description of what you want it to do, namely you seem to be trying to check the row beneath the current cell as well, is this what you really wanted? Anyhow your syntax is slightly wrong. I have written and tested this and it works, I have swapped your offset around so my code is checking the cell in the named range "Authorisation" and then also checking the cell to the right. Amend to suit your needs
Sub test()
Dim c As Range
For Each c In Range("Authorisation").Cells
If c.Value = "" And c.Offset(0, 1).Value = "" Then c.EntireRow.Hidden = True
Next c
End Sub