First Question: So I'm sorry if the formatting etc is awful.
I have one very long column of integers and I need to multiply every element by a constant (ex:2) and fill the next column over.
I know how to make it work by looping through each element but I was hoping there was a single command for it.
[D1:D25].Value = [C1:C25*2]
works for me, but it prints 0 where there were null spaces, so I need to define the range more specifically.
How can I define the both ranges in terms of variables and keep the multiplication functioning.
i.e.
.Range(.cells(firstRow, 1),.cells(lrow,1))*2
gives a type mismatch error.
Related
I need to get the column letters of a range for a macro. I specifically need the column letters, the numbers you get for columns directly using VBA Address functions won't work. Since the ranges are always from one column only, this simplifies the task. The range retrieved could be something like B3 or B3:B5, but always the same column and are inside a table.
So, what I need (in this case) to get would be B as a string. I tried to do the following:
RangeOfInterest = Worksheets("Sheet1").Range("Table1[Column1]").Address(0, 0)
RangeColumn = Right(RangeOfInterest, Len(RangeOfInterest) - InStr(RangeOfInterest, [0-9]))
However, I run into a series of issues with this. First, there is the InStr function. I thought this was the best way, because this function searches for the position of a character starting from the left, which is exactly what I need. However, I would need it to search for many values (any number from 0 to 9). Could I add all the numbers as search arguments or use some kind of trick to search between a range of numbers? What I tried certainly doesn't work.
On the other hand, I assume that if I somehow manage to add all numbers from 0 to 9, the function would start searching for them one by one instead of stopping the first time there is any number in the string? This would result in the issue that if for example there is a range like B3:B10 it will begin searching for a 0 and return the position of the 0 and finish, hence my code will return the string B3:B1 instead of just B.
Also, I can't just use a fixed solution like Left(RangeOfInterest, 1) to get the B because the code should work with any range, and once you reach the Z the column letters are double and go like AA, AB and so on.
I thought that another alternative would be to loop, but all my tries resulted in very complex pieces of code for what seems to have a pretty easy solution. Also, if possible, I would like to avoid looping although that doesn't matter if there is no other option. I would really appreciate any suggestion to solve this.
More often, one wants the number of a columns than its name because if you feed Excel the name it will convert it to the corresponding number for processing. Therefore I hope it isn't that you need the name for the purpose of addressing a cell using VBA. Anyway, here you go:-
Dim RangeOfInterest As String
RangeOfInterest = Worksheets("Sheet2").Range("Table1[Column1]").EntireColumn.Address(0, 0)
RangeOfInterest = Split(RangeOfInterest, ":")(0)
Debug.Print RangeOfInterest
I'm trying to make my monthly transaction spreadsheet less work-intensive but I'm running up against problems outputting my category lookups as an array. Right now I have a table with all my monthly transactions and I want to create another table with monthly running totals. What I've been doing is manually summing each entry from each category, but I'd love to automate the process. Here's what I have:
=SUM(INDEX(Transactions[Out], N(IF(1,MATCH(I12,Transactions[Category],FALSE)))))
I've also tried using AGGREGATE in place of SUM but it still only returns the first value in the category. The N(IF()) was supposed to force INDEX to return all the matches as an array, but it's not working. I found that trick online, with no explanation of why it works, so I really don't know how to fix it. Any ideas?
Just in case anyone ever looks at this thread in the future, I was able to find a simpler solution to my problem once I implemented the Transactions[Category]=I12 method. SUM, itself will take an array as an argument, so all I had to do was form an array of the values I wanted to keep from Transactions[Out] range. I did this by adjusting the method Ron described above, but instead of using 1/(Transactions[Category]=I12 I used 1/IF(Transactions[Category]=I12, 1,1000) and surrounded that by a FLOOR(*resulting array*, .01) which rounded all the thousandth's down to zero and didn't yield any #DIV/0! errors.
Then! I realized that the simplest way to get the actual numbers I wanted, rather than messing with INDEX or AGGREGATE, was to multiply the range Transactions[Out] by the binary array from the IF test. Since the range is a table, I know they will always be the same size. And SUM automatically multiplies element by element and then adds for operations like this.
(The result is a "CSE" formula, which I guess isn't everyone's favorite. I'm still not 100% clear on what it means: just that it outputs data in a single cell, rather than over multiple cells. But in this context, SUM should only output a single number, so I'm not sure why I need CSE... A problem for another day!)
In your IF, the value_if_true clause needs to return an array of the desired row numbers from the array.
MATCH does not return an array of values; it only returns a single value which, with the FALSE parameter, will be the first value. That's why INDEX is only returning the first value.
One way to return an array of values:
Transactions[Category]=I12
will return an array of {TRUE,FALSE,FALSE,TRUE,...} depending on if it matches.
You can then multiply that by the Row number to get the relevant row on the worksheet.
Since you are using a table, to obtain the row number in the data body array, you have to subtract the row number of the Header row.
But now we are going to have an array which includes 0's for the non-matching entries, which is not good for us as a row number argument for the INDEX function.
So we get rid of that by using the AGGREGATE function with the ignore errors argument set after we do change the equality test to 1/(Transactions[Category]=I12) which will create DIV/0 errors for the non-matchers.
Putting it all together
=SUM(INDEX(Transactions[Out],AGGREGATE(15,6,1/(Transactions[Category]=I12)*ROW(Transactions)-ROW(Transactions[#Headers]),ROW(INDIRECT("1:"&COUNTIF(Transactions[Category],$I$12))))))
You may need to enter this with CSE depending on your version of Excel.
Also, if you have a lot of these formulas, you may want to change the k argument for AGGREGATE to use the INDEX function (non-volatile) instead of the volatile INDIRECT function.
=SUM(INDEX(Transactions[Out],AGGREGATE(15,6,1/(Transactions[Category]=I12)*ROW(Transactions)-ROW(Transactions[#Headers]),ROW(INDEX($A:$A,1,1):INDEX($A:$A,COUNTIF(Transactions[Category],$I$12),1)))))
Edit
If you have Excel/O365 with dynamic arrays and the FILTER function, you can greatly simplify the above to the normally entered:
=SUM(FILTER(Transactions[Out],Transactions[Category]=I12))
What is the case
I'm trying to compare two arrays. For simplicity sake let's assume we want to know how often the values of one array exist in the other array.
My referenced/lookup array data sits in A1:A3
Apple
Lemon
Pear
My search array is NOT in the worksheet, but written {"Apple","Pear"}
Problem
So to know how often our search values exists in the lookuparray we can apply a formula like:
{=SUMPRODUCT(--(range1=range2))}
However, {=SUMPRODUCT(--({"Apple","Pear"}=A1:A3))} produces an error. In other words the lookup array wasn't working as expected.
What did work was using TRANSPOSE() function to create a horizontal array from my data first using {=SUMPRODUCT(--({"Apple","Pear"}=TRANSPOSE(A1:A3)))} resulting in the correct answer of 2!
It seems as though my typed array is automatically handled as an horizontal array, and my data obviously was originally vertical.
To test my hypotheses I tried another formula:
{=SUMPRODUCT(--({"Apple","Pear"}={"Apple","Lemon","Pear"}))}
Both are typed arrays, so with above logic it would both be horizontal arrays, perfectly able to work without using TRANSPOSE(), however this returns an error! #N/A
Again {=SUMPRODUCT(--({"Apple","Pear"}=TRANSPOSE({"Apple","Lemon","Pear"})))} gave a correct answer of 2.
Question
Can someone please explain to me:
The reasoning why horizontal can't be compared to vertical arrays.
Why a typed array would automatically be handled as horizontal
Why in my test of the hypotheses the second typed array was handled as vertical.
I'm really curious, and would also be happy to be linked to appropriate documentation as so far I have not been able to find any.
This might be an easy one to answer, though I can't seem to get my head around the logic.
Can someone please explain to me:
The reasoning why horizontal can't be compared to vertical arrays.
This is actually possible, and you can also compare horizontal arrays with other horizontal arrays.
The reason you have been getting the error is because of the mismatch in the length of the array. Consider the following arrays:
Doing =SUMPRODUCT(--(B3:D3=F3:G3)) is the same (on excel's english version, I'm not 100% sure on the delimiters on other versions) as =SUMPRODUCT(--({"Apple","Lemon","Pear"}={"Apple","Pear"})) and results in =SUMPRODUCT(--(Apple=Apple, Lemon=Pear, Pear=???)), that is the nth element of the first array is compared to the nth element of the second array, and if there is nothing to match --the 3rd element in the 1st array is Pear but there is no 3rd element for the 2nd array-- then you get N/A.
When you compare two arrays, one vertical and one horizontal, excel actually 'expands' the final array. Consider the following (1row x 3col and 2row x 1col):
Doing =SUMPRODUCT(--(B3:D3=F3:F4)) is the same as =SUMPRODUCT(--({"Apple","Lemon","Pear"}={"Apple";"Pear"})) and results in =SUMPRODUCT(--(Apple=Apple, Lemon=Apple, Pear=Apple; Apple=Pear, Lemon=Pear, Pear=Pear)). Basically it feels like Excel expanded the two arrays like this (3col x 2row):
This 'expansion' only happens when one array is 1 row high and the other is 1 column wide I believe, so if you take arrays that have something different, then excel will go back to trying to compare an element with 'nothing' to give N/A (you can use the Evaluate Formula feature under Formula tab to help):
So essentially excel is getting something a bit similar to this, where the first array is multiplied to the second array, giving the result array:
But since the last row and last column involve blanks, you get N/A there.
Why a typed array would automatically be handled as horizontal
In your question, it would seem that , delimit rows, so with =SUMPRODUCT(--({"Apple","Pear"}=A1:A3)) you are observing similar to the comparison of two rows in my first example, while with =SUMPRODUCT(--({"Apple","Pear"}=TRANSPOSE(A1:A3))), you are getting the 'expansion' occurring.
As stated in the comments, on the English version of excel, , delimits columns and ; delimits rows, as can be observed in this simple example where I supply an array with 2 rows and 3 columns, excel shows {0,0,0;0,0,0}:
Why in my test of the hypotheses the second typed array was handled as vertical.
TRANSPOSE simply switches an array from vertical to horizontal (and vice versa), but depending on what you are trying to do, you'll get different results as per the first part of my answer, so you'll either have N/A when excel cannot match an item of an array with another item of the other array, or 'expansion' of the two arrays that results in a bigger array.
If I type the formula 1/4*pi()*($A$1)^2 as a string in a cell and assuming I have a value in $A$1, I use the following VBA function in a third cell to evaluate the formula:
Public Function E(byval TextFormula as String) as Variant
E = Evaluate(TextFormula)
End Function
Is there a way to use math characters like •, √, ¼, π, ², etc. so that my typed-out formula looks more agreeable? Even translate '[' and ']' as '(' and ')'. I can just iterate through an array replacements using REPLACE() function for the simple characters but what about the extended characters like π?
For the really sharp macro'ers...
What about showing intermediate steps (iterations) as in (2*3) + (2.5*4) evaluates to 6 + 10 in the first iteration and then 16 in the next iteration. Asside: I would want the iterations to stop just before each set of addings/subtractings because I sometimes like to know what the relative magnitudes of the individual evaluated terms are to see what part of my formula is controlling the result.
And for the mega-genius ones...
What about mixed units? Such as typing out 560{lbs}/[1.23{m}*3.4{'}] and getting my result in ###{psf} as an example. I thought that the unit could be delineated by the underscore such as 34_kN but I think a start and end delineation is required for compound units like 34{kN/m^2}. There would need to be a way to force the output to a desired unit (ie. mm instead of in) like maybe setting up your desired units ahead in your sheet and then it would at least try to convert to one of those units. I think at this stage you will be charging me for the code;)
I like using Excel for my engineering calculations because I only use simpler formulas (no calculus!) and I don't want to constantly switch between Excel and Mathcad apps but use only one.
Shawn
Those are tall orders. The following sub might give you an idea for your first question:
Sub test()
Dim R As Range
Set R = Range("A1")
R.Value = "A = pr2"
R.Characters(5, 1).Font.Name = "Symbol"
R.Characters(7, 1).Font.Superscript = True
End Sub
Run it an then look at the contents of A1
As far as your second question goes - sure you can do it, but you would need to write a full-fledged expression parser. Writing one from scratch is fairly involved (at least a couple hundred lines of code) and is probably best done by using classes to create a custom tree data type then writing a recursive descent parser to parse strings into expression trees. Doable, though I have neither the time nor the inclination to do so.
I'm not quite sure what you are driving at with your last question, though my gut reaction is that it is easier than your second question since no real parsing is required and it is easy enough to create a dictionary of conversion factors.
I'm trying to do some bootstrapping with a data set in Excel with the formula =INDEX($H$2:$H$5057,RANDBETWEEN(2,5057)), where my original data set in is column H. It seems to work most of the time, but there is always approximately one cell that outputs a reference error. Does anyone know why this happens, or how to avoid including that one cell? I'm trying to generate a histogram from this data, and FREQUENCY does not play nice with an array with an error in it.
Please try:
=INDEX($H$2:$H$5057,RANDBETWEEN(1,5056))
=RANDBETWEEN(2,5057) returns a reasonably arbitrary value of 2 or any integer up to and including 5057. Used as above this specifies the position in the chosen array (H2:H5057) - that only has 5056 elements, so one problem would be when RANDBETWEEN hits on 5057. Much easier to observe with just H2:H4 and RANDBETWEEN(2,4).