Update formula when inserting or deleting rows - excel

I have a spreadsheet for measuring costing. All the data is stored within a table. I want to add or delete rows from the table.
Columns A, B and C have data validation lists stored on another tab. Columns D and E are the number of units and number of workers. F, G and H are total rows. F is the unit cost without tax. That is all in a table but there is a formula out in cell M that the formula in cell F makes reference to.
The first row in the table is row 7 and the cell M formula is
=IF(OR(ISBLANK(A7),ISBLANK(B7),ISBLANK(C7)),0,VLOOKUP(CONCATENATE(A7," ",B7," ",C7),Service_Price_List,2,FALSE))
I can't code but I found some code to add and delete a row.
The formulas fill down within the table so it updates the formulas to the correct dynamic formulas in columns F, G and H. The cells within the table update correctly when I add a row but the first time I add a row after performing another action, the cell in column M is added but referencing is wrong.
You might be at row 8, 12 or 13 etc. (any row) but it will put this formula in the cell
=IF(OR(ISBLANK(A7),ISBLANK(B7),ISBLANK(C7)),0,VLOOKUP(CONCATENATE(A7," ",B7," ",C7),Service_Price_List,2,FALSE))
which is the formula from the first cell in the list. If I then try to add another row, (click the add row button twice), the cell will be blank.
As the insert and delete buttons work, I think I need a way to copy the formula from M7, which would be the top of the list. Or would it be better to have the formula inserted dynamically through VBA?
This is the code for my button to add a row:
Private Sub CommandButton3_Click()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim tbl As ListObject
Set tbl = ws.ListObjects("table3")
'add a row at the end of the table
tbl.ListRows.Add
End Sub
This is the code for my button to delete a row:
Private Sub CommandButton4_Click()
Dim oLst As ListObject
Application.ScreenUpdating = False
ActiveSheet.Unprotect Password:=""
If ActiveSheet.ListObjects.Count > 1 Then
For Each oLst In ActiveSheet.ListObjects
With oLst
If .Name = "Table3" Then
If oLst.ListRows.Count > 1 Then
number_of_columns = .ListColumns.Count
oLst.ListRows(oLst.ListRows.Count).Delete
End If
End If
End With
Next
'ActiveSheet.Protect Password:=""
End If
End Sub

Related

Hide Columns in a range based on cell value selected from drop-down

Thank you PGSystemTest and Alexey for your comments. 2nd attempt.
Here’s what I need to do:
Select one or more departments from a list, the rest of the department rows will be hidden. (orange arrow)
Select from a list A, B, or C and display only columns in the task range (orange) that contain the selection.
Show All Rows and Columns again.
The departments are in G25:G37
The Orange Range of Tasks is I25:DM37
matrix sample
Below is what I started doing to loop through the range to hide the columns.
Sub Hide_Columns_Containing_A()
'Description: This macro will loop through the row and
'hide the column if the cell in the column
'has the value of A.
Dim c As Range
For Each c In Range("I25:DM37").Cells
If c.Value = "A" Then
c.EntireColumn.Hidden = False
End If
Next c
End Sub

How to run a function at the end of pasted data in Excel?

