How to perform iteration over excel/google sheets cells to get pairwise combinations?
\"string1\"
\"string2\"
\"string3\"
...
\"string10\"
=QUERY(ARRAYFORMULA(SPLIT(
TRANSPOSE(SPLIT(REPT(CONCATENATE(A2:A&CHAR(9)), COUNTA(A2:A)), CHAR(9)))& " "&
TRANSPOSE(SPLIT(CONCATENATE(REPT(A2:A&CHAR(9), COUNTA(A2:A))), CHAR(9))), " ")),
"where Col1<>Col2 order by Col1", 0)
It is a hard task for native functions. Try a script and use it as a custom function:
function getTournament(teams_from_range)
{
// teams_from_range -- 2D Array
var teams = [];
// convert to list
teams_from_range.forEach(function(row) { row.forEach(function(cell) { teams.push(cell); } ); } );
return getTournament_(teams);
}
function getTournament_(teams)
{
var start = 0;
var l = teams.length;
var result = [], game = [];
// loop each value
for (var i = 0; i < l; i++)
{
// loop each value minus current
start++;
for (var ii = start; ii < l; ii++)
{
game = []
game.push(teams[i]);
game.push(teams[ii]);
result.push(game);
}
}
return result;
}
Usage:
=getTournament(A1:A10)
I have to agree with @Max that it is difficult with native functions, or at least long-winded, but it is possible in Google Sheets
=ArrayFormula(query({if((row(A:A)<=counta(A:A)^2)*(int((row(A:A)-1)/counta(A:A))<mod((row(A:A)-1),counta(A:A))),
vlookup(int((row(A:A)-1)/counta(A:A)),{row(A:A)-1,A:A},2)&vlookup(mod((row(A:A)-1),counta(A:A)),{row(A:A)-1,A:A},2),"")},"select Col1 where Col1<>''"))
Note 1 - method
Using a list of 10 strings as an example.
(1) Add a column to number the strings from 0 to 9 using
{row(A:A)-1,A:A}
(2) Use the row number in a VLOOKUP to get the first string of the pair with
vlookup(int((row(A:A)-1)/counta(A:A)),{row(A:A)-1,A:A},2)
Row number-1 int((row(A:A)-1)/counta(A:A)) String
0 0 String1
1 0 String1
...
9 0 String1
10 1 String2
...
20 2 String3
...
99 9 String10
(3) Use the row number in a VLOOKUP to get the second string of the pair with
vlookup(mod((row(A:A)-1),counta(A:A)),{row(A:A)-1,A:A},2)
Row number-1 mod((row(A:A)-1),counta(A:A)) String
0 0 String1
1 1 String2
2 2 String3
...
9 9 String10
10 0 String1
11 1 String2
...
99 9 String10
Note that the list will include unwanted pairs like String1String1 and String2String1.
(4) Set unwanted pairs to "" with if condition
if((row(A:A)<=counta(A:A)^2)*(int((row(A:A)-1)/counta(A:A))
Note 1 Using a filter to remove unwanted pairs as suggested by @Max Makhrov would be shorter.
(5) Use Query to remove blank rows.
Note 2 - limitation on number of rows
Because redundant pairs are generated then removed, this method requires N^2 rows to be in the sheet where N is the number of strings rather than N*(N-1)/2 which is the number of distinct pairs of N objects. Conversely, the maximum number of strings s which can be processed this way for a sheet with N rows is floor(sqrt(N)), e.g. for a sheet with 1,000 rows s=floor(sqrt(1000))=31.
Note 3 - a possible way to avoid generating redundant pairs
One way of visualising what I have tried to do is as follows, where the array elements represent output rows (A:A) and the row and column headers indicate corresponding values which are used as lookups to get pairs like (string 1, string 1), (string 1 string 2) etc.
It is fairly easy to do the mapping from output rows to lookup values using integer division and the MOD function as above.
What we would really like to do is to get non-redundant pairs like this
but then how would you map from output rows 1-10 to pairs of lookup values 1-5 ?
I hope to show that this is possible with a bit of maths providing (at least in principle) a way to get the N(N-1)/2 non-redundant pairs straight away without first generating all N^2 pairs.
The count S of cells in rows 1 to r of the upper triangular part above is the total count N(N-1)/2 minus the count in the rows below it (N-r)(N-r-1)/2
This can be re-arranged as follows
This is a quadratic in r so we can solve it using the regular formula
to give
So the row is given by the ceiling of the above formula for r.
The number (say T) at the end of row r is given by substituting the ceiling of r back into the second equation above
and finally the column corresponding to S is given by
Now define a named range N whose value is
=counta(A:A)
and a named range M whose value is
=2*N-1
Then finally the formula you need to select stringA (the row r of the matrix) is
=iferror(ArrayFormula(vlookup(ceiling((M-sqrt(M^2-8*row(A:A)))/2,1),{row(A:A),A:A},2)),"")
and the formula you need to select stringB (the column c of the matrix) is
=iferror(ArrayFormula(vlookup(N+row(A:A)-(M*CEILING((M-SQRT(M^2-8*row(A:A)))/2,1)-CEILING((M-SQRT(M^2-8*row(A:A)))/2,1)^2)/2,{row(A:A),A:A},2)),"")
where columns D and E are just included for testing purposes.
Then it only remains to combine the two formulas into one column if desired.
=ARRAYFORMULA(SPLIT(SORT(
TRANSPOSE(SPLIT(CONCATENATE(REPT(UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),",")))&","&
TRANSPOSE(UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),",")))), (
UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),",")))<=
TRANSPOSE(UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),",")))))*
REGEXMATCH(CONCATENATE(","&SUBSTITUTE(TEXTJOIN(",",1,A2:A),",",",,")&","&CHAR(9)),"(,"&
UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),",")))&",[^\t]*,"&
TRANSPOSE(UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),","))))&",)|(,"&
TRANSPOSE(UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),","))))&",[^\t]*,"&
UNIQUE(TRANSPOSE(SPLIT(JOIN(",",TEXTJOIN(",",1,A2:A)),",")))&",)"))&CHAR(9)),CHAR(9)))),","))