Рекурсия есть:
const combinations = (data, size) => size > 1
? Array.from(data, n => combinations(data, ~-size).map(m => [ n, ...m ])).flat()
: Array.from(data, n => [ n ]);
Рекурсии нет:
const combinations = (data, size) =>
Array.from({ length: data.length ** size }, (_, i) =>
Array.from({ length: size }, (_, j) =>
data[(i / (data.length ** (size - j - 1)) | 0) % data.length]
)
);
Применяем:
// ваш случай
combinations('abcde', 2)
// а вот, например, числа в интервале [0, 9999], представленные в виде массивов цифр
combinations([...Array(10).keys()], 4)
// или, интервал [0, 31], но в двоичном виде
combinations([ 0, 1 ], 5)
UPD. Вынесено из комментариев:
Выводит также a,a b,b c,c ... Мне это лишнее
Окей, вот элементы не повторяются:
const partialPermutation = (data, size) =>
data.length < size
? null
: (function get(permutation, result) {
if (permutation.size === size) {
result.push([...permutation]);
} else {
for (const n of data) if (!permutation.has(n)) {
permutation.add(n);
get(permutation, result);
permutation.delete(n);
}
}
return result;
})(new Set, []);