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

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

I am "trying" to get this to work, but I can't seem to find an answer in either the cookbook or programming perl. What I would like to do is randomize an entire array of jpgs in a database of about 50, then take 20 or so and print them out to a template page. What happens is that it puts ONE picture on the page, 20 times, instead of 20 thumbnails once each. Could you please help a newkid out ? Here is what I sometimes refer to as my "code".
#!/usr/bin/perl -w print "Content-type: text/html\n\n"; $dir = "/home/usr/public/images/"; $view = "view.html"; $number_to_show = 15; $title = "Your Title Could Go Here"; srand( time() ^ ($$ + ($$ << 15)) ); opendir DIR, "$dir"; my @arr = grep -f, grep /\.jpg$/i, map "$dir/$_", readdir DIR; $n = @arr; foreach (reverse 0..$n) { my $pic = splice(@arr, rand $n, 1); push @arr, $pic; $#arr = $number_to_show; } &print_it(); sub print_it { open (PAGE, "$dir$view") || die ("I am unable to open the template $vi +ew"); while (<PAGE>) { s/%%title%%/$title/g; s/%%images%%/@arr/g; print $_; } close (PAGE); }

Replies are listed 'Best First'.
Re: array problem
by gav^ (Curate) on Mar 15, 2002 at 02:03 UTC
    A quick perldoc -q shuffle array gives:
    How do I shuffle an array randomly?
    Use this: # fisher_yates_shuffle( \@array ) : # generate a random permutation of @array in place sub fisher_yates_shuffle { my $array = shift; my $i; for ($i = @$array; --$i; ) { my $j = int rand ($i+1); next if $i == $j; @$array[$i,$j] = @$array[$j,$i]; } } fisher_yates_shuffle( \@array ); # permutes @array in place
    Other points to consider:
    • What on earth are you trying to do to $#arr?
    • You probably don't want to use @arr in the subsitution
    • You probably don't want to reinvent the wheel when there are perfectly good templating modules such as HTML::Template and Text::Template
    • splice wants an integer offset, rand returns a fractional number
    • print $_ is the same as print
    I'm sure I've missed a few things. Sorry to sound overly harsh but I'm trying to be constructive :)

    gav^

Re: array problem
by Trimbach (Curate) on Mar 15, 2002 at 02:18 UTC
    A couple more suggestions. Instead of
    opendir DIR, "$dir"; my @arr = grep -f, grep /\.jpg$/i, map "$dir/$_", readdir DIR;
    you can do the same much more simply by just
    my @arr = glob("*.jpg");
    See "perldoc -f glob" for more information.

    Also, when you call a subroutine you're gonna wanna do a &print_it or print_it(), but not a &print_it(). They are different, but you only have to tell Perl it's a subroutine once. :-D

    Gary Blackburn
    Trained Killer

Re: array problem
by dws (Chancellor) on Mar 15, 2002 at 02:44 UTC
    Another approach is to shuffle a set of indices using a Schwartzian transform, and then use a subset of the randomized indices to select a subset of the images.
    my @indices = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, rand()] } 0 .. $#arr; my @subset = @arr[@indices[0..$number_to_show - 1]];
Re: array problem
by particle (Vicar) on Mar 15, 2002 at 05:17 UTC
Re: array problem
by YuckFoo (Abbot) on Mar 15, 2002 at 14:18 UTC
    Your immediate problem is here:

    s/%%images%%/@arr/g;

    Try:

    s/%%images%%/shift(@arr)/eg;

    This will remove and use the first element of @arr as a replacement. Note the addition of the 'e' modifier, to evaluate the replacement expression.

    Not sure why using @arr in s// yields a single item. The item it picks seems to be arbitrary as well.

    Also, shoot for 'use strict;' on all your programs and quit using global variables.

    YuckFoo

Re: array problem
by grummerX (Pilgrim) on Mar 15, 2002 at 18:19 UTC
    You could also check out the shuffle method in List::Util. List::Util, as the name implies, is basically just a handy set of subroutines for dealing with lists. Most of the routines are fairly simple and you could write your own versions, but it's nice to be able to drop a tried-and-true routine into your code and be done with it.

    -- grummerX