Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Basic help with mapping

by bradcathey (Prior)
on Nov 22, 2005 at 04:11 UTC ( #510628=perlquestion: print w/replies, xml ) Need Help??

bradcathey has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monasterians,

I'm a little embarrassed to be this far into monkdom and still not understanding mapping (and grepping for that matter). I'm trying to alter a specific key's value in a AoH ref. I tried mapping a simple array and no problem. But I get the feeling I'm missing something really important as I'm getting zero results with my latter expressions.

#I get this my @array = qw(aaa-aaa bbb-bbb ccc-ccc); my @newarray = map { $_ =~ s/-/_/g } @array; print Dumper(@array); my $AoH_orig = [ { 'title' => 'aaa-aaa', 'name' => 'tom' }, { 'title' => 'bbb-bbb', 'name' => 'kathy' }, { 'title' => 'ccc-ccc', 'name' => 'bill' } ]; # how i would do it "long hand" for ( 0 .. $#$AoH_orig ) { $AoH_orig->[$_]{'title'} =~ s/-/_/g; } #try mapping my $AoH_new = map { $AoH_orig->[$_]{'title'} =~ s/-/_/g } @$AoH_orig; #or my $AoH_new = map { $_->{'title'} =~ s/-/_/g } @$AoH_orig; print Dumper($AoH_orig);

Comments? or can you point me to a good tutorial? Thanks in advance.

Update: I guess I was using map when I should have just been using the good olde foreach. But, hey, I'm a lot closer to getting it with everyone's help. Thanks.


—Brad
"The important work of moving the world forward does not wait to be done by perfect men." George Eliot

Replies are listed 'Best First'.
Re: Basic help with mapping
by pg (Canon) on Nov 22, 2005 at 04:57 UTC

    map works... but there is a feeling that it is not needed. If I do it, I will simply:

    use strict; use warnings; use Data::Dumper; my $AoH_orig = [ { 'title' => 'aaa-aaa', 'name' => 'tom' }, { 'title' => 'bbb-bbb', 'name' => 'kathy' }, { 'title' => 'ccc-ccc', 'name' => 'bill' } ]; for (@$AoH_orig) {$_->{'title'} =~ s/-/_/g}; print Dumper($AoH_orig);

    Which gives you what you wanted:

    $VAR1 = [ { 'name' => 'tom', 'title' => 'aaa_aaa' }, { 'name' => 'kathy', 'title' => 'bbb_bbb' }, { 'name' => 'bill', 'title' => 'ccc_ccc' } ];
Re: Basic help with mapping
by sk (Curate) on Nov 22, 2005 at 04:29 UTC
    Am i missing something? Your second map logic works fine (modified space to -) if i understand the problem

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $AoH_orig = [ { 'title' => 'aaa-aaa', 'name' => 'tom' }, { 'title' => 'bbb-bbb', 'name' => 'kathy' }, { 'title' => 'ccc-ccc', 'name' => 'bill' } ]; #try mapping print Dumper ($AoH_orig); my $AoH_new = map { $_->{'title'} =~ s/-/_/g } @$AoH_orig; print Dumper($AoH_orig);

    $VAR1 = [ { 'name' => 'tom', 'title' => 'aaa-aaa' }, { 'name' => 'kathy', 'title' => 'bbb-bbb' }, { 'name' => 'bill', 'title' => 'ccc-ccc' } ]; $VAR1 = [ { 'name' => 'tom', 'title' => 'aaa_aaa' }, { 'name' => 'kathy', 'title' => 'bbb_bbb' }, { 'name' => 'bill', 'title' => 'ccc_ccc' } ];

    All dashes (-) are replaced with underscore (_) isn't that what you are trying?

    I probably will not use map for this as you are not returning a list back. I would stick with your for for idea.

     $_->{'title'} =~ s/-/_/g for(@$AoH_orig); should work just fine (i think)

    cheers

    SK

Re: Basic help with mapping
by Errto (Vicar) on Nov 22, 2005 at 04:57 UTC
    The main question is whether you want to make a copy of your original structure or simply to modify it in place. If you're going for the modify approach, then you really shouldn't use map I feel because map conveys the sense of returning a list of something. But you can make your for loop a bit more concise:
    for (@$AoH_orig) { $_->{title} =~ s/-/_/g; }
    If you do want to make a copy, then you should really make it a deep copy because otherwise your two arrays will really be identical, since they have the same references in them. For this you can use map:
    my $AoH_new = [ map { my %tmp = %$_; $tmp{title} =~ s/-/_/g; \%tmp } @ +$AoH_orig ];
    The key points here are a) I am calling map in list context and returning the result as an array reference, since that's what you want $AoH_new to be (otherwise you'd have to make it a real array like @AoH_new) and b) I need to actually copy the hash for each element in order to make a completely new structure.
