VBA - Using Columns() in Formula - vba

Right now i am using this code for a SumIf Formula:
Public Sub CommandButton1_Click()
Dim lastrow As Long
lastrow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row + 1
i = 4
calc = "=SUM(SUMIF($A$2:$A$22,N3,$B$2:$B$22),"
Do Until i = lastrow + 1
calc = calc & "SUMIF($A$2:$A$22,N" & i & ",$B$2:$B$22),"
i = i + 1
Loop
ActiveSheet.Range("N" & lastrow + 1 & ":BI" & lastrow + 1).Value = calc & ")"
End Sub
My Question now is, is there a way that i can change the criteria part in calc = calc & SUMIF($A$2:$A$22,N" & i & ",$B$2:$B$22)," that it jumps from column N to column M till column BI?
In fact my code works fine, but if i move some cells in the table it also moves the formula. And if i add the $ sign it only uses column N.
I tried to add this, but it doesn´t work
i = 4
lCol = 14
calc = "=SUM(SUMIF($A$2:$A$22,N3,$B$2:$B$22),"
Do Until i = lastrow + 1
calc = calc & "SUMIF($A$2:$A$22," & Columns(i, lCol) & ",$B$2:$B$22),"
i = i + 1
lCol = lCol + 1
Loop
I hope it is clear what i mean and someone can help me.

Try creating a cell and using its address.
calc = calc & "SUMIF($A$2:$A$22," & Cells(i, lCol).address(0, 0) & ",$B$2:$B$22),"

#Jeeped:
I tried to change the syntax for .address, but i still does not solve my problem. I hope with the following pictures i can explain it better.
In the first picture you can see how my table looks and which cells are used in the formula.
In the second picture i moved some cells to the left and unfortunately the formula also moves. This is what i want to prevent
And if i use .Address(0, 1) the formula looks like this in every column:
=SUM(SUMIF($A$2:$A$22;$N3;$B$2:$B$22),SUMIF($A$2:$A$22;$N4;$B$2:$B$22) ... )

Related

How can I insert a dynamic last row into a formula?

