I'm doing some data crunching work in Excel. What I need to do is to fetch values from a Cell (Say D3) from sequentially numbered workbooks like 1.xlsx, 2.xlsx, 3.xlsx...so on.
I can't use INDIRECT() function as it would require all the target files to be opened (which is not possible in my case). So, I'm using the INDEX() function with the following values:
INDEX( [1.xlsx]Sheet1!D:D, 1,1 )
Now, I want the bold part of the formula to be dyamic based on the number on left column as shown in the image below:
Somebody please suggest solution to the problem at hand as I'm tired on googling :)
Thanks in advance.
Perhaps someone will prove me wrong, but I don't believe it is possible to generate such dynamic referencing without resource to INDIRECT, unless you are happy with a longhand (and static) method such as:
=INDEX(CHOOSE(ROWS($1:1),[1.xlsx]Sheet1!D:D,[2.xlsx]Sheet1!D:D,[3.xlsx]Sheet1!D:D),1,1)
though this requires that each workbook reference be entered explicitly within the formula. As such, this also means that these cannot be created via reference to any cells within the worksheet, as you might have hoped.
Or, if VBA is an option:
Sub Indexing()
Dim i As Long
For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row
Range("B" & i).Formula = "=INDEX([" & Range("A" & i).Value & ".xlsx]Sheet1!D:D,1,1)"
Next i
End Sub
Regards
Related
I am creating a dynamic report summarizing data on multiple XLS sheets all within a single workbook. The sheets have names that tie to a specific date.
This is simplified example of what I am doing, which works fine - it gives the correct answer which is the value of the cell at reference BB38 on the sheet called "221122":
=LAMBDA(r,INDIRECT("'" & r & "'!BB38"))("221122")
Problem comes when I want to iterate this over an array of sheets using BYROW instead of just passing the sheet name to the lambda. Simple example to replicate the problem:
=BYROW({"221122"}, LAMBDA(r,INDIRECT("'" & r & "'!BB38")))
This gives a #VALUE! error, instead of the correct answer which is the reference to that same cell (as part of a one cell dynamic array result). The only way I can solve it is by adding a SUM around the INDIRECT:
=BYROW({"221122"}, LAMBDA(r,SUM(INDIRECT("'"&r&"'!BB38"))))
Apart from being ugly, what I REALLY want to do is get a group of cells (spilled) back, like this, but then I can't use the SUM trick:
=BYROW({"221120","221121","221122"}, LAMBDA(r,INDIRECT("'"&r&"'!BB38:BD38")))
So that I am aiming towards is a spilled range like this:
Column A
Column B
Column C
221120!BB38
221120!BC38
221120!BD38
221121!BB38
221121!BC38
221121!BD38
221122!BB38
221122!BC38
221122!BD38
I know that you can't pass a dynamic function to INDIRECT but that's not what I am doing here - I am passing a single row of the dynamic array, represented by r.
In comment, Harun24hr points out correctly that BYROW can't return a dynamic array - that's why SUM worked. My own 'hack' way around this was to get the individual 1xN ranges of cells representing BB38, BC38 and BD38 and then HSTACK them together, e.g.:
a, BYROW(sheets, LAMBDA(r, SUM(INDIRECT("'" & r & "'!AY38")))),
b, BYROW(sheets, LAMBDA(r, SUM(INDIRECT("'" & r & "'!AZ38")))),
c, BYROW(sheets, LAMBDA(r, SUM(INDIRECT("'" & r & "'!BA38")))),
d, BYROW(sheets, LAMBDA(r, SUM(INDIRECT("'" & r & "'!BB38")))),
HSTACK(a,b,c,d)
Real question is: is there a more elegant / scalable way than HSTACK 1xN columns together?
Any ideas please? Thank you.
How about this approach:
=LET(start,221120,
end,221122,
DROP(REDUCE(0,SEQUENCE(1+end-start,,start),LAMBDA(s,e,VSTACK(s,INDIRECT("'"&e&"'!BB38:BD38)))),1))
Or simple =VSTACK('221120:221122'!BB38:BD38) based on this answer by JvdV: https://stackoverflow.com/a/74077560/12634230
I have a series of functions for each of listobject columns. The file is heavy and crashing, so I want just to keep the results of each formula as static values. I can allocate formula to the range and ask excel to convert the range to value. But I am wondering if there is a way to ask VBA to write only the static values in the range instead of the formula itself.
Here is what I have so far:
Sub calculate2()
Dim i As Long, t As Long
t = Timer
With Sheet3.ListObjects(1)
For i = 3 To 9
.ListColumns(i).DataBodyRange.ClearContents
.Range.Cells(2, i).Formula = sheets3.range("formula").cells(i,1).formula
.ListColumns(i).DataBodyRange = .ListColumns(i).DataBodyRange.Value
Next i
End With
Debug.Print Timer - t
End Sub
As an answer to my own question,
What I was looking for is: " application.Evaluate " I am posting that so if anyone came here by search can find the idea and the sollution I eventually found. Here is an example:
Sheet3.ListObjects(1).ListColumns(3).DataBodyRange = [IFERROR(IF(COUNTIFS(ZZ84!$B:$B,[WO],ZZ84!$E:$E,"=*V99",ZZ84!$L:$L,"<>")=1,1,0),"")]
in this case there is no need to loop and for each range has to write needful line of code (Embed the function in VBA, what I excatly was looking for). The only different in above function with directly putting that in a excel cell is using [WO] instead of [#WO]. So evaluat caculate an array of data and directly write that in specified range. (here body range of list columns 3 ).
For me it helped to avoid crashes beacause of voilate calculation by my functions.
Another simple example would be:
range("b1:b10")=[if(row(1:10),if(a1:a10>3,"big","Small"))]
or
range("b1:b10") = evaluate("if(row(1:10),if(" & range("a1:a10").address&">3,""big"",""small""))")
Kind regards,
M
folks,
not quite sure how to present this properly. so bear with with me:
i have an excel vba userform, and would like to do a calc inside the userform, retrieving data from a cell in an xls worksheet
the cell i can select is in col 'A' (GrossSales) and any row in the Revenue table; the selected cell then identifies 'active cell.row'. NOW, the column i want for specific data is defined elsewhere in a different column - the column could be found by #Headers[Sales]
since the worksheet is in development, i keep moving columns around; so i would like to use a 'dynamic' reference to the name of the column, rather than a 'static' name using column letter [like col 'H']
so, how it works, i click on a cell in col 'A' in a worksheet, and the userform gets me the active row: ActiveCell.Row; and currently, the xls has been 'static', so the column is simply identified as "H". in the userform then, i get my specific value to calc using Me.Sales.Value = Range("H" & ActiveCell.Row).Value
what i need to is to replace "H" with a header row 'name' so if i move columns this link still works.
i tried this:
Me.txtSales.Value = Range("Table_Revenue[[#Headers],[Sales]]" & ActiveCell.Row).Value
and i get this error:
..run time error 1004.. ..Method 'Range' of object '_Global' failed
so the reference to [#Headers],[Sales] is not returning the equivalent of the old "H"
can anyone tell me what i am missing here?
probably my lack of understanding of what is 'inside' the reference Table_Revenue[[#Headers],[Sales]]. it clearly isn't equal to H.
i tried putting the reference into a MsgBox to display, but that failed as well.
thanks,
ron
I'd suggest you use the ListObject instead:
Me.txtSales.Value = Intersect(Activecell.Entirerow, Activesheet.Listobjects("Table_Revenue").Listcolumns("Sales").Range).Value
rory,
u are correct. i was experimenting to understand range & value [i am still very wet behind the ears w/excel & vba]. i tried these things:
Me.txtSales.Value = Intersect(Activecell.Entirerow, Activesheet.Listobjects("Table_Revenue").Listcolumns("Sales").Range)
with dim txtSales Variant and then with dim txtSales Range
Me.txtSales = Intersect(Activecell.Entirerow, Activesheet.Listobjects("Table_Revenue").Listcolumns("Sales").Range).Value
with dim txtSales Variant
and #2 worked right, with the space between "Net Sales". so, as you said - it should work and it did.
what i think i found as well was that this version:
Range("Table_Revenue[[#Headers],[Sales]]" & ActiveCell.Row).Value
was commented as only working in excel, and not in vba.
one last thing: ListObjects worked because it was a 'table'? if it wasn't a table, but defined as a 'range', would it have worked?
anyway, i learned a lot - so thankyou very much for the solution, but thanks more for the learning experience.
ron
I would like to put the below coding into a vba like a function. There is a bunch of data created already by VBA, and when the VBA does its work, then the following function should be run, but i dont know how to add to my vba so that the function always runs as long as data contains. The macro i created already puts the datasheet together, now instead of creating the below with lenthy codings, i just want my macro to run the below, like a man who clicks on the below right hand corner of the cell which contains the below function.
It should be something: Activesheet.ForulaR1C1 = "=RIGHT(AY4,LEN(AY4)-FIND(".",AY4))" something. Can someone help me? Thanks
ORIGINAL FUNCTION TO BE RUN "=RIGHT(AY4,LEN(AY4)-FIND(".",AY4))"
This is where I am at now:
Sub Project_numbers()
Dim j As Integer
Zorro = Range("AY" & Rows.Count).End(xlUp).Row
o = 4
Worksheets("MJE").Range("AF" & o).FormulaR1C1 = "=RIGHT(AE4,LEN(AE4)-FIND(".",AE4))"
o = o + 1
End Sub
You have a couple of problems here. The biggest is that you've got quotation marks in your formula. VBA reads these as the end of the string, so it's interpreting your formula as two separate text strings: =Right(AE4,LEN(AE4)-FIND( and ,AE4)), separated by a .. This isn't a structure VBA can do anything with, so it's going to fail at that point.
When you're inserting a formula with VBA that contains quotation marks, you need to use two quotes together to indicate that it's a literal quote mark that's part of the string, rather than the end of the string:
"=RIGHT(AE4,LEN(AE4)-FIND(""."",AE4))"
The second problem is that you're using the FormulaR1C1 method, which expects cell references to be given in R1C1 (row#column#) notation, rather than A1 notation, but then passing it a formula that uses A1 notation. Again, this is going to confuse the issue and produce errors.
I'm guessing you used the macro recorder to get the syntax, then inserted your own formula? The macro recorder, for some weird reason, loves to use the R1C1 reference style, but we can use a different method for written code.
The full line you need is:
Worksheets("MJE").Range("AF" & o).Formula = "=RIGHT(AE4,LEN(AE4)-FIND(""."",AE4))"
EDITED TO ADD:
With further information, specifically that you need the range referenced to change as you loop, you have some options on how to do it.
1. Use the R1C1 reference style
This allows you to include relative references in formulae easily. You'll use R to designate the formula's row, and C to designate its column; so a cell that referred to itself would simply be =RC. You can follow the R and C with numbers to designate specific rows and columns, so cell B2 would be =R2C2 - row 2, column 2. More usefully, you can use =R[#]C[#] to offset your formula by a certain amount.
In your formula, assuming it's always going to be looking at column AE but whichever row the formula is entered into, your line would be:
Worksheets("MJE").Range("AF" & o).FormulaR1C1 = "=RIGHT(RC31,LEN(RC31)-Find(""."",RC31))"
2. Build your formula from variables.
You already have a variable you can use, o, so we can combine that with the rest of the string to get the appropriate references. It's harder to read, though...
Worksheets("MJE").Range("AF" & o).Formula = "=RIGHT(AE" & o & ",LEN(AE" & o & ") - FIND(""."",AE" & o & "))"
Personally, I find this method rather cumbersome to work with, but it's an option.
3. Assign the formula to your entire range as a single operation
Personally, I prefer this option; I find it to be the neatest one. I'm assuming, from your formula, that your data starts on row 4, and you want the formula to go into every cell between AE4 and the end of your data, which is stored in Zorro. You can use this line to add the formula in one go:
Worksheets("MJE").Range("AF4","AF" & Zorro).Formula = "=RIGHT(AE4,LEN(AE4)-FIND(""."",AE4))"
The cell references will update automatically for each row. There's no need for a loop with this method - of course, if you're looping anyway, that may be no great saving.
I need to write a macro.
I've got a workbook with ~ 30000 rows (changes daily).
I need to search for expression "TRADE" within the strings in cells from column (A)
If string inside the cell contain expression TRADE I need to change string in relevant cell in column (B) (the same row) to expression "TRADEIN"
If condition is not met relevant cells from column (B) need to stay unchanged
What have I learned so far:
Formula =IF(ISNUMBER(FIND("TRADE", A1 )), 1, 2) changes adjacent cell value accordingly ONLY if placed directly inside cell and copied down in Excel.
Problems starts when I try to have string as an outcome
Formula: =IF(ISNUMBER(FIND("TRADE", A1 )), "TRADEIN", "") won't work ->error
Formula: =IF(ISNUMBER(FIND("TRADE", A1 )), ""TRADEIN"", "") won't work ->error
Then any attempts to make my macro insert more complex formulas into cells from VBA failed i.e.:
Below works fine:
For i=1 to i=NumberOfRows
ActiveSheet.Cells(i, 2).Formula = "= 2+2"
next i
Below won't work (again, formula works if placed in the cell directly):
For i=1 to i=NumberOfRows
ActiveSheet.Cells(i, 2).Formula = "=IF(ISNUMBER(FIND("TRADE", (i, 1)), 1, 2)"
next i
I think there's no point in listing all my failed attempts to make it work so far (loads of useless lines to read I presume) but by all means - correct me if I'm wrong.
I can't find solution as specific as my task and have got problems altering some found online whilst other won't work for me at all. Perhaps don't exactly know how to ask for what I need in the most effective way. Be very basic and try not to miss out any declarations from proposed modules/subs if you can - I'm not yet confident when it comes to using and creating objects and methods outside of a few examples I followed, or choosing/using the right type of variables with compatible methods/functions etc.
Using VBA this is how would accomplish the goal. This will find the last row used in column A to set the range to work through.
Sub test()
Dim w As Range
lrow = Range("A1", Range("A" & Rows.Count).End(xlUp)).SpecialCells(xlCellTypeVisible).Count
For Each w In Range("A1:A" & lrow).Cells
If w.Value = "trade" Then
w.Offset(0, 1).Value = "tradein"
End If
Next w
End Sub
Practice using the auto filter, once you have that worked out use the macro recorder to get a code to work on.
Select column A and the goto Data=>Filter=>text Filter=>Contains....type the word in the box to filter for.