This version uses a fairly simple recursion:
const without = (n) => (xs) =>
[... xs .slice (0, n), ... xs .slice (n + 1)]
const permutations = (xs) =>
xs .length == 0
? []
: xs .length == 1
? [[xs[0]]]
: // else
xs .flatMap ((x, i) => permutations (without (i) (xs)) .map (p => [x, ...p]))
const stringPermutations = (s) => {
return permutations (s .split ('')) .map (ss => ss .join (''))
}
console .log (
stringPermutations ('abcd')
)
.as-console-wrapper {min-height: 100% !important; top: 0}
There is a helper function, without
, which returns a copy of an array without the given index. For instance, without (2) (['a', 'b', 'c', 'd', 'e', 'f'])
yields ['a', 'b', 'd', 'e', 'f']
. This is only used once inside our main function and could be easily inlined, but I find it easier to read as is.
stringPermutations
merely changes the string into an array of single-character strings, calls permutations
and then joins the resulting arrays back into strings.
The important part is permutations
. It has two base cases: when the input array is empty, it returns an empty array. When it has only a single value, it returns an array containing an array containing that value. In all other cases, it selects each element in turn for the first element, removing it from the list and recursively calling permutations
with the remaining list for the subsequent positions.