Можно применить рекурсию:
const combinations = (
str,
{
charset = 'abcdefghijklmnopqrstuvwxyz',
placeholder = '&',
} = {}
) =>
str.includes(placeholder)
? [].concat(...Array.from(
charset,
n => combinations(str.replace(placeholder, n), { charset, placeholder })
))
: [ str ];
console.log(combinations('ih&v&'));
А можно не применять:
function combinations(
[...str],
{
charset = Array.from({ length: 26 }, (_, i) => String.fromCharCode(i + 97)),
placeholder = '&',
} = {}
) {
const numReplacements = str.filter(n => n === placeholder).length;
return Array.from(
{ length: charset.length ** numReplacements },
(_, i) => {
let j = numReplacements;
return str
.map(n => n === placeholder
? charset[(i / (charset.length ** --j) | 0) % charset.length]
: n)
.join('');
}
);
}
console.log(combinations('#**', { charset: '0123456789ABCDEF', placeholder: '*' }));