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

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

I had reason to want to know if two arrays contained the same values, in the same order. Okay, -vv me...I had an appointment I was worried about missing, but i really had to lie down on the couch in my office and get some zzz's. (I did deserve them, honest.) So i wrote a quick script to run xmms at a given time, and set the mixer volume up all the way.

To accomplish this, I took the command line argument and split it on ':', assuming military time. That goes into the @wake array. The current time hour and minute go into @current. Now I want to know, for my while loop, whether the two arrays are the same.

Doing (@current == @array) treats the arrays as scalars, and is true, since both arrays have the same number of members. Doing (($current[0], $current[1]) == ($wake[0], $wake[1])) also didn't work, though I didn't know it until the minutes were equal and the hours were not...apparently, it only compares the last element. I tried ([@current] == [@wake]), using anonymous arrays (which I scarsely understand), and it didn't work. In short, the only thing that worked was an element by element comparison. Not very elegant (especially if the two arrays have more than two elements!)

So I'm sure this is one of those silly newby questions that anyone with ten minutes more experience than me will be able to answer quickly. What is the right way to compare two arrays?

On a side note, the code worked, I slept through all of the Chess soundtrack at full volume, and woke up hours late :(

Want to see the code for the alarm clock anyway, despite the fact it failed to wake me?

#!/usr/bin/perl use strict; use POSIX; unless (!@ARGV || ($ARGV[0] =~ /:/)) { print "Usage: $0 [hour:min]\nThis program will start XMMS with the c +urrent playlist at high volume at a given time. If hour: min is not given, the alarm will go off almost immediately.\n"; exit; } my @wake = split /:/, $ARGV[0]; my @current; if (@ARGV) { print "Alarm is set for $wake[0]:$wake[1].\n"; do { my @timenow = localtime(time); @current = ($timenow[2], $timenow[1]); sleep 5; } while (($wake[0] != $current[0]) || ($wake[1] != $current[1])); } if (my $pid = fork) { # parent print "It's $wake[0]:$wake[1] now.\n"; my $mixpid; unless ($mixpid = fork) { exec "mixer vol 100:100"; } waitpid($mixpid, 0); waitpid($pid, 0); } else { # child exec "xmms -p"; } my $mixpid; unless ($mixpid = fork) { exec "mixer vol 75:75"; } waitpid($mixpid, 0);

Replies are listed 'Best First'.
Re: How to compare arrays? (xmms alarm clock)
by chromatic (Archbishop) on Jun 29, 2001 at 07:19 UTC
    How about:
    if ("@current" eq "@wake") { # do something }
    Yeah, that's cheating. Yeah, it's ugly. Yeah, it works.

    Update: Per tadman's suggestion, one might also do something like and @current == @wake as an additional comparison.

      This works so long as both arrays do not contain the $" character (usually space). Otherwise, two lists might "match" even when they don't:
      @a = ('the flying','dorito','brothers'); @b = ('the','flying','dorito brothers'); "@a" = "the flying dorito brothers" "@b" = "the flying dorito brothers"
      So if you're sure that the two arrays are $"-free in that sense, then go ahead.
      This only works because the arrays don't contain a string with $" as substring.

      However, if you have

      @current = ("foo bar", "baz"); @wake = ("foo", "bar baz");
      then both "@current" eq "@wake" and @current == @wake.

      Also, it's not clear what to do when the arrays contain references. Should the references be the same, or is it enough if they point to comparable data? That is, are the following arrays equal?

      @array1 = (1, [2]); @array2 = (1, [2]);

      -- Abigail

      my mistake. i tried to implement your idea, and did it wrong. i thought it should have worked and didn't try hard enough. upon further effort it worked exactly as advertised. it even looks clean and understandable. i just wish it were more intuitive to begin with...

      revised...

      do { my @timenow = localtime(time); @current = ($timenow[2], $timenow[1]); sleep 5; } until ("@wake" eq "@current");

      so can i be greedy and ask for more ways to compare the two?

        See How to test equality of hashes? for more ways.

        You can leave out the @timenow:

        @current = (localtime time)[1..2];
        ...just gives you the slice.

        Hope this helps,

        Jeroen

        Update: You're welcome. That prob is easily solved with reverse:

        @current = reverse (localtime time)[1..2];
        How about something like...
        until ((grep {$wake[$_] == $current[$_]} (0..$#wake)) == @wake));

        (NB can't check this right now though)

        andy.

      for some reason, that is always true. i haven't thought through why yet. perhaps the array name is being interpreted as a scalar inside of a string?

      i also considered using sprintf on each side of the 'eq', which should definitely work. of course, thats cheating and ugly and inelegant too.

      i suppose i could pack both arrays, and compare the results. still not pretty. there must be something better...

Re: How to compare arrays? (xmms alarm clock)
by MZSanford (Curate) on Jun 29, 2001 at 14:54 UTC
    In the spirit of "keep it simple stupid", i would probably suggest the following. Ignore that these are times and think of each of them seperatly. This is untested, but i don't see why it would not work in a simple fasion.
    if (($current[0] == $wake[0]) && ($current[1] == $wake[1])) { # times match }

    may the foo be with you
Re: How to compare arrays? (xmms alarm clock)
by John M. Dlugosz (Monsignor) on Jun 29, 2001 at 09:25 UTC
    though I didn't know it until the minutes were equal and the hours were not...apparently, it only compares the last element.

    The == operator demands scalar context of its arguments. The comma in scalar context returns the rightmost argument. So the comma didn't make a list, but evaluated the stuff for side effects and kept the last (single) value only.

    Look at mapcar to iterate the two lists in tandum.

Re: How to compare arrays? (xmms alarm clock)
by the_slycer (Chaplain) on Jun 29, 2001 at 11:36 UTC
    In this case why use arrays at all?

    Don't split the $ARGV, join localtime[2]and localtime[1] with a : and compare the string..

    At least, that's what I did in almost the exact same situation (acutally I got tired of people kicking the back of my chair to wake me up after lunch). Working my way to a "snooze button" now :-)

      believe it or not, given my newbyish questions, i have been programming since 1979...but i guess this is one more of those programs every programmer needs to write eventually :) actually, i did this about 5 years ago in Visual Basic (/me crosses himself) with the modem making a call on the data line to the voice line...

      i would assume that joining the localtime elements would require some formatting, and that a sprintf would actually be appropriate. one should assume (or better, verify) that the wakeup time has a leading zero, if minute is a single digit.

      when it comes down to it, though, i like perl arrays... that's why i did it the way i did ;)

Re: How to compare arrays? (xmms alarm clock)
by Anonymous Monk on May 14, 2002 at 20:08 UTC
    Hi,

    #The following will perform hash based comparison,
    #first array is stored within a temporary hash then data
    #comparison is made, then size check is made, this
    #avoids space problem but uses extra storage

    perl -e 'my @x=qw(a b c); my @y=(q{a},q{b c}); { my %c; print 1 if @c{@y} = @y and @c{@x} and @x == @y }'

    Audemus iura nostra defendere.