Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

chomp any data structure recursively

by Juerd (Abbot)
on Dec 21, 2001 at 00:25 UTC ( #133596=snippet: print w/ replies, xml ) Need Help??

Description:

19:50 <@Petruchio> Right now I'm inclined to code up a subroutine to overload chomp, so that it'll chomp every scalar value it finds recursively throughout a nested data structure.

I could not refrain myself from coding it immediately, so here it is :)

Update (200112202209+0100) It _does_ work, and doesn't use the outer $_. The line containing "#### <<< :-)" explains why. (for-modifier). merlyn was right about this not being easy to maintain. Sorry about that, let's just hope it doesn't need maintenance, then.

Update (200112211546+0100) gbarr uses defined() in his not-recursive version. I should have done so too, so I fixed that mistake. Also made it work with references to references.

sub mychomp; # Needed so mychomp can be used without parens
             # from within mychomp
sub mychomp {
    ref eq 'ARRAY'  ? do { mychomp $_ for @$_ } :
    ref eq 'HASH'   ? do { mychomp $_ for values %$_ } :
    ref eq 'REF'    ? do { mychomp $$_ } :
    ref eq 'SCALAR' ? eval { defined && chomp $$_ } :
    ref || defined && chomp for @_ #### <<< :-)
}

# Useful stuff ends here :)
# Testing stuff starts here :)
$foo = "scalar\n";
@foo = (["first\n", "second\n", "third\n"], [[ "FIRST\n", "SECOND\n", 
+"THIRD\n"],
        [{xyzzy => "1st\n"}],{ xyzzy => "1<sup>st</sup>\n"} ],
        "Just testing\n", \ $foo, \ \ \ $foo, \ [ "Testing :)\n" ],
        [[[[[[[[[[[[[[[[[ "DEEEEEPER!\n" ]]]]]]]]]]]]]]]]]
);

mychomp @foo;
use Data::Dumper;
print Data::Dumper->Dump(\@foo);
# oh, and $foo is chomped too, of course
Comment on chomp any data structure recursively
Download Code
Re: chomp any data structure recursively
by merlyn (Sage) on Dec 21, 2001 at 01:13 UTC
    Yours is bugged. It looks at the outer $_ in all those ref calls. Just happens to work.

    Here's a non-recursive version:

    sub mychomp { while (@_) { eval { chomp $_[0] }, shift, next unless ref; (push @_, @{shift @_}), next if ref $_[0] eq 'ARRAY'; (push @_, values %{shift @_}), next if ref $_[0] eq 'HASH'; shift; } }

    -- Randal L. Schwartz, Perl hacker


    update: I didn't claim it was a working version. Thanks for the updates in the thread. {grin}

    Also, I didn't see the unusual multi-line backwards-for in the original code, so it appears to be less buggy than I thought. {sigh}

      Which $_ is the outer one?
      The refs should see the values of @_. I can't think of a reason why they wouldn't (help me out here :)

      In short, this is the pseudocode:
      sub mychomp { for (@_){ $_ is iterable && mychomp it || $_ is a scalar ref && try to chomp its scalar || $_ is not any other reference && chomp it } }
      I'd be quite surprised my code works with many different structures if it's bugged.

      Could you please give an example of what could go wrong? (of course accompanied by an explanation :)

      Anyway, thanks for pointing out that it's bugged.

      2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

        Looking at just this part:
        sub mychomp { ref eq 'ARRAY' ...
        That's ref of $_, which you have not yet set up, so it's the outer $_ from the caller. Luckily, you didn't test your subroutine with something that needed ref-ARRAY treatment as an argument. {grin}.

        -- Randal L. Schwartz, Perl hacker

      Errr, yours just happens not to work.
      use Data::Dumper; sub mychomp { while (@_) { eval {chomp $_[0] }, shift, next unless ref ; (push @_, @{shift @_}), next if ref $_[0] eq 'ARRAY'; (push @_, values %{shift @_}), next if ref $_[0] eq 'HASH'; shift; } } my $foo = "scalar\n"; my @foo = ( [ "bar\n", "foo\n", "xyzzy\n", { blaat => "hee hee\n", hihi => "hoho\n" } ], {qw(a b c d e f)}, "w00t\n", \$foo, \ "read-only\n" ); mychomp(@foo); print Dumper(\@foo); __END__ $VAR1 = [ 'ARRAY(0x80f3764)', 'HASH(0x81495a8)', 'w00t', 'SCALAR(0x8131fe8)', 'SCALAR(0x813e78c)' ];

      Reason: (Chmrr) Duplicate

      For more information on this node visit: this

      I think your version is butchering array references, and it doesn't handle scalar references. I think this works:
      sub mychomp { while (@_) { for (shift) { ref or (eval { chomp if defined }, last); ref eq 'SCALAR' and (eval { chomp $$_ if defined $$_ }, last); ref eq 'ARRAY' and (do { push @_, map \$_, @$_ }, last); ref eq 'HASH' and (do { push @_, map \$_, values %$_ }, last); } } }
      Question, though, why are we eval'ing the scalar chomps? chomp warns on undef, but does it fail under some condition? (update: I think I answered my own question...its for constant values which can't be chomped).

      I tracked this thread for a while. merlyn incorrectly calls posted code buggy and supplies replacement code that is buggy and his node gets voted to +6. He doesn't update his node to acknowledge either mistake when it is pointed out and his node still climbs 3 more points to +9 after both errors were pointed out in followups. Now that's what I call personality voting!

        Well, then, he doesn't deserve a best node of the day award for this one, but to be fair, he did acknowlege one (update: now both) of his mistakes, and all I did was take his idea and "fix" it (update: and gbarr just blew mine away :), so although it was buggy starting out, it was an interesting idea to begin with, and I don't begrudge him a few XP for that (as if I cared seriously about XP at all)...

        If someone posts a "you blew it" reply, I'd expect a followup "Hey, you're right" and maybe a fix (depending on time available -- many of us are busy, you know), but if someone (e.g., me) posts a fix, then what's the point in replying to that, especially if you're busy?

Re: chomp any data structure recursively
by gbarr (Monk) on Dec 21, 2001 at 19:11 UTC
    I thought I would comment because not one of the solutions given handle a reference to a reference. So here is my non-recursive solution.

    sub mychomp { while (@_) { for (shift) { push @_, grep { ref || eval { defined && chomp; 0 }; } ref eq 'ARRAY' ? @$_ : ref eq 'HASH' ? values %$_ : ref eq 'SCALAR' ? $$_ : ref eq 'REF' ? $$$_ : $_ } } }

    Of course even this is not complete as there could be a reference to a GLOB, which could contain a SCALAR, HASH and ARRAY. But who would use those :)

      I thought I would comment because not one of the solutions given handle a reference to a reference. So here is my non-recursive solution.
      I forgot to handle references to references, will be fixed in a few minutes
      I also admit i forgot to test if it's defined, so I'll change that right away.
      Thanks!

      2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

Back to Snippets Section

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (13)
As of 2015-05-29 21:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    In my home, the TV remote control is ...









    Results (593 votes), past polls