Perl list processing is for hashes, too
Tags: perl, lists, hashes, map
Published: 2022-02-10 18:43:31

---
title: Perl list processing is for hashes, too
published: true
date: 2022-02-10 15:00:00 UTC
tags: Perl,lists,hashes,map
canonical_url: https://phoenixtrap.com/2022/02/10/perl-list-processing-is-for-hashes-too/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdhnselc0sj27ec1trik.jpeg
series: List processing in Perl
---

This month I started a new job at [Alert Logic](https://www.alertlogic.com/), a cybersecurity provider with Perl (among many other things) at its beating heart. I’ve been learning a lot, and part of the process has been understanding the APIs in the code base. To that end, I’ve been writing small test scripts to tease apart data structures, using Perl [array-processing](https://perldoc.perl.org/perlfunc#Functions-for-real-@ARRAYs), [list-processing](https://perldoc.perl.org/perlfunc#Functions-for-list-data), and [hash- (i.e., associative array)-processing functions](https://perldoc.perl.org/perlfunc#Functions-for-real-%25HASHes).

I’ve covered `map`, `grep`, and friends a [couple](https://phoenixtrap.com/2021/03/16/perl_map_grep/) [times](https://phoenixtrap.com/2021/10/26/better-perl-four-list-processing-best-practices-with-map-grep-and-more/) before. [Most recently](https://phoenixtrap.com/2021/10/26/better-perl-four-list-processing-best-practices-with-map-grep-and-more/), I described using [List::Util’s `any` function](https://perldoc.perl.org/List::Util#any) to check if a condition is true for any item in a list. In the simplest case, you can use it to check to see if a given value is in the list at all:

```perl
use feature 'say';
use List::Util 'any';
my @colors =
  qw(red orange yellow green blue indigo violet);
say 'matched' if any { /^red$/ } @colors;
```

However, if you’re going to be doing this a lot with arbitrary strings, [Perl FAQ section 4 advises](https://perldoc.perl.org/perlfaq4#How-can-I-tell-whether-a-certain-element-is-contained-in-a-list-or-array?) turning the array into the keys of a hash and then checking for membership there. For example, here’s a simple script to check if the colors input (either from the keyboard or from files passed as arguments) are in the rainbow:

```perl
#!/usr/bin/env perl

use v5.22; # introduced <<>> for safe opening of arguments
use warnings;
 
my %in_colors = map {$_ => 1}
  qw(red orange yellow green blue indigo violet);

while (<<>>) {
  chomp;
  say "$_ is in the rainbow" if $in_colors{$_};
}
```

List::Util has a [bunch of functions for processing lists of pairs](https://perldoc.perl.org/List::Util#KEY/VALUE-PAIR-LIST-FUNCTIONS) that I’ve found useful when pawing through hashes. `pairgrep`, for example, acts just like `grep` but instead assigns `$a` and `$b` to each key and value passed in and returns the resulting pairs that match. I’ve used it as a quick way to search for hash entries matching certain value conditions:

```perl
use List::Util 'pairgrep';
my %numbers = (zero => 0, one => 1, two => 2, three => 3);
my %odds = pairgrep {$b % 2} %numbers;
```

Sure, you could do this by invoking a mix of plain `grep`, `keys`, and a [hash slice](https://perldoc.perl.org/perldata#Key/Value-Hash-Slices), but it’s noisier and more repetitive:

```perl
use v5.20; # for key/value hash slice 
my %odds = %numbers{grep {$numbers{$_} % 2} keys %numbers};
```

`pairgrep`’s compiled C‑based [XS](https://perldoc.perl.org/perlxs) code can also be faster, as evidenced by this [Benchmark](https://perldoc.perl.org/Benchmark) script that works through a hash made of the [Unix `words` file](https://en.wikipedia.org/wiki/Words_(Unix)) (479,828 entries on my machine):

```perl
#!/usr/bin/env perl

use v5.20;
use warnings;
use List::Util 'pairgrep';
use Benchmark 'cmpthese';

my (%words, $count);
open my $fh, '<', '/usr/share/dict/words'
  or die "can't open words: $!";
while (<$fh>) {
  chomp;
  $words{$_} = $count++;
}
close $fh;

cmpthese(100, {
  grep => sub {
    my %odds = %words{grep {$words{$_} % 2} keys %words};
  },
  pairgrep => sub {
    my %odds = pairgrep {$b % 2} %words;
  },
} );
```

Benchmark output:

```
           Rate     grep pairgrep
grep     1.47/s       --     -20%
pairgrep 1.84/s      25%       --
```

* * *

In general, I urge you to work through the [Perl documentation](https://phoenixtrap.com/2021/10/12/read-the-fantastic-manual-how-to-get-the-most-out-of-perl-documentation/)’s tutorials on [references](https://perldoc.perl.org/perlreftut), [lists of lists](https://perldoc.perl.org/perllol), the [data structures cookbook](https://perldoc.perl.org/perldsc), and the FAQs on [array](https://perldoc.perl.org/perlfaq4#Data:-Arrays) and [hash manipulation](https://perldoc.perl.org/perlfaq4#Data:-Hashes-(Associative-Arrays)). Then dip into the various [list-processing modules](https://phoenixtrap.com/2021/05/18/a-list-of-perl-list-processing-modules/) (especially the included [List::Util](https://perldoc.perl.org/List::Util) and CPAN’s [List::SomeUtils](https://metacpan.org/pod/List::SomeUtils)) for ready-made functions for common operations. You’ll find a wealth of techniques for creating, managing, and processing the data structures that your programs need.