6

In python, I can do the following to get all the objects in a list with a specific property. In this example I grab the list of id fields of every obj in list objs where obj.id is greater than 100:

ids = [ obj.id for obj in objs if obj.id > 100]

How would I do the same in perl? I think I want to use map, but I don't know how to conditionally map items from the origin set to the destination set.

4 Answers 4

13

The map block can return 0 or more elements for each element in the original list. To omit an element, just return the empty list ():

my @ids = map { $_->id > 100 ? $_->id : () } @objs;

This assumes that the objects in @objs have an id attribute and associated accessor. If you want direct hash access, you can do that too:

my @ids = map { $_->{id} > 100 ? $_->{id} : () } @objs;

Or, you can just combine map and grep:

my @ids = map { $_->id } grep { $_->id > 100 } @objs;

# Or reverse the order to avoid calling $_->id twice:
my @ids = grep { $_ > 100 } map { $_->id } @objs;

I'm not sure which of those would be more efficient, but unless @objs is really big, that's unlikely to matter much.

If the value you're extracting from the object is expensive to calculate, then you can cache the value for the test and return value:

my @vals = map { my $v = $_->expensive_method;  $v > 100 ? $v : () } @objs;
Sign up to request clarification or add additional context in comments.

3 Comments

map+($_->id)x($_->id>100),@objs
@ysth: Yikes. I think I'd save that one for code golf. The ternary operator is much safer, since you don't have to ensure your condition returns only 0 or 1.
if in doubt, use the ()x!! operator
2

Use grep to return only those items that match the condition. It's like filter in other languages.

grep { condition } @array

For example:

my @nums = (1, 50, 7, 105, 200, 3, 1000);
my @numsover100 = grep { $_ > 100 } @nums;
foreach my $num (@numsover100) {
    print $num . "\n";
}

2 Comments

So I have to pair that with map to get what I want? Since I actually want the sub-field of each of the objects that I've grep'd out of the list?
Yep, that's the way I'd do it.
1

You could probably get by with combining map and filter, which is essentially what we did in Python before list comprehensions.

1 Comment

Perl's filter equivalent is called grep. No need to go to the lengths they went to in your perlmonks link for this problem.
0

Using map and grep together passes over the list twice. Build your own:

sub fancy_filter {
  my ($map_block, $grep_block, @list) = @_;
  my @results;
  foreach my $item (@list) {
    local $_ = $item;
    if ($grep_block->()) {
      push @results, $map_block->();
    }
  }
  return @results;
}

my @ids = fancy_filter(
  sub { $_->{id} },       # map block
  sub { $_->{id} > 100 }, # grep block
  @id_list,
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.