I have an excel sheet where I paste some data and I want to run a function automatically on pasting data at the end of each column to count the number of cells that have some text and then give that row which contains formula a specific color.
For example, I paste the below data:
And now I want to run a function at the end of each column which will display the count of cells containing 'Error'.
The function for the first column would be =countif(A2:A9, "Error"), the function for the second column would be =countif(B2:B9, "Error") and so on.
Appreciate any help in advance.
Format a blank table and create a sum row(Click in table -> Tabletools -> Sum row):
Write in the sum row your formula like: =countif([Second],"Error")
Now you can simply copy in your data and it will calculates the occurence in the last row. On pasting the table in, it will move the sum row automaticly downwards.
Expanding on Doomenik's answer
Part 1 Set your data up as a table and insert a total row. Adjust the following table name as appropriate.
Then insert total row by going into the design tab, which appears when you are inside the table range, and checking the Total Row box
A total row will appear at the bottom of the table with a dropdown icon
Starting with column A you want to select the COUNTIF function to apply to the total row which means selecting More Functions from the drop down and then typing in COUNTIF.
In the box that appears enter the following:
Notice that the entire data area of column A in the table is referenced by [ID]. This will be automatically entered when you select the data area of the table A column range when specifying the range argument to COUNTIF i.e. when selecting as below:
The criteria argument is NA() for error.
You then drag the formula from column A, in the total row, across to column C and autofill will do the rest.
Part 2: Apply conditional formatting to the total row by using
=ISFORMULA(INDIRECT("Table1[#Totals]"))
in Excel 2016 or
=LEFT(FORMULATEXT(INDIRECT("Table1[#Totals]")),8) = "=COUNTIF"
in earlier versions.
Entering the formula:
Now, specifying the range to apply to:
I messed around with specifying the last row with
=INDIRECT("Table1[#Totals]")
Turns out, Excel still converts this to the current last row range e.g.
=$A$11:$C$11
And this updates even if i add rows to the table.
Part 3: Adding new rows by pasting
Now, how to handle the adding of rows by pasting? Insert the following code by Zak into the worksheet containing the table.
Then paste the new rows into the first column of the totals row and it will update and shift the totals down.
Option Explicit
Private Const SingleRowOnly As Boolean = False
Private Const MaxRowCount As Long = 100
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ResizeRange As Range
Dim Table As ListObject
Dim TotalsShowing As Boolean
Dim ExpandTables As Boolean
Dim RowIndex As Long
Dim RowCount As Long
' Make sure sheet isn't protected
If Me.ProtectContents Then Exit Sub
' If already in a table, then exit
If Not Target.ListObject Is Nothing Then Exit Sub
' Make sure only one row is being changed
If Target.Rows.Count > 1 Then Exit Sub
' Make sure we're not in row 1
If Target.Row = 1 Then Exit Sub
' Make sure we're in the row right under the Totals row
If Target.Offset(-1, 0).ListObject Is Nothing Then Exit Sub
' Set table
Set Table = Target.Offset(-1, 0).ListObject
TotalsShowing = Table.ShowTotals
ExpandTables = Application.AutoCorrect.AutoExpandListRange
' If Totals not showing, exit
If Not TotalsShowing Then Exit Sub
' Make sure the selection is a contiguous range
If Target.Areas.Count > 1 Then Exit Sub
' Make sure Target range is within the table columns
If Target(1, 1).Column < Table.ListColumns(1).Range.Column Then Exit Sub
If Target(1, Target.Columns.Count).Column > Table.ListColumns(Table.ListColumns.Count).Range.Column Then Exit Sub
' Prepare to adjust table
Application.EnableEvents = False
Table.ShowTotals = False
Application.AutoCorrect.AutoExpandListRange = True
' Set the resize range
If WorksheetFunction.CountA(Table.Range(1, 1).Offset(Table.Range.Rows.Count + 1).Resize(1, Table.Range.Columns.Count)) > 0 Then
If Not SingleRowOnly Then
RowIndex = Target.Row
RowCount = RowIndex
Do Until WorksheetFunction.CountA(Me.Range(Me.Cells(RowCount, Table.Range(1, 1).Column), Me.Cells(RowCount, Table.Range(1, Table.ListColumns.Count).Column))) = 0 Or RowCount - RowIndex > MaxRowCount
RowCount = RowCount + 1
Loop
Set ResizeRange = Table.Range.Resize(Table.Range.Rows.Count + RowCount - RowIndex, Table.Range.Columns.Count)
Else
Set ResizeRange = Table.Range.Resize(Table.Range.Rows.Count + 1, Table.Range.Columns.Count)
End If
Else
Set ResizeRange = Table.Range.Resize(Table.Range.Rows.Count + 1, Table.Range.Columns.Count)
End If
' Make table adjustment
Table.Resize ResizeRange
' Put things back the way we found them
Application.AutoCorrect.AutoExpandListRange = ExpandTables
Table.ShowTotals = TotalsShowing
Application.EnableEvents = True
End Sub
Quoting from the link:
There are two constants declared at the top of this code.
SingleRowOnly. This specifies whether multiple rows should be included
in appending into the Table, or if only a single row should be.
MaxRowCount. As to not go crazy with appending rows to a Table
automatically, this is the maximum number of rows to include at any
one time. If SingleRowOnly is set to True, this constant is moot.
So you can adjust as appropriate.
With Autocomplete feature it should update column references automatically
EDIT
If you want to auto-do this, maybe you can try to paste the following formula at the end of your data:
=COUNTIF((INDIRECT(ADDRESS(ROW()-8;COLUMN()))):(INDIRECT(ADDRESS(ROW()-1;COLUMN()))); "Error")
Explanation
COUNTIF(range; pattern)
The range is specified with two INDIRECT functions. One pointing to the first row, and one pointing to the last one (those 8 and 1 respectively).
So the range looks like:
(INDIRECT(ADDRESS(ROW()-8;COLUMN()))) : (INDIRECT(ADDRESS(ROW()-1;COLUMN())))
NOTE that I assumed that you have 8 rows in total, but you can put any other number there

Copy unique values in Excel VBA

