I've got a whole bunch of cells containing arbitrary length arrays stored as semicolon-delimited strings, ranging in length from 1 to 65 entries, like:
pcmsh15(232);pcmsh16(232);pcmsh17(136);
pcmsh12(40);
pcmsh12(40);
pcmsh12(5);pcmsh15(20);
I want a way to sum up the numbers in the parenthesis in Excel 2010 without using VBA, keeping in mind that they are arbitrary length strings, each contained in their own cell.
I've currently got a VBA function that I wrote that sums the numbers in parenthesis, but it's slowing my spreadsheet down. I know I can use Excel's SUBSTITUTE function to turn the semi-colon delimited arrays into something resembling Excel's internal array format, like
="{"&SUBSTITUTE([#[data]],";",",")&"}"
But unfortunately, Excel won't parse that as an array in SUM or COUNTIF, only as a string. One workaround I found makes a named range that references the cell with a string-formatted array, but since I also have an arbitrary number of these arrays in cells, I can't go naming every single cell.
Is something like this possible in "pure" excel functions?
You can actually do this with no VBA involved. The method is kind of ridiculous, though. But, just for fun...
In cell B4 I put one of the strings you want to "parse":
B4: pcmsh15(232);pcmsh16(232);pcmsh17(136);
Then I put the following formulas in the cells indicated:
B6: =SUBSTITUTE(B4,"pcmsh", """dummy")
B8: =SUBSTITUTE(B6,"(",""";")
B10: =SUBSTITUTE(B8,")","")
B12: =MID(B10, 1, LEN(B10)-1)
B14: ="{"&B12&"}"
That transforms the starting text into a text representation of a legal Excel array:
B14: {"dummy15";232;"dummy16";232;"dummy17";136}
You can make Excel evaluate that string and return an actual array with the following trick. Create a named formula that we'll call eval_left. Make sure cell C14 is selected when you create the named formula:
=EVALUATE(B14)
Note that the formula uses a relative reference, so if you use that name from a cell other than C14, you'll see that the B14 has changed to whatever cell is just to the left.
This uses the old Excel 4 macro function EVALUATE, which takes a string and returns the value that that string represents. Now we can put a final formula in cell C14:
C14: =SUM(eval_left)
Since SUM ignores non-numeric strings, you get the answer you want, with no VBA. (You do still get the macro warning though, because of the use of EVALUATE.)
Like I said, I consider this a just-for-fun curiousity. You're way better off using VBA for stuff like this.
EDIT:
In the unlikely event you wanted to actually do something like this, you'd run into the length limit of what EVALUATE can handle. You'd have to chop up your longer strings into less than 65 values, not use "dummy", etc., but it would still be doable.
Related
I have in a cell two numbers "=5+4" as a text. This is a result of another operation.
I took a part of another formula and concatenated it with "equal" symbol:
= "=" & RIGHT(FORMULATEXT(V8);LEN(FORMULATEXT(V8))-SEARCH("+"; FORMULATEXT(V8)))
I want to get the result of 5+4 in a cell- which means "9" ;)
I DO NOT WANT TO USE VBA code.
my initial problem was to extract all numbers except the first one from an equation and sum in another cell: A1: "=6+5+4". A2: "9". Maybe it can be solved without VBA?
You could try (assuming only addition):
Formula in B1:
=SUM(FILTERXML("<t><s>"&SUBSTITUTE(MID(A1,2,LEN(A1)),"+","</s><s>")&"</s></t>","//s[position()>1]"))
In fact, if you don't want to use the 1st number we could also discard taking '=' into account:
=SUM(FILTERXML("<t><s>"&SUBSTITUTE(A1,"+","</s><s>")&"</s></t>","//s[position()>1]"))
And since SUM() will ignore text in the final answer, we can now even further simplify this (thanks #JosWoolley):
=SUM(FILTERXML("<t><s>"&SUBSTITUTE(A1,"+","</s><s>")&"</s></t>","//s"))
If you are always adding 2 numbers, then you can use this:
=VALUE(VALUE(MID(A1;2;SEARCH("+";A1)-2))+VALUE(MID(A1;SEARCH("+";A1)+1;9999)))
My column A is formatted as text, and all values follow same pattern: =Value1+Value2
So the formula extracts Value1 and Value2 as text with MID functions, based on the position of + symbol. Then Excel convert both values into numeric with VALUE function. Notice there are 3 values, the third one is to make sure the cell stays at standar format (in some versions of Excel, like mine 2007, when involving text formatted cells into formulas, the formulated cell autoformat itself to text format, making the formula to work just once).
As you can see in the image, it works perfectly but this is just for pattern =Value1+Value2. For different patters you'll need different formulas.
If you want to make a formula to work with all patterns, then indeed you need VBA.
I have multiple values in one cell that are ordered like this:
0#0#54232#5#123# ...
Now I want to put every single value in a separate cell. I tried working with SEARCH, FIND, LEFT, MID functions and so on, but everthing looks so fiddly and I can't get it to work. My idea was to look for every # in the text and get every substring in between. But with 20+ values in a single cell this gets very confusing by the end.
EDIT: The lengths of the values can vary
You can use FILTERXML to split the string (this solution will work on Office 365, since dynamic arrays need to be available):
=FILTERXML("<t><s>"&SUBSTITUTE(A1;"#";"</s><s>")&"</s></t>";"//s[string-length()>0]")
There has recently been a nice question here on Stackoverflow that I highly recommend to read:
Excel - Extract substring(s) from string using FILTERXML
You can use following formula (also in older excel versions):
=IFERROR(FILTERXML("<t><s>"&SUBSTITUTE($A$1;"#";"</s><s>")&"</s></t>";"//s["&ROW()&"]");"")
I've put your string in cell B3, and in cell C2, I've put following formula:
=LEFT(B3;FIND.SPEC("#";B3)-1)
In cell C3, I've put following formula:
=SUBSTITUTE(B3;C2&"#";"";1)
I've been dragging the whole thing to the right, and row 2 gave me the results you are looking for. (You might need to add some IfBlank() or IfError() but you understand what I mean)
I am trying to produce a count of the number of times different strings come up in an Excel table. An example table, currently in SHEET1, would be this:
I have another table in another spreadsheet where I want to indicate, for each letter on the left in Table 1, how many entries for "za", "zc" or "zd" come up on the right. However, I would only like to only consider one entry of each.
The end result, on row B of SHEET2, would have to be something like this:
At the moment I am using a combination of SUM and COUNTIFS to do the job.
More specifically, applied to the example, I am using the following formula:
=SUM(COUNTIFS(Sheet1!A1:A18,Sheet2!$A1,Sheet1!B1:B18,{"za","zc","zd"}))
The formula is doing some of what is intended. However, it is not counting each entry just one time. Instead, its is counting, for each letter on the left, every entry of "za","zc" or "zd". The table that the formula is returning is as follows:
How can I change the formula so that it does what I intend?
Thank you.
My initial thought would be:
=SUM(MIN (1,COUNTIFS(Sheet1!A1:A18,Sheet2!$A1,Sheet1!B1:B18,{"za","zc","zd"}))
but I’m not where I can test if the MIN will apply properly to the COUNTIFS array of results. ;-)
EDITED: The MIN function is taking minimum of 1 or all of the items in the COUNTIFS array, rather than minimum of 1 and each item in the COUNTIFS array, which is what I was afraid of. Using
=MIN(COUNTIFS(Sheet1!A$1:A$18,Sheet2!$A1,Sheet1!B$1:B$18,"za"),1)+MIN(COUNTIFS(Sheet1!A$1:A$18,Sheet2!$A1,Sheet1!B$1:B$18,"zc"),1)+MIN(COUNTIFS(Sheet1!A$1:A$18,Sheet2!$A1,Sheet1!B$1:B$18,"zd"),1)
will gain the desired results. It is a little clunky, but simpler than an array formula. If you want an array formula, you can use:
=SUM(FREQUENCY(IFERROR(MATCH({"za","zc","zd"},(IF(Sheet1!$A$1:$A$18=$A5,Sheet1!$B$1:$B$18)),0),""),IFERROR(MATCH({"za","zc","zd"},(IF(Sheet1!$A$1:$A$18=$A5,Sheet1!$B$1:$B$18)),0),"")))
This uses the FREQUENCY function to take a set of values and see how many items in another set of values fall within each of the data ranges. Since you need text instead of numbers, we use the MATCH function to find out the first time the value occurs in your list, returning "" with the IFERROR function if it doesn't. (We only need the first occurrence since you don't want to know how many occurrences there are). Since it is text, we use the same input for both arguments for FREQUENCY.
Therefore, if you need to change the values you are looking for or the ranges in which you are searching, make sure to change both! Alternately, you could list the values out somewhere, say in F1:F3, and make a named range for this, another one for A1:A18, and another for B1:B18. Your formula would then look something like this:
=SUM(FREQUENCY(IFERROR(MATCH(SearchValues,(IF(colA=$A2,colB)),0),""),IFERROR(MATCH(SearchValues,(IF(colA=$A2,colB)),0),"")))
Then you need only change your named range definitions and your formulas would update. :-)
NOTE: Since this is an array formula, you must close out of the cell by pressing CTRL+SHIFT+ENTER rather than only ENTER. When you look at the formula bar, you should see
{=SUM(FREQUENCY(IFERROR(MATCH(SearchValues,(IF(colA=$A2,colB)),0),""),IFERROR(MATCH(SearchValues,(IF(colA=$A2,colB)),0),"")))}
It does NOT work to enter the curly braces yourself. ;-)
You can use this formula at B1 and fill down:
B1:
=SUMPRODUCT(((Sheet1!$A$1:$A$18=A1)*(Sheet1!$B$1:$B$18= {"za","zc","zd"}))/
COUNTIFS(Sheet1!$A$1:$A$18,Sheet1!$A$1:$A$18,Sheet1!$B$1:$B$18,Sheet1!$B$1:$B$18))
Similar questions to this have been asked but not exactly like this. There is one that is very close, but that solution is not working for me.
I have a function which returns comma separated values into a cell. I would like to pass the elements in that cell as criteria to a SUMIFS function using an approach like this one.
My attempt is pictured below:
I believe that this is somehow tied to the way that the function is understanding what is in cell G8. It looks like it is adding some extra quotes. If I highlight G8 in the formula bar and press F9, I get:
There are extra quotes on each side of each criteria.
I am open to a custom VBA function solution, but I would prefer something which can be built as a worksheet function. The criteria are returned from a custom VBA function that pulls elements out of a list box and does some regex work to remove extra commas. The number of elements that can be selected is variable so I would like to avoid having to split the criteria into more than one cell. Thanks.
Seems that the raw comma-separated criteria is in G6, All you need is to split this criteria into an array and feed it to SUMIFS. Splitting is available in VBA, but not exposed to Excel. All we need is to write a little UDF that does the splitting of the CSV and use it in our formula:
Function splitCSV(str As String)
splitCSV = Split(str, ",")
End Function
Now the formula in F10 would be:
=SUM(SUMIFS(C3:C10, B3:B10, "blue", A3:A10, splitCSV(G6)))
EDIT
The above is an array formula (Ctrl+Shift+Enter). To have it a normal formula we can use SUMPRODUCT instead of SUM. This leads to more flexibility (normal formula vs array formula) as well as some "expected" performance improvement.
=SUMPRODUCT(SUMIFS(C3:C10, B3:B10, "blue", A3:A10, splitCSV(G6)))
I have a portion of code that is like the following:
I am trying to return the cell directly below the cell that is "A" but I do not want to have any whitespace in between the cells.
I tried using =IF(A1="A",OFFSET(A1,1,0)) but this adds "FALSE" in between the cells where this is not true like so:
How do I remove the whitespace using a formula only to get the output of:
This standard formula will collect the values directly under each cell containing A.
Use this in B1 =IFERROR(INDEX(A:A,SMALL(INDEX(ROW(A:A)+(A:A<>"A")*1E+99,,),ROW(1:1))+1),"") and fill down as necessary.
The IFERROR return empty strings when it runs out of values to return. These are not the same as truly blank cells.
Although entered as a standard formula, this does use array processing and if you find calculation lag a problem pare down the full column range references to something closer to what the extents of your data represent.
If you would prefer to avoid VBA, you can perform this calculation using the following formulas, or some variant thereof:
The output will look like the following for the example you provided: