Related
This is my first StackOverflow question, so apologies if I am unclear.
Currently, my work uses an Excel tracking doc to log project info. The column info is like so:
CELL B1 (Project Number) =IF(B2=""," ",MID(B2,FIND("P2",B2),9))
CELL B2 (Project Name) Client / P2XXXXXXX / Name
Thus, the P2XXXXXXX gets pulled out of B2 and populated into B1.
However, management has recently switched systems, so now, some project numbers have the P2XXXXXXX format and others have a PRJ-XXXXX format.
So we need a formula the produces nothing if the cell is blank and EITHER the P2XXXXXXX number or PRJ-XXXXX number if the cell is not blank.
Is it possible? If any further details are needed, let me know. Thanks in advance!
Well, if the / is always there then this can work:
IF(B2="","",MID(B2,FIND("/",B2,1)+2,9))
assuming the name is always 9 characters.
String Between Two Same Characters
Maybe the next month your company will start using a different first letter or could add more numbers e.g. SPRXXXXXXXXXX. So you could solve this problem by extracting whatever is between those two slashes.
=IF(B2="","",TRIM(MID(B2,FIND("/",B2)+1,FIND("/",B2,FIND("/",B2)+1)-FIND("/",B2)-1)))
Find the first character =FIND("/",B2), but we need the next one:
=FIND("/",B2)+1
Find the second character but search from the postition after the first found:
=FIND("/",B2,FIND("/",B2)+1)
Now get the string between them:
=MID(B2,FIND("/",B2)+1,FIND("/",B2,FIND("/",B2)+1)-FIND("/",B2)-1)
(note how the last minus was 'converted' from a plus to a minus (- + + = -)).
Remove the leading and trailing spaces:
=TRIM(MID(B2,FIND("/",B2)+1,FIND("/",B2,FIND("/",B2)+1)-FIND("/",B2)-1))
Add the condition when the cell is blank:
=IF(B2="","",TRIM(MID(B2,FIND("/",B2)+1,FIND("/",B2,FIND("/",B2)+1)-FIND("/",B2)-1)))
Here's another way using LEFT and RIGHT:
=IF(B2="","",TRIM(LEFT(RIGHT(B2,LEN(B2)-FIND("/",B2)),FIND("/",B2))))
Although you can solve this problem with a combination of slicing, trimming, and complex conditionals, the most expressive and easy to maintain solution is to use regular expressions. Regular expressions have a bit of a learning curve, but there's a great playground website where you can experiment with them, and this page has a pretty good writeup on how regular expressions work in excel.
Specifically, this regular expression addresses the two naming conventions you've highlighted, but it can be updated to support more naming conventions as your company inevitably adds more:
P(RJ-)?((\d){9}|(\d){5})
To break that down from left to right:
P: both patterns start with a "P"
(RJ-)? One pattern follows with "RJ-", but the other doesn't. This is a grouped part of the pattern, and the question mark means that this part of the pattern is optional.
((\d){9}|(\d){5}): by far the nastiest part, but this basically means that there is going to be a sequence of numbers (\d), and there will either be nine of them or five of them. By wrapping the whole thing in parenthesis, they are always the second captured group, no matter the length of the sequence of numbers. This means that you can always extract the project id by looking at the value of the second capture group.
You can also make the expression more generalized by replacing ((\d){9}|(\d){5}) with simply (\d+). That just means "one or more digits." That gives you a much more simplified overall expression of this:
P(RJ-)?(\d+)
Depending on whether or not you care about validating strictly that project ids are 5 OR 9 digits long, that pattern above might be suitable, and it has the benefit of being more flexible. Still, the project ID is in the second captured group.
I wrote a user-defined function for fractions to be displayed with superscripted & subscripted digits (available in Unicode), with denominator no more than the user wants to. I could basically turn π into ²²/₇ with "=Fraction(PI(),30)", since no other fraction would be closer to π with a denominator smaller than or equal to 30.
Then I'm thinking of writing an InvFraction function as well, to get from a string generated by the Fraction function into an actual number. As you can imagine, though, the value is not π anymore, but 3.142857... (i.e. ²²/₇). So I'm postponing the writing until I remove that sense of chasing a ghost I'm feeling about it.
I saw that one could make the Fraction function generate a size-2 array of values, then through the index function, let the user decide which one to display, or enter the Fraction function as an array function covering 2 cells. Neither one is ideal from my perspective, the first option because the second value, which could be π, gets lost through the index choice and is no longer retrievable, the second option because it forces two cells to contain the data (though I guess I COULD end up living with it).
I also thought of using user-defined types containing the string value for the fraction and the double value for the original input, but I noticed they don't work in the actual sheet, then informally confirmed the info there: Call VBA function that returns custom type from spreadsheet
Anyone would have any idea at how to tackle this? Thanks anyways for having taken the time to read.
Edit: To put it simply, if I were to program the InvFraction function as I conceived it with the tools and ideas I have, I could only manage to have “=InvFraction(Fraction(PI(),30))” to equal 3.142857... (22 divided by 7), but I would rather like it to generate 3.14159265... (π).
First off, thank you to all how have helped get me to this point. I'm so close! On to the scenario, which I apologize in advance is a bit of a work in progress.
I have text in a cell and I need to extract a number. The tricky part is there are various situations to address.
The number may immediately follow a "#" and could vary in length. People on Stack Overflow helped me with coming up with this which works great:
MID(B2,(FIND("#",B2,1)+1),FIND(" ",B2,FIND("#",B2,1)+1)-FIND("#",B2,1))
That was a huge leap forward, but there are also situations where there is no # sign and the cell might have "abc (1205) 645 chan", where I need to extract the 645.
I'm using this, below, in conjunction with an on error statement for when there is no "#"
TRIM(MID(B53,(FIND(" " &{"1","2","3","4","5","6","7","8","9"},B53,1)),FIND(" ",B53,FIND({"1","2","3","4","5","6","7","8","9"},B53,1))-FIND({"1","2","3","4","5","6","7","8","9"},B53)))
So I use the first Mid/Find to avoid the (1205) and find the next " x" where x is a number. The problem is it seems I have trouble when the number I'm searching for has 1 or 3+ numbers in it, but if it has 2 I return the value just fine.
It seems I'm very close but just not there yet.
This formula will return the number that follows either a # or a ) in your string. If that pattern does not exist, it will return a #NUM!` error
=AGGREGATE(14,6,--MID(A1,MIN(FIND({"#",")"},A1&"#)"))+1,{1,2,3,4,5}),1)
Note the array constant as the num_chars argument of the MID function. The maximum number should be at least the largest number of digits (or decimal + digits) plus any spaces between the delimiter and the first digit, that might be expected to be found.
EDIT: If your version of Excel is prior to 2010, and does not have the AGGREGATE function, you may use this array-entered formula instead, so long as the values to be returned will be positive numbers:
=MAX(IFERROR(--MID(A1,MIN(FIND({"#",")"},A1&"#)"))+1,{1,2,3,4,5}),0))
This formula must be entered by holding down ctrl+shift while hitting enter
If I have a password, "rusty", and I input the sequence: "rusty123", "Rusty" and "rush" (which in turn is saved to a list newList), when I print out newList, how do I display a result that says:
rusty123, wrong by 3 characters
Rusty, wrong by 1 characters
rush, wrong by 2 characters
?
What I need to add is a function like (countDifference) that allows me to compare the right password 'rusty' with wrong passwords entered. So if I enter 'rusty123', it should compare 'rusty' to 'rusty123' and save the result as a integer (3 - because the password is off by 3 characters i.e. 123 is wrong). I then convert this integer to a string and record it to the file newFile.
I think something that takes (password ='rusty') as a constant, and then reads every line of a new password input, and compares it so 'rusty' will do the trick, but I just don't know how.
password = "rusty"
user_input = raw_input("Please enter the password")
so the user inputs: "Rusty" and the function reads that the password is wrong by 1 character, namely "R" - (should have been lower case)
SOLVED: If you have the same problem, follow the link that #Chris Beck provides at the end of his explanation. It solved this problem perfectly.
Is there a function that can help me determine by how many characters (in integers) the wrong password (entered as a string) was from the right password?
So, this could mean a few different things. There's a few different notions of "by how many characters does this string differ from that string" that people use. Some of them are easier to program than others, if you are new then you might not want to use the most sophisticated versions.
"Distance" from string A to string B
Simplest:
At how many indices i does A[i] != B[i]? (If one string is longer than the other then count all those indices as mismatching also.)
That's the easiest one to implement. However it doesn't always give the most intuitive results. If I have
A = "abracadabra"
B = "abrarcadabra"
the distance of these strings is going to be 8, even though they are only off "by one letter".
Harder: Edit Distance
Under the edit distance, A and B above would have distance 1. Edit distance means, how many insertions / deletions would have to be performed to change A into B. Under some variations, a swap of two adjacent characters is also thought to count as distance only 1.
The usual way to compute edit distance is using dynamic programming. You can see examples of this here: Edit Distance in Python
I have one Measure which I am using as a denominator in calculating multiple calculated measures. The user typically drags and drops Numerator, denominator and calculation measure to view the report.
It works fine. But now I want to implement suppression rules defined by business. For example: If the calculated % is <=5 then denominator value should be shown as *.
I have used SCOPE statement to handle this. But now I have ended up defining multiple SCOPE statments for single denominator measure. The problem is, as you must have already guessed, the result of one Scope statement is clashing with other scope statements defined for same denominator measure.
For example: Even though denominator is supposed to show actual value for a particular % calculation, it supresses it with * because other % calculation in which the same denominator measure is used returns <=5 value.
My question is: Is it possible to find out, through MDX SCOPE or something like that, which measures are actually in use, or dragged and dropped by user in report?
May be a silly question but please help. Or give me some different perspective to solve this problem.
Thanks,
Parry
To answer the part of your post which says "Is it possible to find out which measures are actually in use?" I suggest this trick: SetToStr(StrToSet("Axis(1)"))
This will get a list of items on rows (or columns) and return it as a string. In a Micrsoft environment you could use InStr() to search this for Measure names, and decide what to do with an IIF(). I hope this gives you a starting point! I have a feeling the MDX could get very long and very messy, hurting your brain.
An easier solution to the "If the calculated % is <=5 then denominator value should be shown as *" problem is to add some code to the DLL which renders the cellset on a webpage. Just detect if you're in the relevant Measure's column, then check the cell's value is <=5 and decide what to output to the user at that point. This might hurt your brain less than the MDX required.