NAME

Hash::Map::Tutorial - What is Hash::Map and how do I use it?

Hint

When I write

$obj = $obj->this_example_method;

I mean, that the Hash::Map object itself will be returned. So it is possible to build chains like that:

$obj->this_example_method->next_example_method;

That is typical used for setter or worker methods.

Why existing both, method_name and method_name_ref?

The methods are existing as normal name and with postfix "_ref". The idea is that user code should be clear and free of noise like:

$obj->name_ref( $hashref );
$obj->name( %hash );
# instead of
$obj->name( %{$hashref} );
$obj->name_ref( \%hash );

%hash     = $obj->target;
$hash_ref = $obj->target_ref;
# instead of
%hash     = %{ $obj->target_ref };
$hash_ref = { $obj->target };

Why source and not set target directly?

In source can stored a hash. If that is not done, an empty hash is used instead.

The methods

copy_keys              copy_keys_ref
map_keys               map_keys_ref
copy_modify            copy_modify_ref
copy_modify_idientical copy_modify_identical_ref
map_modify             map_modify_ref
map_modify_idientical  map_modify_identical_ref

copy selected data from source to target hash. Some methods can modify the data during transport.

Some methods are only for target available.

In target can stored a hash. If that is not done, an empty hash is used instead.

The methods

combine
delete_keys delete_keys_ref
modify      modify_ref

modify the target hash.

Methods

OO style ist typical to use that module.

require Hash::Map;

# The constructor "new" is typical not called directly.
# Methods "target", "set_target", "target_ref", "set_target_ref",
# "source", "set_source", "source_ref", "set_source_ref"
# and "combine" are alternative constructors.
my $obj = Hash::Map->new;

# set source hash
$obj = $obj->source(b => 2, c => 3);
$obj = $obj->set_source(b => 2, c => 3);
$obj = $obj->source_ref({b => 2, c => 3});
$obj = $obj->set_source_ref({b => 2, c => 3});

# get source hash
%hash     = $obj->source; # get, because no set parameters
$hash_ref = $obj->source_ref;

# set target hash
$obj = $obj->target(a => 1);
$obj = $obj->set_target(a => 1);
$obj = $obj->target_ref({a => 1});
$obj = $obj->set_target_ref({a => 1});

# get target hash
%hash     = $obj->target; # get, because no set parameters
$hash_ref = $obj->target_ref;

# keys
@array     = $obj->source_keys;
$array_ref = $obj->source_keys_ref;
@array     = $obj->target_keys;
$array_ref = $obj->target_keys_ref;

# values
@array     = $obj->source_values;
$array_ref = $obj->source_values_ref;
@array     = $obj->target_values;
$array_ref = $obj->target_values_ref;

# exists
$boolean = $obj->exists_source;
$boolean = $obj->exists_target;

# combine - merge targets of other Hash::Map objects into $obj target
$obj = $obj->combine(@objects);

# clone
$obj = $obj->clone_source;
$obj = $obj->clone_target;

# delete keys in target
$obj = $obj->delete_keys( qw(x y) );
$obj = $obj->delete_keys_ref([ qw(x y) ]);

# copy data from source to target using keys
$obj = $obj->copy_keys(qw(b c))
$obj = $obj->copy_keys_ref([ qw(b c) ]);
# including a key rewrite rule as code reference
$obj = $obj->copy_keys(
    qw(b c),
    sub {
        my $obj = shift;
        my $key = $_;
        return "new $key";
    },
);
$obj = $obj->copy_keys_ref(
    [ qw(b c) ],
    sub {
        my $obj = shift;
        my $key = $_;
        return "new $key";
    },
);

# copy data from source (key of map) to target (value of map)
$obj = $obj->map_keys(b => 'bb', c => 'cc');
$obj = $obj->map_keys_ref({b => 'bb', c => 'cc'});

# merge the given hash into target hash
$obj = $obj->merge_hash(d => 4, e => 5);
$obj = $obj->merge_hashref({d => 4, e => 5});

# modify target inplace by given code
# Maybe the combined methods is what you are looking for,
# see method "copy_modify_identical" or "map_modify_identical".
$obj = $obj->modify(
    f => sub {
        my $obj = shift;
        my $current_value_of_key_f_in_target = $_;
        return; # $target{f} will be undef because of scalar context
    },
    ...
);
$obj = $obj->modify_ref({
    f => sub {
        my $obj   = shift;
        my $current_value_of_key_f_in_target = $_;
        return "new $value";
    },
    ...
});

# copy data from source to target using keys
# and then
# modify target inplace by given code
# Maybe method "copy_modify_idientical" is what you are looking for.
$obj = $obj->copy_modify(
    f => sub {
        my $obj = shift;
        my $current_value_of_key_f_in_target = $_;
        return; # $target{f} will be undef because of scalar context
    },
    ...
);
$obj = $obj->copy_modify_ref({
    f => sub {
        my $obj   = shift;
        my $current_value_of_key_f_in_target = $_;
        return "new $value";
    },
    ...
});
$obj = $obj->copy_modify_identical(
    qw(b c),
    sub {
        my $obj = shift;
        my $current_value_of_each_key_in_target = $_;
        return; # $target{key} will be undef because of scalar context
    },
);
$obj->copy_modify_identical_ref(
    [ qw(b c) ],
    sub {
        my $obj = shift;
        my $current_value_of_each_key_in_target = $_;
        return; # $target{key} will be undef because of scalar context
    },
);

