The formula below produces permutations of the digits in the array cols - in this case 1 to 6. The perms variable outputs a 2d array of the permutations without repetitions.
However, I want to get combinations, not permutations. So 1,2,4 is identical to 4,2,1. My idea was to sort each row and then filter out duplicate rows. Every variable works as expected up to the final SORT part, which is BYROW(perms,LAMBDA(row,SORT(row,,,TRUE))). This produces #calc.
Desired behaviour: Taking the first few rows, the variable perms is:
| 1 | | | | | |
| 2 | | | | | |
| 3 | | | | | |
| 4 | | | | | |
| 5 | | | | | |
| 6 | | | | | |
| 1 | 2 | | | | |
| 1 | 3 | | | | |
| 1 | 4 | | | | |
| 1 | 5 | | | | |
| 1 | 6 | | | | |
| 2 | 1 | | | | |
with the LAMBDA function, it should become:
| 1 | | | | | |
| 2 | | | | | |
| 3 | | | | | |
| 4 | | | | | |
| 5 | | | | | |
| 6 | | | | | |
| 1 | 2 | | | | |
| 1 | 3 | | | | |
| 1 | 4 | | | | |
| 1 | 5 | | | | |
| 1 | 6 | | | | |
| 1 | 2 | | | | |
In other words, the row [2, 1] should be converted to [1, 2], and the row [4, 3] should be converted to [3, 4], and so on.
The formula is:
=LET(
cols, SEQUENCE(1, COLUMNS(TableStu)),
firstperm, VALUE(CONCAT(cols)),
lastperm, VALUE(CONCAT(SORT(cols,,-1,TRUE))),
diff, (lastperm-firstperm)+1,
list, SEQUENCE(diff, 1, firstperm),
wanted, FILTER(list, LEN(REDUCE(list, cols, LAMBDA(m,j, SUBSTITUTE(m,j,"")))) = 0),
all, FILTER(wanted, wanted <> "", ""),
repeaters, UNIQUE(TOCOL(IF(LEN(all)-LEN(SUBSTITUTE(all,cols,""))=0, all, ""))),
repsnoblanks, FILTER(repeaters, LEN(repeaters) > 0),
allnoreps, FILTER(all, NOT(ISNUMBER(XMATCH(all, repsnoblanks)))),
allAndLess, VALUE(TOCOL(VSTACK(allnoreps, LEFT(allnoreps, SEQUENCE(1, 4))))),
nums, UNIQUE(SORT(FILTER(allAndLess, NOT(ISNA(allAndLess))))),
starts, IF(REDUCE(cols, MAX(cols), LAMBDA(x,y, cols <= MAX(cols))), cols, ""),
perms, IFERROR(VALUE(MID(nums, starts, 1)), ""),
BYROW(perms,LAMBDA(row,SORT(row,,,TRUE)))
)
What I've tried:
Changed perms from
IFERROR(VALUE(MID(nums, starts, 1)), "")
to
IFERROR(VALUE(MID(nums, starts, 1)), 0)
or
IFERROR(VALUE(MID(nums, starts, 1)), 9999)
still gives #calc.
I'm aware my formula contains a lot of steps and is quite counter-intuitive, as well as probably inefficient. However, I need a solely formula-based solution, because this has to be applied to non-technical users who can't handle refreshing a power query, let alone enabling vba and running a macro. If you can come up with a more readable and efficient formula that works with an array of digits like cols, please let me know.
Edit: SORT(perms,,,TRUE) does not give a result any different to just perms. That is the table shown first will stay the same. It seems byrow can only handle a single column, so what function should I use?
In Excel, with the exception of REDUCE
, the lambda portion of the lambda helper functions cannot return an array. This is why the following formula doesn't work
BYROW(perms,LAMBDA(row,SORT(row,,,TRUE)))
Try something like this instead
REDUCE("",SEQUENCE(ROWS(perms)),LAMBDA(acc,i,VSTACK(acc,SORT(INDEX(perms,i),,,TRUE))))
However, if your goal is to return the set of all subsets of a given set, you could simply use
MID(REDUCE("",set,LAMBDA(a,c,VSTACK(a,a&","&c))),2,9^9)