Related
I'm trying to generate a random 8 character alphanumeric string in Excel (or Google Sheets or Libreoffice, which both have the same challenge) using a formula. I'd like to get something like this:
6n1a3pax
I've tried various formulae including ones like this which generate the ASCII characters for individual random numbers between an upper and lower number:
=CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) &CHAR(RANDBETWEEN(65,90))& CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90)) & CHAR(RANDBETWEEN(65,90))
However, they're lengthy, you have to repeat the RANDBETWEEN() function multiple times inside a formula, and you can't choose both "alpha" and "numeric" in the same RANDBETWEEN().
Is there any easy way to do this in Excel, Google Sheets or LibreOffice Calc? If a solution works in one and not in the others then great if you can mention which one(s).
(N.B. This is not a duplicate of questions about how to stop recalculation of randomisation functions in Excel)
in GS try:
=LAMBDA(x, x)(DEC2HEX(RANDBETWEEN(0, HEX2DEC("FFFFFFFF")), 8))
if that's not enough and you need
A-Z char 65-90
a-z char 97-122
0-9 char 48-58
=JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, IF(COINFLIP(), IF(COINFLIP(),
CHAR(RANDBETWEEN(65, 90)), CHAR(RANDBETWEEN(97, 122))), RANDBETWEEN(0, 9)))))
frozen:
=LAMBDA(x, x)(JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, IF(COINFLIP(), IF(COINFLIP(),
CHAR(RANDBETWEEN(65, 90)), CHAR(RANDBETWEEN(97, 122))), RANDBETWEEN(0, 9))))))
alternative (with better distribution):
=JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, SINGLE(SORT(CHAR({
SEQUENCE(10, 1, 48);
SEQUENCE(26, 1, 65);
SEQUENCE(26, 1, 97)}),
RANDARRAY(62, 1), )))))
or frozen:
=LAMBDA(x, x)(JOIN(, BYROW(SEQUENCE(8), LAMBDA(x, SINGLE(SORT(CHAR({
SEQUENCE(10, 1, 48);
SEQUENCE(26, 1, 65);
SEQUENCE(26, 1, 97)}),
RANDARRAY(62, 1), ))))))
for more see: stackoverflow.com/questions/66201364
LibreOffice Calc 7.x:
A non-volatile option for LibreOffice Calc 7.x is the use of the RANDBETWEEN.NV() function:
Formula in A1:
=CONCAT(IF({1,2,3,4,5,6,7,8},MID("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",RANDBETWEEN.NV(1,62),1),))
Note that using ROW(1:8) would still force recalculation when any value in rows 1-8 have been made (thus volatile):
=CONCAT(IF(ROW(1:8),MID("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",RANDBETWEEN.NV(1,62),1),))
Excel ms365:
Unfortunately there is, AFAIK, not a non-volatile Excel equivalent to this function. If volatility is not a problem, then try:
=CONCAT(MID("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",RANDARRAY(8,,1,62,1),1))
Here's my take, for Google Sheets:
=lambda(_,
_
)(
lambda(
numWords, wordLength, charRegex, ascii,
lambda(
alphabet,
map(
sequence(numWords),
lambda(_,
concatenate(
map(
sequence(wordLength),
lambda(_,
mid(alphabet, randbetween(1, len(alphabet)), 1)
)
)
)
)
)
)(concatenate(filter(ascii, regexmatch(ascii, charRegex))))
)(10, 8, "[0-9a-zA-Z]", arrayformula(char(sequence(127))))
)
The formula will generate 10 passwords of 8 characters each from an alphabet that includes lower and upper case letters, and digits.
To choose which characters to include in the alphabet, replace [0-9a-zA-Z] with another regex like [0-9a-z!#$%&/] or [-!#$%&/\w]. Note that you may need to \escape any regex special characters there.
The pattern avoids the non-uniform distribution issues that plague some of the solutions presented in this thread. The ones that use coinflip() or isodd(rand()*N) will give results that overrepresent smaller sub alphabets like 0-9. The ones that use sort() will not repeat any chars in the result, which is not optimal.
It's possible to do this in Excel using a combination of the following functions:
SEQUENCE() VSTACK() RANDARRAY() CHAR() INDEX() TEXTJOIN()
Unfortunately this doesn't work in LibreOffice (at the moment) as it does not have the SEQUENCE() function. It does not work in Google Sheets as the RANDARRAY() function only takes 2 parameters and the VSTACK() function does not exist, although you can use braces and a semicolon, e.g. {SEQUENCE(26,1,97,1);SEQUENCE(10,1,48,1)}.
Here's the formula you need:
Upper-case e.g "413BK5S0": =TEXTJOIN("",1,INDEX(CHAR(VSTACK(SEQUENCE(26,1,65,1),SEQUENCE(10,1,48,1))),RANDARRAY(8,1,1,36,TRUE)))
Lower-case e.g. "b8etbno8": =TEXTJOIN("",1,INDEX(CHAR(VSTACK(SEQUENCE(26,1,97,1),SEQUENCE(10,1,48,1))),RANDARRAY(8,1,1,36,TRUE)))
The following explanation for each function:
SEQUENCE() - a sequence of e.g. 26 numbers, in 1 column, starting at number 65, increasing by 1 each time (with the second incidence of the function being 10 numbers starting at 48)
VSTACK() - combine the 2 SEQUENCE() formulae into 1 array (sequence) of numbers
CHAR() - the ASCII character associated with a decimal ASCII number (where the decimal number is generated by the SEQUENCE() function) - see https://www.asciitable.com/
RANDARRAY() - an array of 8 random numbers, 1 column wide, minimum number 1, maximum 36
INDEX() - the value from each element within the sequence of characters, where each of 8 element numbers is provided by RANDARRAY()
TEXTJOIN() - join the values in an array together into one cell, with no separator and ignoring empty values
What do you think of something like this?
=CONCATENATE(BYROW(SEQUENCE(8),LAMBDA(e,IF(ISODD(ROUNDUP(RAND()*10)),CHAR(RANDBETWEEN(65,90)),ROUNDDOWN(RAND()*10)))))
If you want to include lower case, you can do a similar logic:
=CONCATENATE(BYROW(SEQUENCE(8),LAMBDA(e,IF(ISODD(ROUNDUP(RAND()*10)),IF(ISODD(ROUNDUP(RAND()*10)),CHAR(RANDBETWEEN(65,90)),CHAR(RANDBETWEEN(97,122))),ROUNDDOWN(RAND()*10)))))
The logic is the next one: what I'm doing is with ISODD(ROUNDUP(RAND()*10) generating a random number between 1 and 10 and checking if it's odd. If it is, it generates a letter or else it generates a number. With CONCATENATE(BYROW(SEQUENCE(8)... I'm doing this 8 times and concatenating them. What I just added was a second "random and odd" time when it's time to generate a letter so you can have upper and lower case
Suppose I have a column of wind directions ("N","S","W",E"). Each cell only contains 1 letter. If I am to find the most common wind directions,=CHAR(MODE(CODE(range))) will do the job
But if I handle wind directions like "SW","NE", the above function would not work. I know that =INDEX(range, MODE(MATCH(range, range, 0 ))) will work.
Just curious, somewhat similar to the first function, is there a way to substitue strings with numbers of choice only when passing in the column into MODE()function, so that it will return a number for me to MATCH() and INDEX() to get the result?
Clarify: Say that I have the following data
And I would like to substitue "N" with 0, "NE" with 45, "SW" with 225 and so on. So that MODE() will be applicable. And if needed, I can then use functions like INDEX(MATCH()) to return the actual letter representation of the wind direction.
=SWITCH(MODE(SWITCH(A:A,"N",0,"NE",45,"E",90,"SE",135,"S",180,"SW",225,"W",270,"NW",315,"")),0,"N",45,"NE",90,"E",135,"SE",180,"S",225,"SW",270,"W",315,"NW","")
Or similar:
=CHOOSE(1+MODE(SWITCH(A:A,"N",0,"NE",45,"E",90,"SE",135,"S",180,"SW",225,"W",270,"NW",315,""))/45,"N","NE","E","SE","S","SW","W","NW")
This first translates the strings to values, then translates the MODE result back to it's string.
I'm parsing strings in excel, and I need to return everything through the last number. For example:
Input: A00XX
Output: A00
In my case, I know the last number will be between index 3 and 5, so I'm brute-forcing it with:
=LEFT([#Point],
IF(SUM((MID([#Point],5,1)={"0","1","2","3","4","5","6","7","8","9"})+0),5,
IF(SUM((MID([#Point],4,1)={"0","1","2","3","4","5","6","7","8","9"})+0),4,
IF(SUM((MID([#Point],3,1)={"0","1","2","3","4","5","6","7","8","9"})+0),3,
))))
Unfortunately, I've run into some edge cases where the numbers extended beyond index 5. Is there a generic way to find the last number in a string using excel formulas?
Note:
I've tried =MAX(SEARCH(... but it returns the index of the first number, not the last.
As a starting point: if we know the position of the last number, we can use LEFT to get the string to that point. Suppose that the position is 5:
=LEFT(A1, 5)
But, we don't know the position of the last number. Now, what if the only valid number was 0, and it only appeared once: then we could use FIND to locate the position of the number:
=LEFT(A1, FIND(0, A1))
But, we have more than one valid number. Suppose that we had all the numbers from 0 through 9, but each number could only appear once — then we could use MAX on a FIND array, to tell us which of the numbers is the last one:
=LEFT(A1, MAX(FIND({0,1,2,3,4,5,6,7,8,9}, A1)))
Unfortunately, FIND will throw a #VALUE! error any number doesn't appear, which will then make MAX return the same error. So, we need to fix that with IFERROR:
=LEFT(A1, MAX(IFERROR(FIND({0,1,2,3,4,5,6,7,8,9}, A1), 0)))
However, numbers can appear more than once. As such, we need a method to find the last occurrence of a value in a string (since FIND and SEARCH will, by default, return the first occurrence).
The SUBSTITUTE function has 3 mandatory arguments — Initial String, Value to be Replaced, Value to Replace with — and one Optional argument — the occurrence to replace. Normally, this is omitted, so that all occurrences are replaced. But, if we know how many times a character appears in a string, then we can replace just the last instance with a special/uncommon sub-string to search for.
To count how many times a character appears in a String, just start with the length of the String, then subtract the length when you SUBSTITUTE all copies of that character for Nothing:
=LEN(A1) - LEN(SUBSTITUTE(A1, 0, ""))
This means we can now replace the last occurrence of the character with, for example, ">¦<", and then FIND that:
=FIND(">¦<", SUBSTITUTE(A1, 0, ">¦<", LEN(A1) - LEN(SUBSTITUTE(A1, 0, ""))))
Of course, we want to do this for all the numbers from 0 to 9, and take the MAX value (remembering our IFERROR), so we need to put the Array of values back in:
=MAX(IFERROR(FIND(">¦<", SUBSTITUTE(A1, {0,1,2,3,4,5,6,7,8,9}, ">¦<", LEN(A1) - LEN(SUBSTITUTE(A1, {0,1,2,3,4,5,6,7,8,9}, "")))), 0))
Then, we plug that all back into our initial LEFT function:
=LEFT(A1, MAX(IFERROR(FIND(">¦<", SUBSTITUTE(A1, {0,1,2,3,4,5,6,7,8,9}, ">¦<", LEN(A1) - LEN(SUBSTITUTE(A1, {0,1,2,3,4,5,6,7,8,9}, "")))), 0)))
An alternative, assuming that the length of the string in question will never be more than 9 characters (which seems a safe assumption based on your description):
=LEFT(A1,MATCH(0,0+ISERR(0+MID(A1,{1;2;3;4;5;6;7;8;9},1))))
This, depending on your version of Excel, may or may not require committing with CTRL+SHIFT+ENTER.
Note also that the separator within the array constant {1;2;3;4;5;6;7;8;9} is the semicolon, which, for English-language versions of Excel, represents the row-separator. This may require amending if you are using a non-English-language version.
Of course, we can replace this static constant with a dynamic construction. However, since we are already making the assumption that 9 is an upper limit on the number of characters for the string in question, this would not seem to be necessary.
If you have the newest version of Excel, you can try something like:
=LEFT(D1,
LET(x, SEQUENCE(LEN(D1)),
MAX(IF(ISNUMBER(NUMBERVALUE(MID(D1, SEQUENCE(LEN(D1)), 1))), x))))
For example:
i have a lot of address data in (mostly) this format:
Karl-Reimann-Ring 13, 99087 Erfurt
Markttwiete 2, 23611 Bad Schwartau
Hüxstraße 55, 23552 Lübeck
Bunsenstraße 1c, 24145 Kiel
and my goal is to extract the zip code.
I copied a formula from a website, which i dont really understand:
=VERWEIS(9^9;1*TEIL(E2861&"#";SPALTE(2860:2860);6))
VERWEIS = LOOKUP,
TEIL = MID,
SPALTE = COLUMN
This formula seems to work 99% of the time, also for the ones above, but for some i get weird results.
Kurt-Schumacher-Straße 56, 55124 Mainz --> 44340
Kleine Früchtstraße 6, 55130 Mainz --> 44346
Bahnstraße 1, 55128 Mainz --> 44344
All with 'Mainz' are wrong and start with 44xxx
But when i increase the last argument from 6 to 7 it seems to work.
Do somebody know, how i can impove this formula to get always the correct zip code?
The problem is that the formula will return the last "number" which is constructed of 6 character strings starting at every character in the string.
The last substring that can be interpreted numerically (in the 55424 Mainz address) is actually 24 Mai. German Excel will parse that into 24 Mai 2021 which, as a number, will be 44340.
One modification you can make to your formula, to prevent that from happening, would be to add a comma after the zipcode. eg:
=LOOKUP(9^9;1*MID(SUBSTITUTE(A1;" ";",") & "#";COLUMN(2860:2860);6))
Another option would be to use FILTERXML where you can separate by spaces, and then return the last numeric node:
=FILTERXML("<t><s>" &SUBSTITUTE(A1;" ";"</s><s>") & "</s></t>";"//s[number(.) = number(.)][last()]")
I have a cell which contains any 4 letters, example akei, skiw. How do I ask
"if the 3rd letter is an i then equals True"
I was thinking something like
"=if(a1="??i?", True, False)"
But that dosen't Work
=MID(A1,3,1) = "i"
Should work, you don't need to use IF, the evaluation using the equals will return either TRUE or FALSE
The MID function let you to select a portion of the text, if you set the Position Start and the numbers of characters you want
=MID(A1,3,1) = "i"
so you just compare it to "i"
You could use the wildcard approach if you use COUNTIF like this
=COUNTIF(A1,"??i?")
That will return 1 or 0 and effectively tests two things, that A1 contains 4 characters AND the third one is "i"
As with MID this isn't case-sensitive so 1 will be returned for both XXIX and zziz
So I came upon this question for a similar use: Expanding a numbering system for lab specimen with each digit distinguishing something different and the 3rd digit indicating a heating profile
The resulting function was used to expand these codes out for people that would not know my logic otherwise:
=IF(MID(B2,3,1) = "1", "Temp1°C for 1 hour", "Temp2°C for X hours")