I have this formula:
IF(ROWS($Q$27:Q27)<=$P$25,INDEX(DataTable[[#All],[Time]],$P27),"")
and if I drag it to the right, it should automatically read each column respectively; example:
=IF(ROWS($Q$27:R27)<=$P$25,INDEX(DataTable[[#All],[Name]],$P27),"")
^Notice that the first Q27 is fixed, the second Q27 is variable.
I drag this formula to the right by 15 columns, and down to 50 rows. that's 750 formulas in total.
I want to do this in vba, but if I did this, it will be 750 lines of code for each cell representing each row/column.
example: .Range("G17").Formula=IF(ROWS($Q$27:R27)<=$P$25,INDEX(DataTable[[#All],[Name]],$P27),"""")
and if I drag it down, it will automatically pick up what I exactly want, example:
=IF(ROWS($Q$27:Q28)<=$P$25,INDEX(DataTable[[#All],[Time]],$P28),"")
so this formula should be written 750 times in total for the cell range [ A27:N76 ]
Any faster / more dynamic approach? and if possible, can I make it depend on more than 50 lines based on a cell value inside the sheet?
Example:
This should do it all in one line:
Range("A27:N76").FormulaR1C1 = "=IF(ROWS(R27C17:RC[16])<=R25C16,INDEX((DataTable[[#All],[Name]],RC16),"""")"
EDIT: Seems a more that one line of code required after all 😊
The code below will do what you want (this time fully tested)
Sub FillFormulas()
Dim inC%, rgHead As Range
''' Assumes the target sheet is Active.
''' o If that's not the case, change this With statement to reference the target sheet
With ActiveSheet
''' Set rgHead to the Table's header row
Set rgHead = .ListObjects("DataTable").Range.Rows(1)
''' Add the formulas to the target range, column by column updating the table header on the fly
With .Range("A27:N76")
For inC = 1 To .Columns.Count
.Columns(inC).FormulaR1C1 = _
"=IF(ROWS(R27C17:RC[16])<=R25C16,INDEX(DataTable[[#All],[" & rgHead.Cells(inC) & "]],RC16),"""")"
Next inC
End With
End With
End Sub
so this formula should be written 750 times in total for the cell range [A27:N76]
You don't need to do that. If you specify range.Formula, it will fill the proper formulas all the way across and down. Just give it the formula of the top/left most cell.
So, in your case
Range("A27:N76").Formula = "=IF(ROWS($Q$27:R27)<=$P$25 ... "
EDIT: This response had some obvious errors
This has an obvious error (as tested part and then merged to the full thing).
Range(A27:N76).FormulaR1C1 = "=IF(ROWS(R27C17:RC[16])<=R25C16,INDEX((DataTable[[#All],[Name]],$P27),"""")"
Related
In Excel 365, currently I manually update multiple named ranges based on a dynamic header row, with dynamic values under each header. I hoping someone can help me with using VBA code to update the names and ranges all together based on the current data/table.
The goal is to have a drop down list with the relevant Units values, without blanks or duplicates, for each Item.
The source data comes from an external workbook that is updated weekly. The data is in columns of Item and Units, but there is often multiple instances of each Item and Units and each week there may be new Items or some removed, same with the associated Units values. The source is as per below example:
ITEM
UNITS
AA 120
100
AA 120
100
AA 120
150
AA 60
350
BB 200
36
BB 200
30
BB 200
30
SH 1001
55
SH 1001
55
The unique headers are pulled using the formula
=IFERROR(INDEX($B$4:$B$600, MATCH(0, COUNTIF($K$2:K2, $B$4:$B$600), 0)),"")
The following formula is in the row below the headers, to pull the unique values for each header
=UNIQUE(FILTER($C$4:$C$600,$B$4:$B$600=L2))
The resulting table is as per below example:
AA 120
AA 60
BB 200
SH 1001
100
350
36
55
150
30
Currently I have highlight all the headers and the row below and select Create from Selection under Defined Names and select Top row. This creates named ranges that refer to the cell below each header. I then edit each range that the name refers to, by adding # at the end, so it refers to the spilled data, as it is a dynamic range.
e.g. Update the named range reference for AA_120 from =SHEET1!$L$3 to =SHEET1!$L$3#
I do this one by one for 100+ named ranges. Any tips or help to make this more efficient?
oh and the formula I'm using for the source of the Data Validation is =INDIRECT(C7) where C7 is the Item/named range. This all works well...
TIA
EDIT:
I worked out some VBA code to add the # at the end of the referred range. The range for all Named Ranges starts on row 3, so this worked to update all relevant ranges at once...
Sub RangeRename()
Dim n As Name
For Each n In Names
If Right(n.RefersTo, 1) = "3" Then n.RefersTo = n.RefersTo & "#"
Next n
End Sub
Would still really appreciate it if someone could improve on my VBA, to update all the ranges from when the source data is updated. Currently I delete all named ranges in the Name Manager, then select the 2 rows with all the updated Item and Units data. Then Create from Selection under Defined Names and select Top row. Then I run my macro. But if the Macro could do all above, that would be great. The difficulty I see if that the amount of Items and corresponding Units vary as it is dynamic. Plus there are 3 data sources on 3 different sheets, all rows 2&3 but columns start at L, N & T for the 3 sheets.
What I have worked out will work for me, but if anyone can improve on my code, I'd be extremely grateful!
Maybe I don't understand exactly what you're trying to do...but can't you get the info you want with a simple pivot table? I imagine there are advanced tricks you can apply to that to get to the formatting you want...
But using current Excel formulas, I think I got what you want by doing this:
I pasted your source data into a worksheet (A1:B10) and made it into
a table (Control+T), selecting 'My table has headers' option. (I think it's better to use a table but it's certainly possible to tweak the formulas below if you don't want to.)
I picked another cell on the same or another sheet--let's say E2--and entered =TRANSPOSE(UNIQUE(Table1[ITEM])). This gives a horizontal spill of unique items.
In the cell immediately below that (ie, E3) I entered =IFERROR(SORT(UNIQUE(FILTER(Table1[[UNITS]:[UNITS]],Table1[[ITEM]:[ITEM]]=D15))),""). (You need to repeat the bracketed column names like this if you want to drag things horizontally, which we do.) If you don't need/want to sort the Units in the columns we're going to create, you can leave that part of the formula out.
I dragged that cell horizontally to the left, going way beyond you need it right now (in case any new Items appear in your list). The IFERROR in the formula will keep the table looking clean on the right-most columns where no data currently appear. If/when you add more rows to your source data table, the spilled formulas will adjust accordingly, so I think your worry about dynamic sources is taken care of.
Results shown below. Maybe there's a way to devise a single formula that when put into E3 will spill into a 2-D array, so you don't even need to drag things...but I'll leave that to someone else.
Just in case anyone is wanting to achieve the same thing.
I used a filter to create the column of items, then TRANSPOSE(UNIQUE(FILTER(G:G,B:B=T7 for the rows of unit options next to each item.
For the VBA I added the following to a module
Function xDAV(c As Range) As Range
Dim r As Range
With Sheets("LookUpList").Columns("T:T") 'Items
Set r = .Find(What:=c.Value, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
'Units are in col U
'col U is 1 column to the right from col T
n = 1 'change to suit
If Not r Is Nothing Then
Set f = r.Offset(, n)
If f.Offset(, 1) = "" Then
Set xDAV = f 'single cell or blank cell
Else
rc = f.End(xlToRight).Column 'get last column with data
Set xDAV = f.Resize(, rc + 1 - f.Column)
End If
End If
End With
End Function
Then in the data validation I entered =toXDAV in the source field.
I am trying to count all blank cells in a row while ignoring hidden columns but I can't find any formula that returns the right answer. The SUBTOTAL function only works on hidden rows but I cannot change my data to hide rows instead of columns.
For example, I wan to count blank cells from B2:BA2 but need to ignore any blank cells from hidden columns between that range.
Appreciate any help!
You can try the following VBA function:
Function CntBlnk(Rng As Range)
Dim Cell As Range
Application.Volatile
For Each Cell In Rng
If Cell.EntireColumn.Hidden = False And Len(Trim(Cell)) = 0 Then
CntBlnk = CntBlnk + 1
End If
Next Cell
End Function
Then call the function CntBlnk in the required cell.
A VBA solution is probably the best option here. A set-up using worksheet formulas alone is possible, viz:
=SUMPRODUCT(N(CELL("width",OFFSET(B2,,COLUMN(B2:BA2)-MIN(COLUMN(B2:BA2))))>0),N(B2:BA2=""))
or, Office 365:
=SUMPRODUCT(N(CELL("width",OFFSET(B2,,SEQUENCE(,COLUMNS(B2:BA2),0)))<>0),N(B2:BA2=""))
though it suffers three drawbacks:
It's volatile
Despite said volatility, changes to the column widths in the range passed will not trigger a recalculation of this formula; the user will need to perform a manual recalculation
Columns having a column width of less than 0.5 will be treated as hidden
If you have Excel 365 and are open to using a Lambda, you could also try:
=LAMBDA(range,index,IF(index>COLUMNS(range),0,ISBLANK(INDEX(range,index))*(#CELL("width",INDEX(range,index))>0)+CountVisBlanks(range,index+1)))
where the Lambda is named as CountVisBlanks in the name manager.
As with the other answer using Cell, it suffers from the issue that Cell doesn't update until you force the sheet to re-calculate.
Called as:
=CountVisBlanks(b2:ba2,1)
Good day,
I'm at a loss on this problem.
I have a group of cells that contain words, like apple, this word would be the value. It is separated by a symbol for completing the math. They can be changed by the user to make custom calculations.
Cell A1 is "apple", B1 is "+", cell C1 is "apple", cell D1 is "*", cell E1 is "apple", call F1 is "=" and cell G1 is the suggested total, in this case would be "6".
It would be posted as | apple | + | apple | * | apple | = | 6 |
The legend holds the value for the word, so if you enter 2 in the legend, apple would be 2.
The logic would determine that the formula would be 2+2*2= if written in excel, I would like to combine all the cells and calculate this.
I tried using =sum, sumproduct, concate and the like to no avail.
Any head way I did make, I ended up getting BEDMAS wrong as it calculated it as 2+2=4*2=8, instead of the correct 2*2=4+2=6.
Anyone know of a way to combine these cells and properly sum the values to the correct total?
Thank you for your time!
Go to the Name manager and create named range Eval, into Refers to field add formula:
=EVALUATE(CONCATENATE(VLOOKUP(Sheet1!A1,Sheet1!$A$3:$B$5,2,0),Sheet1!B1,VLOOKUP(Sheet1!C1,Sheet1!$A$3:$B$5,2,0),Sheet1!D1,VLOOKUP(Sheet1!E1,Sheet1!$A$3:$B$5,2,0)))
In range A3:B5 I have legend.
Change references as you need. Then in cell G1 write formula =Eval.
Sample:
This is a UDF based solution. Its advantage is that it's more versatile and by far easier to maintain if you learn its elements of code. The disadvantage is in that you do have to learn the elements of code and you have an xlsm macro-enabled workbook which isn't welcome everywhere.
The setup is simple. I created a table with columns Item and Value. I placed it on another sheet than the task. In fact, you could make the sheet with the table VeryHidden so that the user can't look at it without access to the VBA project, which you can password protect. I called the table Legend. The item columns has names like apple, pear, orange. The Value column has the numeric values associated with each name.
Note that, since this is a VBA project, the entire list can be transferred to VBA leaving no trace on the sheet. You could have a function to display the value of each item as the user clicks on it and have it disappear as he clicks elsewhere.
Next I created a data validation drop-down in A1 with the List defined as =INDIRECT("Legend[Item]"). Copy this cell to C1 and E1.
Then I created another Data Validation drop-down in B1 with the list as +,-,*,/. This drop-down must be copied to D1.
Now the code below goes into a standard code module. Find the way to create it because it isn't any of those Excel sets up automatically. It's default name would be Module1. Paste the code there.
Function Evalue(Definition As Range) As Double
Dim Task As String
Dim Fact(2) As Double
Dim C As Long
Dim i As Long
With Definition
For C = 1 To 5 Step 2
On Error Resume Next
Fact(i) = Application.VLookup(.Cells(C).Value, Range("Legend"), 2, False)
i = i + 1
Next C
Task = "(" & Fact(0) & .Cells(2).Value _
& Fact(1) & ")" & .Cells(4).Value _
& Fact(2)
End With
Evalue = Evaluate(Task)
End Function
Now you are ready for testing. Call the function from the worksheet with a call like
=Evalue(A1:E1). You can use it in comparisons like =IF(G6 = Evalue(A1:E1), "Baravo!", "Try again"). As you change any of the components the cell with the function will change.
Remember to use absolute addressing if you copy formulas containing the range. If you need to get a result in VBA while testing, use this sub to call the function.
Private Sub TestEvalue()
Debug.Print Evalue(Range("A1:E1"))
End Sub
My Sheet
Here is what I have.
In cells M - U, i count all the instances of the word from cells E, G and I from the legend.
=SUMPRODUCT((LEN(E3)-LEN(SUBSTITUTE(E3,$B$3,"")))/LEN($B$3))
In cells W - AE, I multiply the instances with the value to give me a total each time the word appears.
=SUM(M3*$C$3)
In cell E8 - I8, i add the three possible values together.
=SUM(W3:Y3) so each worded cell is now a number.
I'd like to take the cells E8 - I8 to make a calculation in k8 and so on.
So, each cell is put together to make an
=SUM(E8:I8)
statement, which all works except E11 - I11 which equates to 26 instead of 43.
Currently I have a couple of SUM functions setup in my worksheet.
Lets use H2:H34 for my example: If I add a row within that range, the sum function will automatically adjust. The problem is when I add new rows, it is below the range (below H:33). In this case, I would just have to highlight the column and expand/drag the range to where it needs to be. There are a total of 8 columns that are doing a sum function. Is this only possible by using a macro?
=SUBTOTAL(3,H2:H34)
With your formula being the extent of the range minus one row we can use this:
=SUBTOTAL(3,H2:INDEX(H:H,ROW()-1))
Now as rows are added or deleted the reference will remain intact.
If there is a chance that row 2 would be deleted you will get #Ref errors. To avoid that you can use this formula:
=SUBTOTAL(3,INDEX(H:H,2):INDEX(H:H,ROW()-1))
Now there are no specific cell references and adding or deleting will not effect the formula and the range will be dynamic.
you could use a named range like =sum(range1) and something like;
Select first empty cell in column F starting from row 1. (without using offset )
to find the first empty cell to change the reference for the named range.
Triggering it would be difficult... potentially could use on_save?
or option 2.... bit of a dodge, but can work....
you say your sum is in cell H35... maybe move it to h36, and create a dummy hidden row in 35 (0.1 row height). that way when you add a row, it is always above 35. Use sum H1:H35 and it will always auto update? its not a neat solution, but should work
Use the following code, you need to adjust column "H" to whatever column you need.
Dim sumtest As Variant
sumstest = Application.WorksheetFunction.Subtotal(9, Range("H2:H" & ActiveSheet.Cells(ActiveSheet.Rows.Count, "H").End(xlUp).Row))
This time, I have a cell holding a natural number, let's say 10. Starting from another cell and for the 9 columns to the right of it, I want to display the numbers 1 through 10. If my reference cell held 20, however, I want to start at the other cell and display the numbers 1 through 20 in 20 columns. I hope you get the gist.
Is there a good way to do this, first of all, without VBA? If not, is there a good way to do this with VBA? Is there some event I should catch on a cell which would allow me to then manually insert the values I want? My knowledge of Excel and VBA is not very good, so sorry if the question is an easy one.
Thank you all in advance!
Let's say that the cell with the number is A3 and the you want to start the series in F3. In F3, put this formula
=IF(COLUMN()-5<=$A$3,COLUMN()-5,"")
Copy F3 and paste to the right (or fill right) for as many columns as you may ever need. If A3 will never be more than 100, then fill right for 106 columns. If you want to start in a column other than F, you'll need to change the -5 parts of the formula.
You won't be able to use a UDF since values are changed in more than one cell. So the easiest way (with VBA) is probably with the Change event. If the value is in the named range "holdvalue" and you want to start the numbers at range "valueStart" then this will work:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
If Target.Cells.Count > 1 Or IsEmpty(Target) Then Exit Sub
If Target.Address = Range("holdvalue").Address Then
For i = 1 To Range("holdvalue")
Range("valueStart").Offset(0, i - 1) = i
Next i
End If
End Sub
Note: This doesn't deal with clearing the values.
You can do this with formulas:
=IF(COLUMN()<$A$35+2,COLUMN()-1,"")
Where $A$35 is the reference cell and I started the "another cell" in column B. If you start at a later column you would need to adjust the +2 and -1 in the formula accordingly. Just drag out the formula to where you would expect the max number would be, you can also do a validation on the reference cell to make sure it is below the max number and a whole number.