Um...I have the following code snippet, and was wondering why the second subroutine, search($$)
fails to yield results...while the first routine, search_item($$$)
, performs admirably (imo).
########################
# generate and return a list of users which match only
# one criteria (eg: eyes=2)
#
# $users is a reference to an array of 6-digit hexidecimal user IDs (eg: 000001, 000002, etc)
# $name is the name of the key (or field) to find (eg: 'eyes')
# $value (eg: 2) is compared with the value stored in the key
# if $value matches what's in the $name'd key, then add the uid to a list
sub search_item($$$) {
my ($users, $name, $value) = @_;
my @searched;
foreach my $uid (@$users) {
my %ustats = user::getUserStats($uid);
if ($ustats{$name} eq $value) { push @searched, $uid; }
}
return @searched;
}
########################
# generate and return a list of users which match
# many criteria (eg: eyes=2, hair=1, etc)
#
# $users is a reference to an array of user IDs (eg: 000001, 000002, etc)
# $terms is a reference to an array of search terms (eg: $terms[0] = "eyes=2";)
sub search($$) {
my $users = $_[0]; # an array reference
my $terms = $_[1]; # an array reference
my @searched;
my $first = 1;
foreach my $term (@$terms) {
# since @$terms is an array of scalars, in the format of 'name=value' pairs
my $name = $term; $name =~ s/=(.)*//;
my $value = $term; $value =~ s/$name=//;
if ($first) {
# search the given list reference ($users)
@searched = search_item($users, $name, $value);
$first = 0; # set to 0 cause now we gotta use @searched
} else {
# otherwise use a reference to @searched
@searched = search_item(\@searched, $name, $value);
}
}
return @searched;
}
i have setup the data so the code should return 1 hit. the data is correct and underlying functions (eg: getUserStats($)
) also perform flawlessly.
both user 000001 and 969696 have eyes=2 all others eyes=1 and user ID 000001 is gender=1, all others gender=0
so...if i write:
my @users = getUsers();
foreach my $uid (search_item(\@users, 'eyes', 2)) {
print "$uid<br>\n";
}
i get 2 hits of a total of 6 users in my database (this IS a correct result, of course). satisfied with those results, I run the search
routine.
my @terms = ('eyes=2', 'gender=1'); # gender=0 is a boy. 1 is a girl
my @sResults = search(\@users, \@terms);
if (@sResults) {
foreach my $uid (@sResults) {
print "$uid<br>\n";
}
} else {
print "nothing found!<br>\n";
}
i always see "nothing found!" when i pray and hope to see "000001" instead... :(
this seems like legit code....so....whud am i doin wrong guys??? am i not derefencing something correctly? or...is the dereferencing / referencing the source of my dilema? i loath pointers...however incredibly useful :p
Your code does actually work if you pass correct parameters. My best guess is that the strings like eyes=2
that you're using contain spurious whitespace such as a trailing newline
Here's the test program that I used to work on your subrouitines
use strict;
use warnings;
use 5.010;
my %users = (
'000001' => { eyes => 2, gender => 1 },
'000002' => { eyes => 1, gender => 0 },
'000003' => { eyes => 1, gender => 0 },
'000004' => { eyes => 1, gender => 0 },
'969696' => { eyes => 2, gender => 0 },
);
sub user::getUserStats {
my ( $uid ) = @_;
%{ $users{$uid} };
}
########################
# generate and return a list of users which match only
# one criteria (eg: eyes=2)
#
# $users is a reference to an array of 6-digit hexidecimal user IDs (eg: 000001, 000002, etc)
# $name is the name of the key (or field) to find (eg: 'eyes')
# $value (eg: 2) is compared with the value stored in the key
# if $value matches what's in the $name'd key, then add the uid to a list
sub search_item($$$) {
my ( $users, $name, $value ) = @_;
my @searched;
foreach my $uid ( @$users ) {
my %ustats = user::getUserStats( $uid );
if ( $ustats{$name} eq $value ) { push @searched, $uid; }
}
return @searched;
}
########################
# generate and return a list of users which match
# many criteria (eg: eyes=2, hair=1, etc)
#
# $users is a reference to an array of user IDs (eg: 000001, 000002, etc)
# $terms is a reference to an array of search terms (eg: $terms[0] = "eyes=2";)
sub search($$) {
my $users = $_[0]; # an array reference
my $terms = $_[1]; # an array reference
my @searched;
my $first = 1;
foreach my $term ( @$terms ) {
# since @$terms is an array of scalars, in the format of 'name=value' pairs
my $name = $term;
$name =~ s/=(.)*//;
my $value = $term;
$value =~ s/$name=//;
if ( $first ) {
# search the given list reference ($users)
@searched = search_item( $users, $name, $value );
$first = 0; # set to 0 cause now we gotta use @searched
}
else {
# otherwise use a reference to @searched
@searched = search_item( \@searched, $name, $value );
}
}
return @searched;
}
my $users = [ keys %users ];
say for search( $users, [ 'eyes=2', 'gender=1' ] );
000001
Here's how I would write similar subroutines that behave identically and take the same parameters, but there is a lot in the design of this application that is less that optimal
sub search_item {
my ( $users, $name, $value ) = @_;
grep {
my %ustats = user::getUserStats( $_ );
$ustats{$name} eq $value;
} @$users;
}
sub search {
my ($users, $terms) = @_;
my @searched;
for my $term ( @$terms ) {
my ($name, $value) = split /=/, $term;
@searched = search_item( $users, $name, $value );
$users = \@searched;
}
@searched;
}
but I think user::getUserStats
should be called User::get_user_stats
(because Perl reserves capital letters for global identifiers such as package names) and it should return a reference to a hash instead of just a list