I have written VBA code that copies a filtered table from one spreadsheet to another. This is the code:
Option Explicit
Public Sub LeadingRetailers()
Dim rngRows As Range
Set rngRows = Worksheets("StoreDatabase").Range("B5:N584")
With rngRows
.SpecialCells(xlCellTypeVisible).Copy _
Destination:=Worksheets("LeadingRetailersAUX").Range("B2")
End With
Sheets("Leading Retailers").Activate
End Sub
The filter is applied before the code is ran and then the code selects the visible cells and copies them so as to get only those rows that passed the filter.
In the filtered table to be copied I have, in column L of the range, a certain set of names, some of which are repeated in several rows.
I would like to add to the code so that it only copies one row per name in column L. In other words, I would like the code to copy only the first row for each of the names that appears in Column L of the filtered table.
Pehaps something like this can help you. Code will loop through your rows (5 to 584). First it checks if row is hidden. If not, will check if the value in column "L" is already in the Dictionary. If it is not, it will do two things: copy the row to Destination Sheet, and add the value to the Dictionary.
Option Explicit
Public Sub LeadingRetailers()
Dim d As Object
Dim i As Long
Dim k As Long
Set d = CreateObject("scripting.dictionary")
i = 2 'first row of pasting (in "LeadingRetailersAUX")
For k = 5 To 584
If Not (Worksheets("StoreDatabase").Rows(k).RowHeight = 0) Then 'if not hidden
If Not d.Exists(Worksheets("Hoja1").Cells(k, 12).Value) Then 'if not in Dictionary
d.Add Worksheets("StoreDatabase").Cells(k, 12).Value, i 'Add it
Worksheets("LeadingRetailersAUX").Cells(i, 2).EntireRow.Value = Worksheets("StoreDatabase").Cells(k, 1).EntireRow.Value
i = i + 1
End If
End If
Next
End Sub
You could apply another filter to the table to only show the first occurrence of each set of names and then run your macro as usual. See this answer:
https://superuser.com/a/634284

Button Generates the columns from user input but not the cell lines?

I implemented a button that ask the user where to add a column, and the button takes the user input(A-Z) and generates the column until the end of the table NOT SPREADSHEET. The column ends based on how many rows there are in my table, meaning if there are 10 rows, after the user clicks the button an inputs where they want the column to be(They input a letter of the column A-Z), I should not see a column box on line 11 of the spreadsheet.
Now I've managed to do this my issue is below:
My issue is the cells the button generate does not include the lines or boxes around the cells so that you are aware that its an extension of the table?
here is what I mean: Picture of spreadsheet
notice the i column there are no lines around the cells?
Here is code, I think I am missing a copy function after the line
shift:=xlRight, but I don't know how to implement it?
I don't want to use macros because since the tables rows and column change due to the user's input I will have to constantly hard-code the range into the macro which i dont want.
Trust me I tried it an its annoying.
Private Sub CommandButton2_Click()
Dim x As Variant
Dim ColumnNum
x = InputBox("Enter a column that you want to add: ", "What column?")
If x = "" Then Exit Sub
ColumnNum = x
ThisWorkbook.Sheets("Sheet1").Columns(ColumnNum).Insert shift:=xlRight
ThisWorkbook.Sheets("Sheet1").Columns(ColumnNum).ClearContents
End Sub
you could try this:
Private Sub CommandButton2_Click()
Dim colIndex As Variant
colIndex = Application.InputBox("Enter a column that you want to add: ", "What column?", , , , , , 2) '<--| force a text
If colIndex = "" Then Exit Sub
With ThisWorkbook.Sheets("Sheet1").Columns(colIndex) '<--| reference column you want to insert
.Insert shift:=xlRight '<--| insert a new column , then the referenced one shifts one column to the right of the inserted one
.Offset(, -2).Copy '<--| copy the column two columns to the left of the referenced one (i.e. one column left of the new one)
.Offset(, -1).PasteSpecial xlPasteFormats '<--| paste formats to the new column
Application.CutCopyMode = False
End With
End Sub

Display one column at a time

I would like to know if this is possible and how i could do this.
I have a workbook with 2 sheets. Sheet2 has multiple columns with 50 different records. sheet1 i would like to have a play button or run button when i click "play/run" it it will have one column and that one column will display the records of each column from sheet2 until there are no more columns form sheet2. Additionally, the display will have a 5 sec interval before cycling to the next column.
i found out how to do the time interval but not the displaying
'time interval
Application.Wait Now + TimeSerial(0, 0, 5)
'displaying i have been using copy/paste but it does not work.
Thanks in advance
Add these procedures to the VBA module. Then add a button or textbox/etc. to the Sheet1, and right-click the shape, and choose "Assign Macro", then select the MyButtonClick procedure. This will associate the macro with the button.
Then, you just need to loop over the columns and copy/paste like so:
Sub MyButtonClick()
Dim ws1 as Worksheet, ws2 as Worksheet
Dim cols as Range, c as Range
Set ws1 = ThisWorkbook.Sheets("Sheet1")
Set ws2 = ThisWorkbook.Sheets("Sheet2")
Set cols = ws2.Range("A1:G50") '## Modify as needed
For Each c in cols.Columns
c.Copy Destination:=ws1.Range("A1") '## Puts the column in Sheet1, Column A
Call WaitForIt(5)
Next
End Sub
Sub WaitForIt(seconds as Long)
Application.Wait Now + TimeSerial(0, 0, seconds)
End Sub
A somewhat minimal-VBA approach:
Say that sheet2 looks like:
then in sheet1, create a 1-cell named range record (cell A2 in this case):
In column B put the formula:
=IF(NOT(ISBLANK(INDIRECT("Sheet2!R"&ROW()&"C"&record,FALSE))),INDIRECT("Sheet2!R"&ROW()&"C"&record,FALSE),"")
and copy it down for as many rows as the longest record in sheet 2
Then -- the VBA part can just have a loop where it has the statement
Range("record").Value = i
(with an i that cycles through the column numbers containing records). The spreadsheet formula takes care of pulling the correct values.

Resources