Re: Basic help with mapping
by blokhead (Monsignor) on Nov 22, 2005 at 06:15 UTC
    There are several things that need fixing here:
    • my $AoH_new = map ...
      map generally returns a list, not an array ref. Here, you're calling it in scalar context, so it actually returns the number of items in its return list. This isn't what you want, judging by your variable names.
    • my $AoH_new = map { $AoH_orig->[$_]{'title'} =~ s/ /_/g } @$AoH_orig;
      For each item in the list, map executes the block with that item aliased to $_. But here you are treating $_ as an array index for @$AoH_orig, not as an element of $AoH_orig. Either map over the actual indices (0 .. $#$AoH_orig), or use something closer to your second example, which is:
    • my $AoH_new = map { $_->{'title'} =~ s/ /_/g } @$AoH_orig;
      Each time map executes the block, it accumulates the return value of the block in a list, and finally returns the list. In this case, you're accumulating the return values of "$str =~ s///g" statements. But these statements only return the number of changes made to the string. Again, it seems like you want modified copies of the strings in $AoH_orig, with a similar structure -- not a list of the number of changes made to each string.
    • Since $_ is aliased to the elements of @$AoH_orig, the s/// inside the map actually modifies things inside of $AoH_orig. Again, probably not what you want.
    • Even fixing all that, if you want to replicate the structure of $AoH_orig, you need to have a level of hashes in there somewhere. If you want an AoH, then each item in the array needs to be a hashref -- the map statement must return a list of hashrefs. So the map's block must evaluate to a hashref each time.
    Here is maybe what you wanted to say:
    my @AoH_new = map { ## store the result in an array my %h = %$_; ## make a copy of each hashref $h{title} =~ s/ /_/g; ## modify a value in the hash copy \%h; ## return a ref to that copy } @$AoH_orig;
    Or even better, perhaps:
    use Storable 'dclone'; my $AoH_new = dclone $AoH_old; ## copy the old structure ## change an item in each hash in the new copy $_->{title} =~ s/ /_/g for @$AoH_new;

    Update: changed "$h->" to "$h" in my example. Thanks Errto.

    blokhead

Re: Basic help with mapping
by steveAZ98 (Monk) on Nov 22, 2005 at 04:33 UTC
    I'm not sure if it's a typo or not, but your missing the "-" in the map statements.

    Also, in the first "try mapping" statement you're trying to use the hash as the array index. The map statement iterates over the items in the array, which are hash references. So the second map statement is the correct usage.

    The map statement returns only the number of elements in scalar context, so you would want to use "my @AoH_new = ". And the result of the substitution is 1 so you would get an array of three 1's unless you return the value of $_;

    So you end up with:
    my @AoH_new = map { $_->{'title'} =~ s/-/_/g; $_ } @$AoH_orig;

    HTH, Steve
Re: Basic help with mapping
by NetWallah (Canon) on Nov 22, 2005 at 07:00 UTC
    Here is another interpration of the OP's requirements :
    wanting to get an array ref containing ONLY the "title" element values, with the "_" substitution:
    my $Aref_new =[ map {(my $x=$_->{'title'}) =~ s/ /_/g;$x } @$AoH_orig]; print Dumper(\$Aref_new);
    Note: Using the temporary "$x" allows the use of the "map" while preserving the original $AoH_orig, and getting the array ref (Hence the square brackets) containing the modified values.

         "Man cannot live by bread alone...
             He'd better have some goat cheese and wine to go with it!"

Re: Basic help with mapping
by tphyahoo (Vicar) on Nov 22, 2005 at 10:01 UTC
    Hey, nobody has complained yet that you didn't use strict;use warnings. So I will.

    When I did this, I got lots of warnings, such as

    Use of reference "HASH(0x224f58)" as array index at scratch.pl line 23 +. Use of uninitialized value in substitution (s///) at scratch.pl line 2 +3. Use of reference "HASH(0x18916bc)" as array index at scratch.pl line 2 +3. Use of uninitialized value in substitution (s///) at scratch.pl line 2 +3. Terminating on signal SIGINT(2) Use of uninitialized value in substitution (s///) at scratch.pl line 2 +3.
    I haven't looked into what the specific problem is, but since other monks seem to already have addressed this in various ways, I will just admonish to always use the strictures! Hope this helps :)
Re: Basic help with mapping
by Roy Johnson (Monsignor) on Nov 22, 2005 at 16:02 UTC
    can you point me to a good tutorial?
    In the Tutorials section is Map: The Basics.

    Caution: Contents may have been coded under pressure.

      Perfect, thanks (for anyone interested, this is a great tutorial).


      —Brad
      "The important work of moving the world forward does not wait to be done by perfect men." George Eliot

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://510628]
Approved by sk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2020-11-28 03:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?