I have an array like this
my @stopWords = ("and","this",....)
My text is in this variable
my $wholeText = "....and so this is...."
I want to match every occurrence of every element of my stopWords array in the scalar wholeText and replace it with spaces.
One way of doing this is as follows :
foreach my $stopW (@stopWords)
{
$wholeText =~ s/$stopW/ /;
}
This works and replaces every occurrence of all the stop words. I was just wondering, if there is a shorter way of doing it.
Like this:
$wholeText =~ s/@stopWords/ /;
The above does not seem to work though.
The Perl grep() function is a filter that runs a regular expression on each element of an array and returns only the elements that evaluate as true. Using regular expressions can be extremely powerful and complex. The grep() functions uses the syntax @List = grep(Expression, @array).
Substitution Operator or 's' operator in Perl is used to substitute a text of the string with some pattern specified by the user.
The grep equivalent would be $idx = grep { $array[$_] eq 'whatever' and last } 0 ..
Note: In Perl arrays, the size of an array is always equal to (maximum_index + 1) i.e. And you can find the maximum index of array by using $#array. So @array and scalar @array is always used to find the size of an array.
While the various map
/for
-based solutions will work, they'll also do regex processing of your string separately for each and every stopword. While this is no big deal in the example given, it can cause major performance issues as the target text and stopword list grow.
Jonathan Leffler and Robert P are on the right track with their suggestions of mashing all the stopwords together into a single regex, but a simple join
of all the stopwords into a single alternation is a crude approach and, again, becomes inefficient if the stopword list is long.
Enter Regexp::Assemble, which will build you a much 'smarter' regex to handle all the matches at once - I've used it to good effect with lists of up to 1700 or so words to be checked against:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use Regexp::Assemble;
my @stopwords = qw( and the this that a an in to );
my $whole_text = <<EOT;
Fourscore and seven years ago our fathers brought forth
on this continent a new nation, conceived in liberty, and
dedicated to the proposition that all men are created equal.
EOT
my $ra = Regexp::Assemble->new(anchor_word_begin => 1, anchor_word_end => 1);
$ra->add(@stopwords);
say $ra->as_string;
say '---';
my $re = $ra->re;
$whole_text =~ s/$re//g;
say $whole_text;
Which outputs:
\b(?:t(?:h(?:at|is|e)|o)|a(?:nd?)?|in)\b
---
Fourscore seven years ago our fathers brought forth
on continent new nation, conceived liberty,
dedicated proposition all men are created equal.
My best solution:
$wholeText =~ s/$_//g for @stopWords;
You might want to sharpen the regexp using some \b
and whitespace.
What about:
my $qrstring = '\b(' . (join '|', @stopWords) . ')\b';
my $qr = qr/$qrstring/;
$wholeText =~ s/$qr/ /g;
Concatenate all the words to form '\b(and|the|it|...)\b
'; the parentheses around the join are necessary to give it a list context; without them, you end up with the count of the number of words). The '\b
' metacharacters mark word boundaries, and therefore prevent you changing 'thousand' into 'thous'. Convert that into a quoted regular expression; apply it globally to your subject string (so that all occurrences of all stop words are removed in a single operation).
You can also do without the variable '$qr
':
my $qrstring = '\b(' . (join '|', @stopWords) . ')\b';
$wholeText =~ s/$qrstring/ /g;
I don't think I'd care to maintain the code of anyone who managed to do without the variable '$qrstring
'; it probably can be done, but I don't think it would be very readable.
My paranoid version:
$wholeText =~ s/\b\Q$_\E\b/ /gi for @stopWords;
Use \b
to match word boundaries, and \Q..\E
just in case any of your stopwords contains characters which may be interpreted as "special" by the regex engine.
You could consider using a regex join to create a single regex.
my $regex_str = join '|', map { quotemeta } @stopwords;
$string =~ /$regex_str/ /g;
Note that the quotemeta
part just makes sure that any regex characters are properly escaped.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With