All answers so far are using a traditional imperative style but perhaps what you want is to move to functional style:
let statement = 'Some text with [square brackets] inside [several] times'
let tags = statement.match(/[^[\]]+(?=])/g)
?.map(t => t.toLowerCase())
.map(t => t.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g, '_'))
?? []
console.log(tags)
The ?
operator makes sure map
only runs when not null
or undefinied
. If nothing matches the ?
will return undefinied
, short circuiting the map
functions.
The map
transforms the list into something else, so to lower case and then replace symbols with underscore.
The ??
transformsundefinied
into something else. Note that ?
will return undefinied
when nothing matches. When nothing matches I want an empty array instead of undefinied
, just because it makes more sense.
You may wonder what's the point of using functional style. In this particular case it's more readable and less error prone.
Functional style is declarative. Instead of saying what the computer must do, you say what it should do. For example, you don't have to know how map
is implemented, only that it transform a list into another list. It doesn't matter if it's implemented with a while loop, a for loop, or even if it's splitting the array and running each part in parallel; the result of map
is always the same, given the same input. This gives opportunity for optimization in future engine updates (e.g., execute map
in parallel) and results in shorter, less error prone code. In imperative style not only there's no opportunity for any optimization, but the developer may also have to know unimportant details, like how an array index starts (e.g., zero or one) or what state must be changed in each loop iteration (e.g., tags must be changed); the reader must pay more attention to detail and it's more error prone for the developer.