http://www.perlmonks.org?node_id=1058714

calebcall has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to compare a string, and for some reason it will always match the first stanza even though it really doesn't. Here's is my compare:

if ($genres[0] eq 'Science Fiction' || 'Sci-Fi' || 'Fantasy') { $genre = 'Sci-Fi & Fantasy'; }elsif ($genres[0] eq 'Action' || 'Adventure' || 'War') { $genre = 'Action & Adventure'; }elsif ($genres[0] eq 'Kids' || 'Family') { $genre = 'Kids & Family'; }else { $genre = $genres[0]; }
Some debug output is:
print "Genres: $genres[0]\n"; print "Genre: $genre\n";
The output looks like:
Genres: Action Genre: Sci-Fi & Fantasy

If I move say Kids & Family to the first block, it always ends up being Kids & Family. Any idea what I'm doing wrong that would cause it to always match that first block?

Replies are listed 'Best First'.
Re: Trying to compare a string...
by choroba (Cardinal) on Oct 18, 2013 at 08:10 UTC
    That's not how || works. The general pattern is
    condition || condition || condition

    So, you have to repeat the whole condition each time:

    if ($genres[0] eq 'Science Fiction' || $genres[0] eq 'Sci-Fi' || or $g +enres[0] eq 'Fantasy') {

    If you think it is a bit verbose, switch to some other way of checking the values:

    # Either if ($genres[0] =~ /^ (?: Science\ Fiction | Sci-Fi | Fantasy ) $/x) { # or if (grep $genres[0] eq $_, 'Science Fiction', 'Sci-Fi', 'Fantasy') {

    etc.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Trying to compare a string...
by moritz (Cardinal) on Oct 18, 2013 at 08:26 UTC

    In addition to choroba's excellent answer, I'd like to point out that you can replace code with data like this:

    my %genre_map = ( 'Science Fiction' => 'Sci-Fi & Fantasy', 'Sci-FI' => 'Sci-Fi & Fantasy', 'Fantasy' => 'Sci-Fi & Fantasy', 'Action' => 'Action & Adventure', ... ); my $genre = $genre_map{$genres[0]} // $genres[0];

    Factoring the code like this would even allow you to put the genre mapping into a configuration file, and load the hash from it.

    Finally, Perl 6 has a feature called junctions which does allow you to write code similar to what you tried:

    use v6; my $genre; my @genres = 'Kids', 'Ignored'; if @genres[0] eq 'Science Fiction' | 'Sci-Fi' | 'Fantasy' { $genre = 'Sci-Fi & Fantasy'; } elsif @genres[0] eq 'Action' | 'Adventure' | 'War' { $genre = 'Action & Adventure'; } elsif @genres[0] eq 'Kids' | 'Family' { $genre = 'Kids & Family'; } else { $genre = @genres[0]; } say $genre;
Re: Trying to compare a string...
by kcott (Archbishop) on Oct 18, 2013 at 08:25 UTC

    G'day calebcall,

    A non-empty string (except '0') will always be TRUE in a boolean context. So, 'Sci-Fi' will always be TRUE. Therefore, even if $genres[0] eq 'Science Fiction' was FALSE, $genres[0] eq 'Science Fiction' || 'Sci-Fi' would always be TRUE, and (with the short-circuiting nature of '||') || 'Fantasy' will never be tested.

    The same will be true for those other conditions. You might want to consider something like this (untested):

    my $genre = $genres[0]; for ($genres[0]) { /^(?:Science Fiction|Sci-Fi|Fantasy)$/ && $genre = 'Sci-Fi & Fanta +sy'; /^(?:Action|Adventure|War)$/ && $genre = 'Action & Adventure'; /^(?:Kids|Family)$/ && $genre = 'Kids & Family'; }

    -- Ken

Re: Trying to compare a string...
by hdb (Monsignor) on Oct 18, 2013 at 08:49 UTC

    If you combine kcott's and moritz's answers, you could put the regexes into a hash and then test against that.

    use strict; use warnings; my %genre_table = ( 'Sci-Fi & Fantasy' => qr/Science Fiction|Sci-Fi|Fantasy/, 'Action & Adventure' => qr/Action|Adventure|War/, 'Kids & Family' => qr/Kids|Family/, ); my @genres = ( 'Adventure' ); my( $genre ) = ( ( grep { $genres[0] =~ $genre_table{$_} } keys %genre +_table ), $genres[0]); print $genre;
Re: Trying to compare a string...
by tobyink (Canon) on Oct 18, 2013 at 10:43 UTC

    There's already been some good answers, but just in the interests of pimping...

    use v5.14; use Switcheroo; my @genres = ('War', 'Death'); my $genre = switch ($genres[0]) do { case "Science Fiction", "Sci-Fi", "Fantasy": "Sci-Fi & Fantasy"; case "Action", "Adventure", "War": "Action & Adventure"; case "Kids", "Family": "Kids & Family"; default: $_; }; say $genre; # Action & Adventure
    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name