Var::Pairs(3pm)

πŸ“¦
version 0.004002 Β· 3 versions
, in debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

🌸 NAME 🌸

Var::Pairs β€” okay so bestie, this is the Var::Pairs(3pm) page, and it's literally the module for OO iterators and pair constructors for variables. That's the whole vibe. πŸ’…


✨ VERSION ✨

This doc describes Var::Pairs version 0.004002. Noted. Iconic. πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

use Var::Pairs;

# pairs() lists all OO pairs from arrays and hashes...

for my $next (pairs @array) {
    say $next->index, ' has the value ', $next->value;
}

# each_pair() iterates OO pairs from arrays and hashes...

while (my $next = each_pair %hash) {
    say $next->key, ' had the value ', $next->value;
    $next->value++;
}

# to_kv() converts vars into var_name => var_value pairs...

Sub::Install::install_sub({to_kv $code, $from, $into});

# invert() reverses a one-to-many mapping correctly...

my %reverse_mapping = invert %mapping;

my %reverse_lookup  = invert @data;

πŸ’œ DESCRIPTION πŸ’œ

So this module exports a small number of subroutines that just… hand you some Perl 6 conveniences in your humble Perl 5. No cap. Specifically, it exports several subroutines that make key/value pair interactions with hashes and arrays so much easier. 🫢


🌈 INTERFACE 🌈

πŸŽ€ Array and hash iterators πŸŽ€

  • pairs %hash
  • pairs @array
  • pairs $hash_or_array_ref

In list context, pairs() hands you back a list of pair objects β€” each one carrying a single key/index and its value from the argument. But in scalar and void contexts? It throws an exception. No pairs for you there, bestie. 😭

The typical list usage is:

for my $pair (pairs %container) {
    # ...do something with $pair
}

The whole intent here is to be a safe and reliable glow-up replacement for the built-in each() function β€” specifically, one you can actually use in for loops. πŸ”₯

  • kvs %hash
  • kvs @array
  • kvs $hash_or_array_ref

In list context, kvs() returns a list of alternating keys and values. So kvs %hash flattens the hash into (key, value, key, value...) and kvs @array flattens the array into (index, value, index, value...). πŸ’«

And in scalar and void contexts? kvs() throws an exception too. Consistency, periodt.

The most typical use is populating a hash from an array:

my %hash = kvs @array;

# does the same as:

