This is an update regarding the bounty by @Arcaeca who asked for 3 things:
1- The line .map(s => Array.from(s, c => tCategories[c] || [c]))
does not replace a key of tCategories with its corresponding value when key.length > 1.
2- Passing an input string with an empty subpattern (i.e. substring delimited by ","), e.g. "aT,Ps,eNe,,NP"
, causes the function to throw: TypeError
.
3- It's a new feature, I tried to add was the ability to define "nonce" categories on the spot by enclosing them in square brackets [ ], e.g. the input string "a[Ps,T]"
should yield the same output as "aPs,aT"
My Answer (From @Nina Scholz Answer)
I will start with the third requirement since it's completely new, so to make it easy I will make another function to parse the given string and check if it has square brackets multiplication, then resolve it, e.g. the input "a[Ps,T]"
, the output would be "aPs,aT"
e.g the input "a[T, X]d"
, the output would be "aTd, aXd"
I will call this clean()
.
You can enhance this function as you want.
const clean = string => {
while (true) {
const patterns = [
/(\w+)\[([\w+\,]*)\](\w+)*/,
/(\w+)*\[([\w+\,]*)\](\w+)/
]
let match = null
for (const i in patterns) {
match = patterns[i].exec(string)
if (match) {
break;
}
}
if (!match) {
break
}
const newString = [match[1] ? [match[1]] : [''], match[2].split(',').map(v => v.replace(',', '')), match[3] ? [match[3]] : ['']].reduce(cartesian).map(a => a.join('')).join(',')
string = string.replace(match[0], newString)
}
return string
};
Backing to the first two requirements, I made this modification
const foo = string => Object.keys(tCategories)
.reduce((a, b) => a.replaceAll(b, `?${b}?`), string)
.split(',')
.map(v => v.split('?').map(t => tCategories[t] || [[t]]))
.map(a => a.reduce(cartesian).map(a => a.join('')))
.flat()
What I did is, I went through each key of tCategories then checked if my string contains that key, if yes then put a placeholder around it to make it easy to identify it, in my example, I chose ?
, and got rid of Array.from
method. now our function supports keys whose length > 1, and also empty subpatterns.
Full Example
let tCategories = { T: ["t", "d", "th"], P: ["p", "t", "k", "q"], N: ["m", "n"], KK: ['a', 'b'] };
const cartesian = (a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []);
const clean = string => {
while (true) {
const patterns = [
/(\w+)\[([\w+\,]*)\](\w+)*/,
/(\w+)*\[([\w+\,]*)\](\w+)/
]
let match = null
for (const i in patterns) {
match = patterns[i].exec(string)
if (match) {
break;
}
}
if (!match) {
break
}
const newString = [match[1] ? [match[1]] : [''], match[2].split(',').map(v => v.replace(',', '')), match[3] ? [match[3]] : ['']].reduce(cartesian).map(a => a.join('')).join(',')
string = string.replace(match[0], newString)
}
return string
};
const foo = string => Object.keys(tCategories)
.reduce((a, b) => a.replaceAll(b, `?${b}?`), string)
.split(',')
.map(v => v.split('?').map(t => tCategories[t] || [[t]]))
.map(a => a.reduce(cartesian).map(a => a.join('')))
.flat()
console.log(...foo(clean('aT,Ps,eNe,NP,,KK[z,c,f]')))