I need to create a dynamic Excel formula through a Perl script.
I store a formula that contains a fake range
my $stored_formula = $worksheet->store_formula('=MEDIAN(A1:A2)');
In the script, I calculate the new range
$formula_range = $mm_arr[$mmindx-1] . "!" .
num2col(2+$form_off) . ((($serv-1)*5)+5) . ":" .
num2col((2+31+$form_off)) . ((($serv-1)*5)+5);
where @mm_arr is an array ("Jan", "Feb", …)
and num2col is a function I use to translate column numbers to letters.
The line just below is the repeat_formula
$worksheet->repeat_formula(
(($serv-1)*5)+4,
1+$mmindx,
$stored_formula,
undef,
'A1:A2', $formula_range
);
I expect to get:
=median(Feb!K3:AH3)
but instead I get:
=median(K3:AH3)
So the find/replace is somehow working, but I can’t really pin down how!
What am I doing wrong?
First, a few recommendations:
There are way too many parentheses in your expressions which, coupled with the variables and functions whose definitions we can’t see make your code hard to parse visually. You also did not post a small, self-contained script that exhibits the problem, which means anyone who tries to help you has to set up a test script. Make it easy on others.
For example, the line where you define
$formula_rangecan be re-written as follows:Keep in mind you can use functions provided by Spreadsheet::WriteExcel::Utility to convert between cell notation and row/column indexes.
The problem seems to be that if the stored formula is not in the form
SheetName!Range, then the substitution chops off the sheet name in the replacement, and only puts in the range.The reason for this seems to have to do with the parser setup in
Spreadsheet::WriteExcel::Formula. A plain range is parsed as arange2dtoken whereas a range with a sheet reference is parsed as arange3dtoken. Later, inrepeat_formula, the replacement is done on tokens. From what I can gather, arange2dtoken looks like'_ref2dA1:A10'. So,'_ref2dA1:A2'gets transformed into'_ref2dFeb!A1:10'in repeat formula. However, when it is passed toSpreadsheet::WriteExcel::Formula::parse_tokens, the code still classifies it as arange2dtoken based on the prefix. I gather the final call$parse_str .= $self->_convert_ref2d($token, $class);then turns it back into'_ref2dA1:A10. I am not sure if this could be classified as a bug, but it is definitely unexpected from the user’s POV.So, the solution is to put in the name of a sheet that is guaranteed to exist.
Here is a test script:
And here is a screenshot: