Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Perl's shift complain 'Type of arg 1 to shift must be array (not grep iterator).'?

Tags:

syntax

grep

perl

I've got a data structure that is a hash that contains an array of hashes. I'd like to reach in there and pull out the first hash that matches a value I'm looking for. I tried this:

   my $result = shift grep {$_->{name} eq 'foo'} @{$hash_ref->{list}};

But that gives me this error: Type of arg 1 to shift must be array (not grep iterator). I've re-read the perldoc for grep and I think what I'm doing makes sense. grep returns a list, right? Is it in the wrong context?

I'll use a temporary variable for now, but I'd like to figure out why this doesn't work.

like image 779
wes Avatar asked Mar 28 '10 22:03

wes


3 Answers

A list isn't an array.

my ($result) = grep {$_->{name} eq 'foo'} @{$hash_ref->{list}};

… should do the job though. Take the return from grep in list context, but don't assign any of the values other than the first.

like image 104
Quentin Avatar answered Nov 16 '22 07:11

Quentin


I think a better way to write this would be this:

use List::Util qw/first/;

my $result = first { $_->{name} eq 'foo' } @{ $hash_ref->{list} };

Not only will it be more clear what you're trying to do, it will also be faster because it will stop grepping your array once it has found the matching element.

like image 29
Leon Timmermans Avatar answered Nov 16 '22 08:11

Leon Timmermans


Another way to do it:

my $result = (grep {$_->{name} eq 'foo'} @{$hash_ref->{list}})[0];

Note that the curlies around the first argument to grep are redundant in this case, so you can avoid block setup and teardown costs with

my $result = (grep $_->{name} eq 'foo', @{$hash_ref->{list}})[0];

“List value constructors” in perldata documents subscripting of lists:

A list value may also be subscripted like a normal array. You must put the list in parentheses to avoid ambiguity. For example:

# Stat returns list value.
$time = (stat($file))[8];

# SYNTAX ERROR HERE.
$time = stat($file)[8];  # OOPS, FORGOT PARENTHESES

# Find a hex digit.
$hexdigit = ('a','b','c','d','e','f')[$digit-10];

# A "reverse comma operator".
return (pop(@foo),pop(@foo))[0];

As I recall, we got this feature when Randal Schwartz jokingly suggested it, and Chip Salzenberg—who was a patching machine in those days—implemented it that evening.

Update: A bit of searching shows the feature I had in mind was $coderef->(@args). The commit message even logs the conversation!

like image 2
Greg Bacon Avatar answered Nov 16 '22 06:11

Greg Bacon