Home > Enterprise >  Excel Randbetween in excel puzzle
Excel Randbetween in excel puzzle

Time:10-23

Here is my problem.

If cell A1 is not empty then I would like to choose a random name from a list (the names are Ben, Bill, Bob). The formula for this as follows: IF(A1<>"",INDEX(Q:Q,RANDBETWEEN(2,COUNTA(Q:Q))))

So far so good...

However, say A1 = Ben, Bill or Bob, then I would like my formula to exclude that name and pick another name from the list.

I can't figure out how to do this annoyingly. Any help is much appreciated, thanks.

CodePudding user response:

Option for older versions:

=IF(A1<>"",INDEX(Q:Q,AGGREGATE(15,6,ROW(Q:Q)/((Q:Q<>A1)*(Q:Q<>"")),RANDBETWEEN(1,SUMPRODUCT((Q:Q<>"")*(Q:Q<>A1))))))

enter image description here

CodePudding user response:

EDIT P.b in the comments notes that my filter can be reduced from

FILTER(FILTER(F:F,F:F<>""),FILTER(F:F,F:F<>"")<>A1) 

to

FILTER(F:F,(F:F<>"")*(F:F<>A1)) 

making the final function:

=INDEX(
  FILTER(F:F,(F:F<>"")*(F:F<>A1)),
  RANDBETWEEN(1, COUNTA(FILTER(F:F,(F:F<>"")*(F:F<>A1))))
)

If you are using a version of Excel that has FILTER, you can use it to filter the list of names, obtain the count, then pass that filtered list to INDEX along with a RANDBETWEEN using that count. Assuming your list of names is in Col F:

=INDEX(
  FILTER(FILTER(F:F, F:F<>""), FILTER(F:F, F:F<>"")<>A1),
  RANDBETWEEN(1, COUNTA(FILTER(FILTER(F:F, F:F<>""), FILTER(F:F, F:F<>"")<>A1)))
)

I am passing F to FILTER twice, once to remove empty rows and once to remove whatever name is in A1. This is the basic filtered list:

FILTER(FILTER(F:F,F:F<>""),FILTER(F:F,F:F<>"")<>A1)

As you can see, I pass that same filtered list to COUNTA and use that in RANDBETWEEN to get an INDEX from the list.

Here is a version using FILTERXML to filter the list. This should be supported in Windows versions back to 2012:

=INDEX(
  FILTERXML("<a><x>" & TEXTJOIN("</x><x>", TRUE, IF((F:F<>A1)*(F:F<>"")>0, F:F, "")) & "</x></a>", "//x"),
  RANDBETWEEN(1, SUM((F:F<>A1)*(F:F<>"")))
)

Here, we are going to use array products to generate a list of matching conditions. We can use the array product passed to sum to get the count for RANDBETWEEN, and also use it to filter the name list, then pass that list to TEXTJOIN to convert it into an xml string, then use FILTERXML to parse the node values back out into an array.

F:F=A1 will return a true/false array, and (F:F=A1)*(F:F<>"") will return a true/false array where F matches both conditions. And we can pass this to SUM to get a count:

=SUM((F:F<>A1)*(F:F<>""))

We can also pass this array to IF and get a list of matching names. We'll tell IF to return the values from F if TRUE or "" if FALSE.

IF((F:F<>A1)*(F:F<>"")>0, F:F, ""))

or to TEXTJOIN, telling it to ignore the empty values, using the separator to generate an xml string:

="<a><x>" & TEXTJOIN("</x><x>", TRUE, IF((F:F<>A1)*(F:F<>"")>0, F:F, "")) & "</x></a>"

And then use FILTERXML with //x to retrieve the values of the x nodes as an array. Then we pass that result to INDEX, along with RANDBETWEEN where we use the sum formula from before as the upper bound. A little hackier than the first one, but it should still work.

  • Related