Assumptions:
- the string
A,B,C is considered to be equivalent to A,C,B, B,A,C, B,C,A, C,A,B and C,B,A (ie, we only need to generate one of these 6 combinations)
One idea for generating a list of combinations ...
- we'll load lines into an array
- for each item in the array we'll start a new set of output strings
- make recursive calls to append the next array item to our output string
- as we're appending array items to the output we'll go ahead and print each string that consists of 2 or more array items
- this is more of a tail recursion method which should eliminate the generation of duplicates (
A,B,C vs the other 5 equivalent patterns) and/or or the need to rollback "already seen' combos
One awk idea for implementing this logic:
awk '
# input params are current output string, current arr[] index, and current output length (ie, number of fields)
# parameter "i" will be treated as a local variable
function combo(output, j, combo_length, i) {
if ( combo_length >= 2) # print any combination with a length >= 2
print output
for (i=j+1; i<=n; i++) # loop through "rest" of array entries for next field in output
combo(output "," arr[i], i, combo_length+1 )
}
{ arr[NR]=$1 } # load fields into array "arr[]"
END { n=length(arr)
for (i=1; i<=n; i++) # for each arr[i] start a new set of combos starting with arr[i]
combo(arr[i],i,1)
}
' test.txt
This generates:
A,B
A,B,C
A,B,C,D
A,B,D
A,C
A,C,D
A,D
B,C
B,C,D
B,D
C,D
If we want to sort based on number of fields and then the output string we can make the following change:
- change
print output to print combo_length, output and then ...
- pipe the
awk output through sort | cut (we'll borrow glenn's code here)
This generates:
$ awk ' ... print combo_length,output ...' test.txt | sort -k1,1nr -k2 | cut -d" " -f2-
A,B,C,D
A,B,C
A,B,D
A,C,D
B,C,D
A,B
A,C
A,D
B,C
B,D
C,D
For a 20-line test.txt ( letters A to T) with the output dumped to file test.out:
$ time awk '...' test.txt > test.out
real 0m1.420s
user 0m1.279s
sys 0m0.139s
$ wc -l test.out
1048555 2097110 23685256 test.out
$ time awk '...' test.txt | sort ... | cut ... > test.out
real 0m3.456s
user 0m3.493s
sys 0m0.185s
$ wc test.out
1048555 1048555 20971480 test.out