Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

using grep to match more than two words

by sroy5 (Initiate)
on Jun 12, 2007 at 05:52 UTC ( [id://620634]=perlquestion: print w/replies, xml ) Need Help??

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

I have an array as @arr1 = ("Sun" "Moon" "Venus" "Pluto" "Neptune");

Now I need to find out with the help of grep if the array has got "Sun" and "Moon". If found both are present in the array then return true otherwise if does not found both of them present in the array then return false.

I am actually looking for the above queries solution in single statement.

Thanks in advance.. SR

Edit: g0n - formatting tags & removed pre tags

Replies are listed 'Best First'.
Re: using grep to match more than two words
by McDarren (Abbot) on Jun 12, 2007 at 06:37 UTC
    Firstly, @arr1 = ("Sun" "Moon" "Venus" "Pluto" "Neptune"); is wrong, and will throw an error if you try use it. Your either need to do:
    @arr1 = qw(Sun Moon Venus Pluto Neptune);
    or..
    @arr1 = ("Sun", "Moon", "Venus", "Pluto", "Neptune");
    Secondly, to answer your question - here is one way to do it:
    if ((grep {$_ eq 'Sun'} @arr1) && (grep {$_ eq 'Moon'} @arr1)) { print "We have both a sun and a moon - yay!\n"; } else { print "Something is missing...\n"; }
    --Darren
Re: using grep to match more than two words
by blazar (Canon) on Jun 12, 2007 at 08:51 UTC
    Now I need to find out with the help of grep if the array

    [snip]

    I am actually looking for the above queries solution in single statement.

    A few considerations:

    • please stop friggin' putting your posts in pre tags: they're discoraged at the Monastery and certainly not useful here to present some simple text;
    • nobody seems to have noticed yet, but why do you insist that you "need" to use grep and that you want a single statement? This strongly smells like homework!
    • Thanks to stuff like do, quite about anything in Perl can be a "single statement".

    To illustrate the last point, the following does use grep and is a single statement:

    print "ok\n" if 2 == grep sub { my $x=shift; $_ eq $x and return 1 for @arr1; 0; }->($_), qw/Sun Moon/;
Re: using grep to match more than two words
by naikonta (Curate) on Jun 12, 2007 at 07:12 UTC
    Well, I feel bit ithcyitchy, so here's a one (probably generic) way to do it.
    use strict; use warnings; my @source = qw(Sun Moon Pluto Venus Neptune); my @target = @ARGV ? @ARGV : qw(Sun Moon); print is_target_in_source(\@target, \@source), "\n"; sub is_target_in_source { my($target, $source) = @_; # omits arg validation my %target = map { $_ => 1 } @$target; my $in = 0; $target{$_} && $in++ for @$source; return $in == @$target ? 'Yes' : 'No'; }
    One can also play with one or more functions in List::MoreUtils.

    But I found Array::Utils simpler to use:

    use strict; use warnings; use Array::Utils 'intersect'; my @source = qw(Sun Moon Pluto Venus Neptune); my @target = @ARGV ? @ARGV : qw(Sun Moon); my @in = intersect @target, @source; print @in == @target ? 'Yes' : 'No';
    Update: See also this FAQ entry

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Re: using grep to match more than two words
by johngg (Canon) on Jun 12, 2007 at 09:23 UTC
    Another way to do it would be to use regex look-aheads in alternation (Sun followed by Moon or Moon followed bu Sun) and do a match against the array interpolated into a string.

    use strict; use warnings; my @arrays = ( [ qw{Sun Moon Venus Pluto Neptune} ], [ qw{Jupiter Mercury Moon Io} ], [ qw{Europa Moon Ganymede Sun Earth} ], [ qw{Venus Sun Saturn Titan} ], ); my $rxSunAndMoon = qr {(?x) (?:\bSun\b(?=.*\bMoon\b)) | (?:\bMoon\b(?=.*\bSun\b)) }; foreach my $raRocks ( @arrays ) { print qq{Array: @$raRocks\n}; print qq{@$raRocks} =~ $rxSunAndMoon ? qq{ Found Sun and Moon\n} : qq{ No match\n}; }

    The output is

    Array: Sun Moon Venus Pluto Neptune Found Sun and Moon Array: Jupiter Mercury Moon Io No match Array: Europa Moon Ganymede Sun Earth Found Sun and Moon Array: Venus Sun Saturn Titan No match

    I hope this is of interest.

    Cheers,

    JohnGG

Re: using grep to match more than two words
by dewey (Pilgrim) on Jun 12, 2007 at 06:24 UTC
    This worked for me:
    + #!/usr/bin/perl + + use strict; use warnings; my @arr1 = qw(Sun Moon Venus Pluto Neptune); my @arr2 = qw(Son Moon Venus Pluto Neptune); print "\@arr1 has both" if grep {/^Sun$/} @arr1 and grep {/^Moon$/} @arr1; print "\@arr2 has both" if grep {/^Sun$/} @arr2 and grep {/^Moon$/} @arr2;
    But after some consideration, I feel better about this slightly different phrasing:
    print "\@arr1 has both" if grep {$_ eq "Sun"} @arr1 and grep {$_ eq "Moon"} @arr1;
    I don't know if I should really be concerned about the regular expression's interpretation of $, but in any case since we seem to want exact equality we might as well write  eq. I've opted not to use the EXPR, LIST form of grep to avoid awkward parentheses, but there might be a better way around that :)

    ~dewey
Re: using grep to match more than two words
by Samy_rio (Vicar) on Jun 12, 2007 at 06:25 UTC
      ((grep/^(Sun|Moon)$/, @arr1) == 2) ? (print "True") : (print "False");

      This is not reliable if @arr1 is allowed to contain duplicates, it is deemed to give false positives and false negatives, and the OP wrote nothing about that possibility.

        Oh Dang! I was going to post which would had counted the number of matches (did not post as similar method had already been shown) but I completely missed the possibility of duplicates. Thanks much.

Re: using grep to match more than two words
by pgor (Beadle) on Jun 12, 2007 at 15:54 UTC

    My solution is almost like naikonta's, except I'm removing from the list of required entries instead of just counting matches. This allows you to terminate early once you've found everything required.

    sub containsAll { my ($arrEverything, $arrRequire) = @_; my %stillRequired = map { $_ => 1 } @$arrRequire; for my $entry ( @$arrEverything ) { if ( delete($stillRequired{$entry}) ) { last unless %stillRequired; } } return %stillRequired ? 0 : 1; }

    Call as containsAll(\@arr1,['Sun','Moon'])

    Feel free to take the concept and make it into your single statement requirement, since I don't see how anything maintainable can come of that kind of requirement.

    pg
Re: using grep to match more than two words
by johngg (Canon) on Jun 12, 2007 at 22:02 UTC
    A subroutine inspired by the responses from naikonta and pgor but greping using exists against an on-the-fly hash constructed from the elements of @$raAll.

    sub isTargetInAll { my ($raAll, $raTarget) = @_; return @$raTarget == grep { exists { map { $_ => 1 } @$raAll }->{$_} } @$raTarget; }

    I think that should be reliable as long as @$raTarget doesn't contain duplicates; if you were looking for a Sun and two Moons you would get a false positive if @$raAll had a Sun and just one Moon.

    Cheers,

    JohnGG

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://620634]
Approved by Samy_rio
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (6)
As of 2024-04-18 18:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found