I'm working with a clean sheet where I paste one column of dates with a varying number of rows. My goal is to show how many times each date shows up. However, every time I get to the last line I keep getting Run-time error '1004' Application-defined or object-defined error.
Here is my code:
Dim lastrow As Long
Set ws = ActiveSheet
Set startcell = Range("A1")
lastrow = ws.Cells(Rows.Count, "A").End(xlUp).Row
Range("B2").Formula = "=countif(A1:" & lastrow & ")"
Thanks in advance!!
COUNTIF function takes 2 arguments (https://support.microsoft.com/en-us/office/countif-function-e0de10c6-f885-4e71-abb4-1f464816df34), not one as in your code. Also missed column letter in range. If you want to process N dates, you have to make N formulas COUNTIF.
Try this code (dates in column A from A1, formulas in column B):
Sub times()
With ActiveSheet
Intersect(.Columns(1), .UsedRange).Offset(0, 1).FormulaR1C1 = "=COUNTIF(C[-1],RC[-1])"
End With
End Sub
Result:
SOLVED! 4 WAYS
High-level
Countif formula is incorrect (missing: col label A + condition)
Outlook: quick to fix, but requires loop for all dates
Current method may be CPU/time-intensive for large lists
Solutions A-D:
See Below (Links section), for google sheets (with full macro code, descriptions) for 4 solutions (3 macro based + 1 macro-free albeit dynamic soln). Briefly:
A: output with correction to your code
B: as for A, with for loop deployed
C: VB code for much quicker implementation of for loop/.Function code
D: macro-free variant (proposed/preferred)
Screenshots
Comparison table
Links:
Google Sheet
VBA count and sumifs - Automate Excel
VB code (A-C)
For completeness (same can be found in linked Google Sheet, typed - so macro-free / safe workbook):
Sub Macro_A():
lastrow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
cell = Range("a4").Address
Range("B4").Value = "=countif(A1:" & "A" & lastrow & "," & cell & ")"
' For i = 1 To lastrow - Range(cell).Row + 1
' Range("B4").Offset(i - 1).Formula = "=countif(A1:" & "A" & lastrow & "," & Range(cell).Offset(i - 1).Address & ")"
' Next
End Sub
Sub Macro_B():
Application.Calculation = xlCalculationManual
start_time = Timer
lastrow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row
cell = Range("a4").Address
' Range("B4").Value = "=countif(A1:" & "A" & lastrow & "," & cell & ")"
For i = 1 To lastrow - Range(cell).Row + 1
Range("c4").Offset(i - 1).Formula = "=countif(A1:" & "A" & lastrow & "," & Range(cell).Offset(i - 1).Address & ")"
Next
Application.Calculation = xlCalculationAutomatic
Range("c3").End(xlDown).Offset(2).Value = Round(Timer - start_time, 2)
End Sub
Sub Macro_C():
start_time = Timer
Set Rng = Range("A4", Range("A4").End(xlDown))
For Each cell In Rng
cell.Offset(0, 3) = WorksheetFunction.CountIf(Rng, cell.Value)
Next
Range("d3").End(xlDown).Offset(2).Value = Round(Timer - start_time, 2)
End Sub
Macro-free soln (D)
Go to Formulas (ribbon), Name Manager:
In Name Manager window that apperas, click 'New...'
Populate dialogue box as req. (modifying Sheet name and $A$4 starting cell as req.)
Test your new dynamic range by clicking on upward arrow in bottom right hand corner (which should select dates in column A as depicted below)
Enter single formula in first cell of output range (here, cell D4)*:
Formulae (for convenience):
range_countif:
=Sheet1!$A$4:OFFSET(Sheet1!$A$4,COUNTA(Sheet1!$A:$A)-2,0,1,1)
Entry in cell D4
=COUNTIF(range_countif,range_countif)
*Notes: requires Office 360 for 'spill effect' (input as array formula with 'ctrl' + 'shift' + 'enter' otherwise).
Let me know if you have any further Qs. Best of luck with your excel Spreadsheet!
So many ways to achieve. I use this formula to get the number of rows
=ArrayFormula(MAX(IF(L2s!A2:A1009<>"",ROW(2:1011))))
Then I build a string from it
="L2s!A2:E"&D3
I love named ranges so I named the cell with the string built DynamicRangeL2s
Here is how I used the dynamic range in my formula (denormalize() is a custom function I wrote but could be any function) using the INDIRECT() function
=denormalize(INDIRECT(DynamicRangeL2s),INDIRECT(DynamicRangeSchedule),2,2,"right")
Here is a great article on Dynamic Ranges
https://www.benlcollins.com/formula-examples/dynamic-named-ranges/

Running a For Loop in VBA on a Selected Range

What I am trying to do is simple but I just can't get it to work. I want select a range of cells and the run a macro that will:
Run a for loop from the first row to the last row on the range selected
Then return the "=NUMBERVALUE()" of the concatenated values in each column of the row
The output column will be right next to the last column of the selected range
This is what I have for the code:
Dim myRange As Range
Set myRange = Selection
First_Col = myRange.Column
Last_Row = myRange.Rows.Count + myRange.Row - 1
Last_Col = myRange.Column + myRange.Columns.Count - 1
Out_put_Col = Last_Col + 1
For i = myRange.Row To Last_Row
Cells(i, Out_put_Col) = "=NUMBERVALUE(Cells(i, First_Col) & Cells(i, First_Col+1)"
Next i
VBA is having issues with what is written in the for loop. I am newer to VBA but based on the way this is written I would have though it would work. Thanks for the help.
more like:
For i = myRange.Row To Last_Row
Cells(i, Out_put_Col) = "=NUMBERVALUE(" & Cells(i, First_Col).address & ":" & _
Cells(i, First_Col+1).address & ")"
Next i
I'm not sure NumberValue works like that though.

Excel 2013 VBA add new line to Formula

With my VBA Code i add a new line to a table. In the last row of the table i have a =Sum(SumIf(...)) Formula, this row will jump one row down if i add the new line.
So now i want to add the added line to the Formula.
This is my Code so far:
Private Sub CommandButton1_Click()
Dim lastrow As Long
Dim i As Integer
lastrow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row + 1
i = 4
Do Until i = lastrow
calc = "SUMIF($K$3:$K$19,N" & i & ",$L$3:$L$19))"
ActiveSheet.Range("N" & lastrow + 1 & ":BI" & lastrow + 1).Formula = "=SUM(SUMIF($K$3:$K$19,N3,$L$3:$L$19)," & calc
i = i + 1
Loop
End Sub
In fact it works fine, but in the End the Formula looks like this:
=Sum(SumIf($K$3:$K$19,N3,$L$3:$L$19),SumIF($K$3:$K$19,N21,$L$3:$L$19))
What do i have to add to the code that every row will be added to the Formula?
Hope someone can help me. I am new to VBA.
EDIT:
The Worksheet shows all assignments and the working progress. With a CommandButton i add a new assignment to the table. So right now my table has 20 rows, in row 21 is the =SUM(SUMIF(..)) - Formula. If i add a new assignment the formula goes to row 22 and in row 21 the new assignment will be shown.
With my Code i want that every row will be added to the formula. Right now the formula goes from N3 to N20. If i add the new line the N21 will not be added to the formula. With my code above it only shows the first and the last row.
In fact i want that the formula will look like this:
=SUM(SUMIF($K$3:$K$19,N3,$L$3:$L$19),SUMIF($K$3:$K$19,N4,$L$3:$L$19) ... SUMIF($K$3:$K$19,N21,$L$3:$L$19))
This is my table hope it will help
This will solve your problem.
You need to assign a value to the variable "calc" before your loop. Then add to its value inside the loop with another statement. Finally, do not put the formula into the cells' values until after you have exited the loop and add a final parentheses to the formula.
Dim lastrow As Long
Dim i As Integer
lastrow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row + 1
i = 4
calc = "=SUM(SUMIF($K$3:$K$19,N3,$L$3:$L$19),"
Do Until i = lastrow
calc = calc & "SUMIF($K$3:$K$19,N" & i & ",$L$3:$L$19),"
i = i + 1
Loop
ActiveSheet.Range("N" & lastrow + 1 & ":BI" & lastrow + 1).Value = calc & ")"

VBA - Loading master workbook using another workbook

I got these 2 tables, 1 is a datatable, the 2nd is a instruction table.
everyday i will update the instruction table, and run the macro, which will update the datatable accordingly based on ID.
It is currently really simple. If datatable ID (Col A) matches instructiontable ID (Col J) then the corresponding data Col B-F will update according to instructiontable.
Datatable
Col A= ID
Col B-G = Different names
The instruction table is:
Col I is add (change to Y) or delete (change to N) Col K i
Col J is ID
COl K indicates which name (Header of Col B-G) to update.
Sub updatedatatable()
On Error Resume Next
Dim instructionlastrow, findtablecolumn, findtablerow As Long
Dim findid As Integer
instructionlastrow = Range("I" & Rows.Count).End(xlUp).Row
For i = 2 To instructionlastrow
findid = 0
If Range("I" & i).Value = "Add" And Range("A:A").Find(Range("J" & i).Value).Row = 0 Then
Range("A" & Range("A" & Rows.Count).End(xlUp).Row + 1).Value = Range("J" & i).Value
findtablecolumn = Rows(1).Find(Range("K" & i)).Column
findtablerow = Range("A:A").Find(Range("J" & i)).Row
Cells(findtablerow, findtablecolumn).Value = "Y"
ElseIf Range("I" & i).Value = "Add" And Range("A:A").Find(Range("J" & i).Value).Row <> 0 Then
findtablerow = Range("A:A").Find(Range("J" & i)).Row
findtablecolumn = Rows(1).Find(Range("K" & i)).Column
Cells(findtablerow, findtablecolumn).Value = "Y"
ElseIf Range("I" & i).Value = "Remove" And Range("A:A").Find(Range("J" & i).Value).Row <> 0 Then
findtablerow = Range("A:A").Find(Range("J" & i)).Row
findtablecolumn = Rows(1).Find(Range("K" & i)).Column
Cells(findtablerow, findtablecolumn).Value = "N"
End If
Next i
End Sub
i was wandering if anyone can teach me so that the Instruction table can be loaded from a different workbook.
Thanks
I'm not sure excactly what the relation of the two tables are supposed to be according to your description, but I understand as this; you currently have them in the same workbook (in the same worksheet?) but want to have them in different workbooks.
To access a workbook from another workbook in vba you can use the Workbooks.Open method
https://msdn.microsoft.com/en-us/library/office/ff194819.aspx
I can also recommend some code examples from Ron de Bruin
http://www.rondebruin.nl/win/section3.htm
Since you haven't made any coding attempts to access separate workbooks I'm not 100% what you want to do, but please have a look at the links, make an attempt at a solution and get back if you get stuck again.

Am I using the isnumeric function correctly?

This program is to convert a column of data from cumulative to non-cumulative. On my sheet I have A1, B1, and C1 with the text Non-Cumulative, Cumulative, and Converted, respectively. I have numbers 1 to 10 beneath A1, then them summed cumulatively beneath B1. C1 is where I want to convert column B back to non-cumulative.
The IsNumeric is used to make the first row of data in C equal to the first row of data in B. It should detect that the title is above the number it is evaluating, thus knowing that no calculations have to be performed. For the rest of them, it'll see that the number above the one it is evaluating is a number, and thus the calculation has to be done.
My problem is that it isn't working. I think the reason is because IsNumeric() keeps coming back as false. Is there a different function I should be using? Do cell references not work in IsNumeric?
Here's the program!
Option Explicit
Dim i As Variant
Sub Conversion()
Sheets("Test Sheet").Select
For i = 1 To 10
If IsNumeric("B" & i) = False Then
Range("C" & i + 1) = Range("B" & i + 1)
Else: Range("C" & i + 1) = Range("B" & i + 1) - Range("B" & i - 1)
End If
Next
End Sub
The way you wrote your code is logical, just a minor syntax changes you need initially. However,
It's also best to check if the range is empty first...
Then check on if the value is numeric.
Better even, if you set the Range into a Range object and use offset
Code:
Option Explicit '-- great that you use explicit declaration :)
Sub Conversion()
Dim i As Integer '-- integer is good enough
Dim rngRange as Range
'-- try not to select anything. And for a cleaner code
Set rngRange = Sheets("Test Sheet").Range("B1")
For i = 1 To 10
If (rangeRange.Offset(i,0).value) <> "" then '-- check for non-empty
If IsNumeric(rangeRange.Offset(i,0).value) = False Then
rangeRange.Offset(i+1,1) = rangeRange.Offset(i+1,0)
Else
rangeRange.Offset(i+1,1) = rangeRange.Offset(i+1,0) - rangeRange.Offset(i-1,0)
End If
End if
Next i '-- loop
End Sub
To make your code more dynamic:
Another suggestion, you may simply Application.WorkSheetFunction.Transpose() the entire B column range that you need to validate into a variant array
Process the array and Transpose back to the Range with column B and C.
By doing so, you may omit setting for loop size manually but setting it using Lower and Upper bound of the array ;)
You need to check if the range of B i is numeric, not the string "B" & i
and rather than selecting the sheet, simply using a parent identifier like:
sheets("sheet1").range("B" & i)
This will help you avoid errors in your code
For i = 1 To 10
If IsNumeric(sheets("test sheet").range("B" & i).value) = False Then
Range("C" & i + 1) = Range("B" & i + 1)
Else: Range("C" & i + 1) = Range("B" & i + 1) - Range("B" & i - 1)
End If
Next