I'm working on a project that does some string parsing (for a song lyrics format) and I ran across a usage of the .replace()
method that feels a bit hacky to me... but it does work.
This is how it currently works right now, and it logs out the expected result.
const scan = (str, pattern) => {
const results = [];
str.replace(pattern, function() {
results.push(Array.prototype.slice.call(arguments, 1, -2));
});
return _.flatten(results)
}
console.log("Lyrics", scan(
"I don't see! a bad, moon a-rising. (a-rising)",
/(\[[\w#b/]+])?([\w\s',.!()_\-"]*)/gi
));
console.log("Chords", scan(
"[D] [D/F#] [C] [A7]",
/\[([\w#b+/]+)]?/gi
));
console.log("Measures", scan(
`
# Instrumental
| [A] [B] | [C] | [D] [E] [F] [G] |
`,
/([[\w#b/\]+\]\s]+)[|]*/gi
));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
The usage of .replace()
feels wrong since it's not actually doing any replacing but relying on the looping behavior to push items into an array. That returns an array with a single item, of an array of strings. Lodash is then used to flatten it out into a single string array.
I feel like this could be improved to use .matchAll()
instead, but I haven't been able to get it quite right just yet. Here's what I have so far, which works but the output is wrong since it's not identical to the above example.
const scan = (str, pattern) => {
return [...str.matchAll(pattern)].map(r=>{
return r[1]
});
}
console.log("Lyrics", scan(
"I don't see! a bad, moon a-rising. (a-rising)",
/(\[[\w#b/]+])?([\w\s',.!()_\-"]*)/gi
));
console.log("Chords", scan(
"[D] [D/F#] [C] [A7]",
/\[([\w#b+/]+)]?/gi
));
console.log("Measures", scan(
`
# Instrumental
| [A] [B] | [C] | [D] [E] [F] [G] |
`,
/([[\w#b/\]+\]\s]+)[|]*/gi
));
Is .matchAll()
the correct alternative to .replace()
in this case? Is there a better way to get the desired output instead of what I am currently doing? I know I'm currently not calling the .slice()
method that the first example has yet because I'm not entirely sure I understand what this is trying to accomplish here since this code was not originally written by me.
You can use flatMap
and slice
.
const pattern = /(\[[\w#b/]+])?([\w\s',.!()_\-"]*)/gi;
const str = "I don't see! a bad, moon a-rising. (a-rising)";
const results = [...str.matchAll(pattern)].flatMap(m => m.slice(1));
console.log('results', results)