Here, another alternative using Excel functions. We use a recursive function to generates all the index positions of the input data. This approach produces exactly the set of combination we need without generating an additional set of combination that then require to remove non valid set of values, such duplicates, etc., therefore the calculation steps are optimized.
You need to enter in the Name Manager (see formula at the end in case you don't want to use it), the following user LAMBDA
function: NEXT_ROW
:
=LAMBDA(x,m,n,i, IF(i=0, x, LET(s, SEQUENCE(,n), idx, XMATCH(m, IF(i<n,
IF(s>i, m+1, x+1), x+1),-1,-1), if(idx=i, x + N(s=i),
NEXT_ROW(IF(s=i, INDEX(x+1,idx), x),m,n,i-1)))))
UPDATE: The function can be simplified as follows:
=LAMBDA(x,m,n,i, IF(i=0, x, LET(s, SEQUENCE(,n), y, x+1, idx, XMATCH(m, IF(i<n,
IF(s>i,m+1,y),y),-1,-1),NEXT_ROW(IF(s=i,INDEX(y,idx),x),m,n,IF(idx=i,0,i-1)))))
Where:
x
, array with the index positions with shape: 1 x n
.
m
, The total number of input values we need to distribute in n
positions.
n
, The total number of index positions to distribute
i
, The first index position to evaluate. On every iteration we reduce the index position by one. When i=0
, we finish the recurrence, returning the array of the next row. We start from right to left, so i=n
.
NEXT_ROW
, returns the index positions of the next row, based on input array x
. For example the first element is in the range: A1:C1
for the sample data from the question, where m=6
, n=3
, then:
1 1 1
1 1 2 <- NEXT_ROW(A1:C1,6,3,3)
1 1 3 <- NEXT_ROW(A2:C2,6,3,3)
1 1 4 <- NEXT_ROW(A3:C3,6,3,3)
1 1 5 <- NEXT_ROW(A4:C4,6,3,3)
1 1 6 <- NEXT_ROW(A5:C5,6,3,3)
1 2 2 <- NEXT_ROW(A6:C6,6,3,3)
...
6 6 6 <- NEXT_ROW(A56:C56,6,3,3)
which corresponds with the index position we need.
Note: If you are using Excel Web, which doesn't provide access to the Name Manager, you can install the following add-ins: Advanced Formula Environment. Here is the view of NEXT_ROW
function:

Having the sequence of all possible index positions, to generate the final result is straightforward:
=LET(in, A2:A7, m, ROWS(in), n,3, cnts, SEQUENCE(COMBINA(m,n)-1),
idx, REDUCE(SEQUENCE(,n,1,0), cnts, LAMBDA(ac,i,
VSTACK(ac, NEXT_ROW(IF(i=1, ac, TAKE(ac,-1)),m,n,n)))),INDEX(in, idx))
We use the REDUCE/VSTACK
pattern to generate the entire set of index positions. Check my answer to the question:
how to transform a table in Excel from vertical to horizontal but with different length.
We initialize the accumulator of REDUCE
with the first set of index positions, a constant array of ones: SEQUENCE(,n,1,0)
, that is why we need one less iteration from all total combinations: COMBINA(m,n)
with replacements.
Here is the output:

You can encapsulate the entire process in a new user LAMBDA
function COMBINA_SET
and add to the Name Manager, to reuse it in the future:
=LAMBDA(x, n, LET(y, TOCOL(x), m, ROWS(y), IF(AND(n=1,m=1), x,
LET(cnts, SEQUENCE(COMBINA(m,n)-1), idx, REDUCE(SEQUENCE(,n,1,0), cnts,
LAMBDA(ac,i, VSTACK(ac, NEXT_ROW(IF(i=1, ac, TAKE(ac,-1)),m,n,n)))),
INDEX(y, idx)))))
Then you can invoke it as follows:
COMBINA_SET(A2:A7,3)
We consider additional scenarios for a more general case:
- Treat the special case of
n=1
and m=1
, we don't need a recursive process for that and for this case the previous formula produced an error.
- Allow the general case, where the input argument
x
, can be a column-wise array.
Note: You need anyway to create NEXT_ROW
, because you cannot create a recursive function inside of a LET
statement. You can overcome it, following the suggestion from this post: LAMBDA Formulaic Recursion: It’s All About ME! (credit to @JosWoolley for sharing this link):
=LAMBDA(x, n, LET(NEXT_SET, LAMBDA(ME,x,m,n,i, IF(i=0, x, LET(s, SEQUENCE(,n),
y, x+1, idx, XMATCH(m, IF(i<n, IF(s>i,m+1,y),y),-1,-1),ME(ME, IF(s=i,
INDEX(y,idx),x),m,n,IF(idx=i,0,i-1))))), y, TOCOL(x), m, ROWS(y),
IF(AND(n=1,m=1), x, LET(cnts, SEQUENCE(COMBINA(m,n)-1),
idx, REDUCE(SEQUENCE(,n,1,0), cnts, LAMBDA(ac,i, VSTACK(ac,
NEXT_SET(NEXT_SET, IF(i=1, ac, TAKE(ac,-1)),m,n,n)))),INDEX(y, idx)))))
If you don't really want to use the Name Manager, with the previous approach using ME
workaround to circumvent it, then you can have everything in a single formula:
=LET(A, A2:A7,n,3, COMBINA_SET, LAMBDA(x, n, LET(NEXT_SET, LAMBDA(ME,x,m,n,i,
IF(i=0, x, LET(s, SEQUENCE(,n), y, x+1,
idx, XMATCH(m, IF(i<n, IF(s>i,m+1,y),y),-1,-1),ME(ME, IF(s=i,
INDEX(y,idx),x),m,n,IF(idx=i,0,i-1))))), y, TOCOL(x), m, ROWS(y),
IF(AND(n=1,m=1), x, LET(cnts, SEQUENCE(COMBINA(m,n)-1),
idx, REDUCE(SEQUENCE(,n,1,0), cnts, LAMBDA(ac,i, VSTACK(ac,
NEXT_SET(NEXT_SET, IF(i=1, ac, TAKE(ac,-1)),m,n,n)))),INDEX(y, idx))))),
COMBINA_SET(A,n))