Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

removing the spaces from keys of a complex hash

by gwhite (Friar)
on May 07, 2008 at 16:15 UTC ( #685250=perlquestion: print w/replies, xml ) Need Help??
gwhite has asked for the wisdom of the Perl Monks concerning the following question:

I have an older legacy module that returns me a hash with a couple of hundred keys. This module is in use by several other parts of the application so rewriting it is not desirable. The hash that is returned to me has some of the values as hash references. My desire is to write the hash out as XML using XML::Simple. Part of the trouble is that some of the keys have spaces in them which means the XML is not well formed. Keys in the hash references also suffer this affliction.

What I cannot get my mind around is how to parse through the hash and hash references, then use a regex to strip the spaces from the keys, and result in a hash with XML valid keys.

The hash looks something like: $global{'title'}='Report for today' $global{'data 1'}{title}='My data 1 title' $global{'data 1'}{data}= (ref to hash) $global{'data 1'}{footer}='My footer' $global{'data 2'}{title}='My data 2 title' $global{'data 2'}{subtitle}='Data 2 subtitle' $global{'data 2'}{data}= (ref to hash) $global{'data 2'}{'data percentages'}= (ref to hash) $global{'data 2'}{'data raw'}= (ref to hash) $global{'data 2'}{footer}='Footer for data 2' and so on..

Once I have printed the XML file, I am done with the data so changing the keys at this point will not impact any other processes. Here is what I have come up with at this point (but it does not seem to be affecting the hash references)

sub hash_remove_key_space { my $in_hash = shift; my %work_hash = %$in_hash; my %output_hash; foreach my $ckey(keys %work_hash){ my $value; if (ref($work_hash{$ckey}) eq "HASH") { $value = hash_remove_key_space($work_hash{$ckey}); } else { $value = $work_hash{$ckey}; } my $temp = $ckey; $temp =~ s|\s||isg; $output_hash{$temp} = $value; } return \%output_hash; } corrected code - per Sam

Any insight would be appreciated


Replies are listed 'Best First'.
Re: removing the spaces from keys of a complex hash
by samtregar (Abbot) on May 07, 2008 at 16:23 UTC
    It looks like you're on the right track here - walk the list of keys producing a new hash with corrected keys, recursing when you encounter a hash-ref.

    Your downfall is your lack of:

       use strict;

    That's why you didn't notice that you called the key $ckey but referred to it in a number of places as $key:

       if (ref($work_hash{$key}) eq "HASH")


Re: removing the spaces from keys of a complex hash
by pc88mxer (Vicar) on May 07, 2008 at 17:07 UTC
    This is a non-destructive recursive version based on kyle's approach:
    sub unspace_keys { my $hash = shift; if (ref($hash) eq 'HASH') { my $out = {}; for my $k (keys %$hash) { (my $k1 = $k) =~ s/\s+//g; $out->{$k1} = unspace_keys($hash->{$k}); } return $out; } else { return $hash; } } my $new = unspace_keys( \%global ); print Dumper \%global; print Dumper $new;
Re: removing the spaces from keys of a complex hash
by kyle (Abbot) on May 07, 2008 at 16:39 UTC

    I don't see what's wrong with yours. I found it easier to write my own.

    use strict; use warnings; use Data::Dumper; my %global; $global{'title'}='Report for today'; $global{'data 1'}{title}='My data 1 title'; $global{'data 1'}{data}= {}; $global{'data 1'}{footer}='My footer'; $global{'data 2'}{title}='My data 2 title'; $global{'data 2'}{subtitle}='Data 2 subtitle'; $global{'data 2'}{data}= {}; $global{'data 2'}{'data percentages'}= {}; $global{'data 2'}{'data raw'}= {}; $global{'data 2'}{footer}='Footer for data 2'; unspace_keys( \%global ); print Dumper \%global; sub unspace_keys { my $hash_ref = shift; foreach my $spaced_key ( grep /\s/, keys %{ $hash_ref } ) { ( my $new_key = $spaced_key ) =~ s/\s+//g; if ( exists $hash_ref->{$new_key} ) { die "unspaced key '$new_key' exists"; } $hash_ref->{$new_key} = $hash_ref->{$spaced_key}; delete $hash_ref->{$spaced_key}; } unspace_keys( $_ ) for grep { ref $_ eq ref {} } values %{ $hash_ref }; return; } __END__ $VAR1 = { 'data2' => { 'subtitle' => 'Data 2 subtitle', 'data' => {}, 'title' => 'My data 2 title', 'dataraw' => {}, 'datapercentages' => {}, 'footer' => 'Footer for data 2' }, 'title' => 'Report for today', 'data1' => { 'data' => {}, 'title' => 'My data 1 title', 'footer' => 'My footer' } };

    This is (also) a recursive solution. It does not check for cycles, so it will need some work if your input has any. It does check if a new unspaced key already exists, so it won't clobber anything if you have keys like 'psychotherapist' and 'psycho therapist'.

Re: removing the spaces from keys of a complex hash
by alexm (Chaplain) on May 07, 2008 at 19:11 UTC
    Replacing spaces with dashes should produce well-formed XML and makes it harder to have key collisions.
Re: removing the spaces from keys of a complex hash
by jasonk (Parson) on May 08, 2008 at 00:53 UTC

    The easiest way is to use the tools that CPAN provides you with...

    use Data::Visitor::Callback; my $visitor = Data::Visitor::Callback->new( hash => sub { my $new = {}; while ( my ( $key, val ) = each %{ $_ } ) { $key =~ s/\s+//g; $new->{ $key } = $val; } return $new; }, ); my $without_spaces = $visitor->visit( \%global );
    We're not surrounded, we're in a target-rich environment!
Re: removing the spaces from keys of a complex hash
by jwkrahn (Monsignor) on May 07, 2008 at 16:29 UTC
    sub hash_remove_key_space { my $in_hash = shift; %$in_hash = map { ( my $key = $_ ) =~ s/\s+//g; ( $key => $in_hash->{ $_ } ) } keys %$in_hash; }
      The poster is looking for a non-destructive transform - that code modifies the input hash.


Re: removing the spaces from keys of a complex hash
by gwhite (Friar) on May 08, 2008 at 14:02 UTC
    Thanks for the help. Sam got it, in my quickie testing I failed to add use strict, lesson learned. Jason, I am all for using CPAN modules, but that name didn't lend itself to me finding it, and I am not sure loading another module to save 8-10 lines of code was worth it in this case.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://685250]
Approved by kyle
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (10)
As of 2018-06-25 20:03 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (128 votes). Check out past polls.