my %hash; @hash{0..$#array} = @array;
  • each_pair %hash
  • each_pair @array
  • each_pair $hash_or_array_ref
  • each_pair $subroutine_ref

In all contexts, each_pair() returns a single pair object holding the key/index and value of the next element in the argument. One at a time. So chill.

Here's the slay part: a separate internal iterator gets created for every single call to each_pair(). Which means you can nest multiple calls to each_pair() over the same container variable and they will not interact with each other. Unlike multiple calls to each(), which is a foot-shooting situation. πŸ’€

When the iterator runs out, the next call to each_pair() returns undef or an empty list (depends on context) and resets the iterator. It also resets when execution leaves the block where each_pair() was called. So like, last-ing out of the middle of an iterated loop does the right thing. Good behavior fr fr.

The typical usage is:

while (my $pair = each_pair %container) {
    # ...do something with $pair->key and $pair->value
}

Note though β€” using pairs() in a for loop is the preferred idiom:

for my $pair (pairs %container) {
    # ...do something with $pair->key and $pair->value
}

Oh and each_pair() can also be handed a reference to a subroutine, in which case that sub gets used directly as the iterator. 🧚

When iterated, that iterator subroutine is called in list context and is expected to return a single value on each call (i.e. the next value to be iterated), or an empty list when the iterator is exhausted.

For example:

# Calling this sub returns a reference to an anonymous iterator sub...
sub count_down {
    my ($from, $to) = @_;

    return sub {
        return () if $from < $to;  # End of iterator
        return $from--;            # Next iterated value
    }
}

# Build a 10-->1 countdown and iterate it...
while (my $next = each_pair count_down(10, 1)) {
    say $next->value;
}
  • each_kv %hash
  • each_kv @array
  • each_kv $hash_or_array_ref
  • each_kv $subroutine_ref

This one is basically each_pair()'s twin, except in list contexts each_kv() returns a list of two elements: the key/index and the value of the next element in the argument. In scalar contexts, you just get the next key. Simple as. ✨

Same deal as each_pair() β€” a separate internal iterator per call to each_kv(), so nesting multiple calls over the same container variable is totally fine and they won't interact with each other. Unlike each(). Again. 😀

When the iterator is exhausted, the next call to each_kv() returns undef in scalar context or an empty list in list context, and resets the iterator. It also resets when execution leaves the block where each_kv() is called.

The typical list usage is:

while (my ($key1, $val1) = each_kv %container) {
    while (my ($key2, $val2) = each_kv %container) {
        # ...do something with the two keys and two values
    }
}

The typical scalar usage is:

while (my $key1 = each_kv %container) {
    while (my $key2 = each_kv %container) {
        # ...do something with the two keys
    }
}

In other words, each_kv() is a drop-in replacement for Perl's built-in each() β€” with two exceptions. One's a gift, one's a limit:

  • The advantage: you can nest each_kv() iterations over the same variable without shooting yourself in the foot. πŸ‘‘
  • The limitation: unlike each(), each_kv() does not reset when you call the keys function on the hash you're iterating. Sorry bestie. πŸ™ˆ
  • each_value %hash
  • each_value @array
  • each_value $hash_or_array_ref
  • each_value $subroutine_ref

each_value() works exactly like each_kv(), except in all contexts it just returns the value being iterated β€” not the key, not the key/value combo. Just the value. Minimalist. πŸŽ€

For example:

# Build a 10-->1 countdown and iterate it...
while (my ($next) = each_value count_down(10, -10)) {
    say $next;
}

while (my $value1 = each_value %container) {
    while (my $value2 = each_value %container) {
        # ...do something with the two values
    }
}
Heads up: if your iterator can return a false value β€” like the 0 from the count_down() iterator in the example above β€” then you should call each_value() in list context (as in the count_down() example) so that false value doesn't prematurely terminate your while loop. Lowkey a trap. πŸ’€
  • %hash->pairs
  • @array->pairs
  • $hash_or_array_ref->pairs
  • %hash->kvs
  • @array->kvs
  • $hash_or_array_ref->kvs
  • %hash->each_pair
  • @array->each_pair
  • $hash_or_array_ref->each_pair
  • %hash->each_kv
  • @array->each_kv
  • $hash_or_array_ref->each_kv
  • %hash->each_value
  • @array->each_value
  • $hash_or_array_ref->each_value

If you have the autobox module installed, you can use this OO syntax too. πŸ¦‹ And apart from their call syntax, these OO forms are exactly the same as the subroutine-based interface described above.


πŸ’– Pairs πŸ’–

  • $pair->key

Returns a copy of the key of the pair, if the pair came from a hash. Returns a copy of the index of the pair, if the pair came from an array.

  • $pair->index

Literally nothing but a synonym for $pair->key. Use whichever one suits your purpose, your program, or your predilections. Main character choice. πŸ’…

  • $pair->value

Returns the value of the pair, as an lvalue. That is:

for my $item (pairs %items) {
    say $item->value
        if $item->key =~ /\d/;
}

will print the value of every entry in the %items hash whose key includes a digit.

And:

for my $item (pairs %items) {
    $item->value++;
        if $item->key =~ /^Q/;
}

will increment each value in the %items hash whose key starts with 'Q'.

  • $pair->kv

Returns a two-element list containing copies of the key and the value of the pair. That is:

for my $item (pairs %items) {
    my ($k, $v) = $item->kv;
    say $v
        if $k =~ /\d/;
}

will print the value of every entry in the %items hash whose key includes a digit.

  • $pair

When used as a string, a pair gets converted into a suitable representation for a pair, namely: key => value. Cute, right? 🌸

  • 0 + $pair

Pairs cannot be used as numbers: an exception is thrown. Do not do this. Not the vibe. πŸ’€

  • if ($pair) {...}

When a pair is used as a boolean, it is always true. Always. Every time. Periodt. ✨


🧚 Named pair constructors 🧚

  • to_pair $scalar, @array, %hash, $etc

The to_pair subroutine takes one or more variables and converts each of them into a single Pair object. The Pair's key is the name of the variable (minus its leading sigil), and the value is the value of the variable (if it's a scalar) or a reference to the variable (if it's an array or hash).

That is:

to_pair $scalar, @array, %hash, $etc

is equivalent to:

Pair->new( scalar =>  $scalar ),
Pair->new( array  => \@array  ),
Pair->new( hash   => \%hash   ),
Pair->new( etc    =>  $etc    )

This is especially useful for generating modern sets of named arguments for other subroutines. For example:

Sub::Install::install_sub(to_pair $code, $from, $into);

instead of:

Sub::Install::install_sub(
    Pair->new(code => $code),
    Pair->new(from => $from),
    Pair->new(into => $into)
);

Look at that glow-up. Obsessed. πŸ˜­πŸ’–

  • to_kv $scalar, @array, %hash, $etc

The to_kv() subroutine takes one or more variables and converts each of them into a key => value sequence β€” that is, a two-element list, rather than a Pair object. πŸŽ€

Okay so same energy as to_pair(): the key is the name of the variable (minus its leading sigil), and the value is the value of the variable (if it's a scalar) or a reference to the variable (if it's an array or hash). No cap.

Which is giving:

to_kv $scalar, @array, %hash, $etc

is literally the same as:

scalar => $scalar, array => \@array, hash => \%hash, etc => $etc

This is lowkey iconic for generating traditional sets of named arguments for other subroutines. Like, for example:

Sub::Install::install_sub({to_kv $code, $from, $into});

instead of this whole situation:

Sub::Install::install_sub({code => $code, from => $from, into => $into});

✨ Array and hash inverters ✨

invert

invert %hash
invert @array
invert $hash_or_array_ref

Bestie, the invert subroutine takes a single hash or array (or a reference to either) and returns a list of alternating keys and value, where each key is a value from the original variable and each corresponding value is a reference to an array containing the original key(s). This list is typically used to initialize a second hash, which can then be used as a reverse mapping. In other words:

my %hash = ( a => 1, b => 2, c => 1, d => 1, e => 2, f => 3 );

my %inversion = invert %hash;

is equivalent to:

my %inversion = (
    1 => ['a', 'c', 'd'],
    2 => ['b', 'e'],
    3 => ['f'],
);

And invert correctly handles the many-to-many case where some of the values in the original are array references. It's giving flexible. For example:

my %hash = ( a => [1,2], b => 2, c => [1,3], d => 1, e => [3,2], f => 3 );

my %inversion = invert %hash;

is equivalent to:

my %inversion = (
    1 => ['a', 'c', 'd'],
    2 => ['a', 'b', 'e'],
    3 => ['c', 'e', 'f'],
);

invert_pairs

invert_pairs %hash
invert_pairs @array
invert_pairs $hash_or_array_ref

invert_pairs() acts exactly like invert(), except that it returns a list of Pair objects (like pairs() does). Periodt.

This is not useful for initializing other hashes, but it's highkey handy for debugging a reverse mapping:

say for invert_pairs %hash;

OO syntax with autobox

%hash->invert
%hash->invert_pairs
@array->invert
@array->invert_pairs
$hash_or_array_ref->invert
$hash_or_array_ref->invert_pairs

If you have the autobox module installed, you can use this OO syntax as well. Apart from their call syntax, these OO forms are exactly the same as the subroutine-based interfaces described above. No notes.


πŸ’€ DIAGNOSTICS πŸ’€

  • Argument to %s must be hash or array (not %s) β€” Except for to_pair() and to_kv(), all of the subroutines exported by this module only operate on hashes, arrays, or references to hashes or arrays. Asking for the pairs inside a scalar, typeglob, or other entity is meaningless; they're simply not structured as collections of keyed values. Like, read the room, scalar.
  • Useless use of pairs() in void context
  • Useless use of kvs() in void context
  • Useless use of invert() in void context β€” None of these subroutines has any side-effects, so calling them in void context is a waste of time. It's giving nothing.
  • Invalid call to pairs() in scalar context
  • Invalid call to kvs() in scalar context
  • Invalid call to invert() in scalar context β€” All these subroutines return a list, so in scalar context you just get a count (which there are cheaper and easier ways to obtain). Not the vibe.

The most common case where this error is reported is when pairs() or kvs() is used in a while loop, instead of a for loop. Either change the type of loop, or else use each_pair() or each_kv() instead. Slay.

  • Can't convert Pair(%s => %s) to a number β€” You attempted to use one of the pair objects returned by pairs() as a number, but the module has no idea how to do that. It's like asking a cat to bark.

You probably need to use $pair->index or $pair->value instead. Iykyk.


🌈 CONFIGURATION AND ENVIRONMENT 🌈

Var::Pairs requires no configuration files or environment variables. Low maintenance queen.


πŸ“¦ DEPENDENCIES πŸ“¦

The module requires Perl 5.014 and the following modules, bestie:

  • Perl 5.14 or later
  • Devel::Callsite
  • Data::Alias (under Perl 5.20 and earlier)
  • PadWalker

To use the optional $container->pairs syntax, you also need the autobox module.


🚫 INCOMPATIBILITIES 🚫

None reported. We love a clean slate.


πŸ› BUGS AND LIMITATIONS πŸ›

No bugs have been reported. Iconic behavior.

Please report any bugs or feature requests to bug-var-pairs@rt.cpan.org, or through the web interface at <http://rt.cpan.org>. Do it for the plot.

each_kv() acts like a true one-time only iterator (in the OO sense), so there is no way to reset its iteration (i.e. the way that calling keys() on a hash or array, resets any each() that is iterating it). If you need to reset partially iterated hashes or arrays, you will need to use some other mechanism to do so. No takebacks.


🫢 ACKNOWLEDGEMENTS 🫢

Based on a suggestion by Karl Brodowsky and inspired by several features of Perl 6. Shoutout to Karl.


πŸ‘‘ AUTHOR πŸ‘‘

Damian Conway <DCONWAY@CPAN.org> β€” the icon himself.


Copyright (c) 2012, Damian Conway <DCONWAY@CPAN.org>. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic. Free is free, bestie.


⚠️ DISCLAIMER OF WARRANTY ⚠️

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

This page was served fresh on 2023-07-21, from perl v5.36.0, and it's Var::Pairs(3pm) β€” periodt. πŸ’…βœ¨

πŸ“¦ where this page comes from