# copy data from source (key of map) to target (value of map)
# and then
# modify target inplace by given code
# Maybe method "map_modify_idientical" is what you are looking for.
$obj = $obj->map_modify(
    f => ff => sub {
        my $obj = shift;
        my $current_value_of_key_f_in_source = $_;
        return; # $target{ff} will be undef because of scalar context
    },
    ...
);
$obj = $obj->map_modify_ref([
    f => ff => sub {
        my $obj   = shift;
        my $current_value_of_key_f_in_source = $_;
        return "new $value";
    },
    ...
]);
$obj = $obj->map_modify_identical(
    (
        f => ff,
        ...
    ),
    sub {
        my $obj = shift;
        my $current_value_of_each_key_in_source = $_;
        return; # $target{key} will be undef because of scalar context
    },
);
$obj = $obj->map_modify_identical_ref(
    {
        f => ff,
        ...
    },
    sub {
        my $obj   = shift;
        my $current_value_of_each_key_in_source = $_;
        return "new $value";
    },
);

Iteration

Use each_...:

while ( my ($key, $value) = $obj->each_source ) {
    ...
}

while ( my ($key, $value) = $obj->each_target ) {
    ...
}

Use iterator code:

my $iterator_code = $obj->source_iterator;
while ( my ($key, $value) = $iterator_code->() ) {
    ...
}

my $iterator_code = $obj->target_iterator;
while ( my ($key, $value) = $iterator_code->() ) {
    ...
}

Automatic construction

Methods "source", "set_source", "source_ref", "set_source_ref" "target", "set_target", "target_ref", "set_target_ref" and "combine" can work as constructor too.

Hash::Map->new->target(...);
Hash::Map->new->set_target(...);
Hash::Map->new->target_ref(...);
Hash::Map->new->set_target_ref(...);
Hash::Map->new->source(...);
Hash::Map->new->set_source(...);
Hash::Map->new->source_ref(...);
Hash::Map->new->set_source_ref(...);
Hash::Map->new->combine(...);

shorter written as:

Hash::Map->target(...);
Hash::Map->set_target(...);
Hash::Map->target_ref(...);
Hash::Map->set_target_ref(...);
Hash::Map->source(...);
Hash::Map->set_source(...);
Hash::Map->source_ref(...);
Hash::Map->set_source_ref(...);
Hash::Map->combine(...);

Also available - functional style

The first idea was to implemnt that functional. But OO style is more clear readable. But this exists.

use Hash::Map qw(hash_map hashref_map);

%target_hash = hash_map(
    \%source_hash,
    # The following references are sorted anyway.
    # Running in order like written.
    [ qw(key1 key2) ],               # copy_keys from source to target hash
    [ qw(key3 key4), $code_ref ],    # copy_keys, code_ref to rename keys
    {
        source_key1 => 'target_key', # map_keys from source to target hash
        source_key2 => $code_ref,    # modify values in target hash
    },
);

Similar, only the method name and return value has changed.

$target_hashref = hashref_map(
    $source_hashref,
    ...
);

Code examples

Why are this code examples with Hash::Map longer than the original code?

Because this example is untypical or typical for fist development step. The fully code would explode this tutorial. If you have nearly 1 type of each mapping. Map it like before.

Often that copied code is not produced during first development. There are lots of changes and everyone adds lines. Then you have lots of nearly equal lines.

This module helps you to refractor, makes code readable and prevents: Don't repeat yourself.

Line reduced found code (maybe from fist development step)

person_data(
    street       => $form->{street},
    city         => $form->{city},
    country_code => $form->{country_code} eq 'D'
                    ? 'DE'
                    : $form->{country_code},
    zip_code     => $form->{zip},
    name         => "$form->{first_name} $form->{family_name}",
    account      => $user->get_account,
    mail_name    => $mail->{name},
    mail_address => $mail->{address},
);

Implemented with OO interface

person_data(
    Hash::Map->combine(
        Hash::Map
            ->source_ref($form)
            ->copy_keys(
                qw(street city)
            )
            ->copy_modify(
                country_code => sub {
                    return $_ eq 'D' ? 'DE' : $_;
                },
            )
            ->map_keys(
                zip => 'zip_code',
            )
            ->merge_hash(
                name => "$form->{first_name} $form->{family_name}",
            ),
        Hash::Map
            ->source_ref($user)
            ->copy_modify(
                account => sub {
                    return $_->get_account;
                },
            ),
        Hash::Map
            ->source_ref($mail)
            ->copy_keys(
                qw(name address),
                sub {
                    return "mail_$_";
                },
            ),
    )->target
);

Implemented with functional interface

person_data(
    hash_map(
        # source_ref,
        $form,
        # copy_keys
        [ qw(street city country_code) ],
        {
            # modify
            country_code => sub {
                return $_ eq 'D' ? 'DE' : $_;
            },
            # map_keys
            zip => 'zip_code',
        },
    ),
    # merge_hash
    name => "$form->{first_name} $form->{family_name}",
    hash_map(
        $user,
        # copy_keys
        [ qw(account) ],
        {
            # modify
            account => sub {
                return $_->get_account;
            },
        },
    ),
    hash_map(
        $mail,
        [
            # copy_keys
            qw(name address),
            sub {
                return "mail_$_";
            },
        ],
    ),
);

an example from real code

Rename keys during fetching from database result into a hash reference.

Hash::Map
    ->new
    ->map_modify_identical(
        qw(
            vorname  firstname
            name     lastname
            zusatz1  name1
            zusatz2  name2
            strasse  street
            ortsteil city_part
            plz      zipcode
            ort      city
        ),
        method ($source_key) {
            return $db_result->$source_key;
        },
    )
    ->target_ref;