http://qs321.pair.com?node_id=1233311

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

Hi all. I wanted to know if it's possible to join string inside the 2D array and store it into 1D array. I know if it's 1D array you can store it in the scalar variable but does this work for 2D array? Here is my code:

use warnings; use strict; use Data::Dumper qw(Dumper); my $clk_new; my @clk_new; my @clk = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); my @clk_output = map [ split /_/, $_ ], @clk; print Dumper \@clk_output; for ( my $c= 0; $c<= $#clk_output; $c++) { for ( my $d = 0; $d <= $#{$clk_output[$c]}; $d++ ) { $clk_new = join '_', $clk_output[$c]; print Dumper $clk_new; push @clk_new, $clk_new; } } print Dumper @clk_new;

When i print this, it will give me this output:

$VAR1 = [ [ 'ux', 'prim', 'clk' ], [ 'ux', 'side', 'clk' ], [ 'ux', 'xtal', 'frm', 'refclk' ] ]; $VAR1 = 'ARRAY(0x619920)'; $VAR1 = 'ARRAY(0x619920)'; $VAR1 = 'ARRAY(0x619920)'; $VAR1 = 'ARRAY(0x625700)'; $VAR1 = 'ARRAY(0x625700)'; $VAR1 = 'ARRAY(0x625700)'; $VAR1 = 'ARRAY(0x6929a0)'; $VAR1 = 'ARRAY(0x6929a0)'; $VAR1 = 'ARRAY(0x6929a0)'; $VAR1 = 'ARRAY(0x6929a0)'; $VAR1 = 'ARRAY(0x619920)'; $VAR2 = 'ARRAY(0x619920)'; $VAR3 = 'ARRAY(0x619920)'; $VAR4 = 'ARRAY(0x625700)'; $VAR5 = 'ARRAY(0x625700)'; $VAR6 = 'ARRAY(0x625700)'; $VAR7 = 'ARRAY(0x6929a0)'; $VAR8 = 'ARRAY(0x6929a0)'; $VAR9 = 'ARRAY(0x6929a0)'; $VAR10 = 'ARRAY(0x6929a0)';

I want to store back this array to be like this:

my @clk_new =  ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk');

May I know if my method is wrong? Thank you in advance.

Replies are listed 'Best First'.
Re: join string in 2D array
by roboticus (Chancellor) on May 03, 2019 at 12:13 UTC

    Newbie95:

    The answer Anonymous Monk said was very good. As stated, you were pretty close *and* a C-style for loop often indicates potential for improvement. Going from the first paragraph to the second, though, has a couple missing steps that may not be obvious, so I'm replying only to fill in a couple of those skipped steps.

    Making the indicated change, and tweaking the call to Dumper gives you:

    for ( my $c= 0; $c<= $#clk_output; $c++) { for ( my $d = 0; $d <= $#{$clk_output[$c]}; $d++ ) { $clk_new = join '_', @{$clk_output[$c]}; push @clk_new, $clk_new; } } print Dumper \@clk_new;

    which gives you (edited for space):

    $VAR1 = [ 'ux_prim_clk', 'ux_prim_clk', 'ux_prim_clk', 'ux_side_clk', 'ux_side_clk', 'ux_side_clk', 'ux_xtal_frm_refclk', 'ux_xtal_frm_refclk', 'ux_xtal_frm_refclk', 'ux_xtal_frm_refclk' ];

    Now you have the result with the problem that there are too many copies of each value. Since join accepts a list, you didn't need the inner loop at all. Removing it give you:

    for ( my $c= 0; $c<= $#clk_output; $c++) { $clk_new = join '_', @{$clk_output[$c]}; push @clk_new, $clk_new; } print Dumper \@clk_new;

    which gives the desired result:

    $VAR1 = [ 'ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk' ];

    But as the Anonymous Monk mentions, a C-style loop frequently gives you the chance for improvements. Rather than looping over the array indices and using the index to fetch the value to operate on, you can loop over the array values using them directly:

    for my $c (@clk_output) { $clk_new = join '_', @$c; push @clk_new, $clk_new; }

    Next, you're creating $clk_new only to use it in the next statement and then throwing it away. So the next improvement is to skip creation of the temporary variable $clk_new, like this:

    for my $c (@clk_output) { push @clk_new, join '_', @$c; }

    In fact, now you're using another variable that you're using only once: $c. If you leave out "my $c" from the for loop, perl will use the $_ variable to hold each value, letting you say:

    for (@clk_output) { push @clk_new, join '_', @$_; }

    Then we come to very common operation: building a new list by performing an operation on each element of another list. It's such a common operation that perl offers (as many languages do) a way to do that a little more directly, the map operator, which gives you the ability to create a new list given a list and a block of code to operate on each element:

    @new_list = map { operation_to_perform_on_each_element } @old_list;

    Like before, inside the block the $_ variable contains the current element from the original list. Making that change gives you the final code that Anonymous Monk gave you:

    @clk_new = map { join '_', @$_ } @clk_output;

    Edit: A couple grammatical things (missing words) and an fix to one of the outputs.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: join string in 2D array
by Anonymous Monk on May 03, 2019 at 10:52 UTC
    $clk_new = join '_', $clk_output[$c];

    You were almost there: $clk_output[$c] is a scalar that happens to contain an array reference, but join doesn't know that, since it takes a list. Dereference the scalar so it is transformed into a list in a function call, and the join works: join '_', @{$clk_output[$c]}.

    You could also use a similar map construct to construct @clk_new with less code (C-style for loop in Perl is usually a sign of potential improvements): my @clk_new = map { join "_", @$_ } @clk_output;

    All code is untested, sorry.
Re: join string in 2D array
by Marshall (Canon) on May 04, 2019 at 20:46 UTC
    Consider the code below....
    use warnings; use strict; use Data::Dumper qw(Dumper); my @clk = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); #my @clk_output = map [ split /_/, $_ ], @clk; # Just as a note: Performance is the same with explict loop. # Every map statement can be expressed as a foreach() loop. # This is the same your previous statement - nothing wrong with it # just demo'ing what the map actually does... my @clk_output; foreach my $clk_line (@clk) { push @clk_output, [split "_",$clk_line]; } print Dumper \@clk_output; my @clk_new; foreach my $row_ref (@clk_output) { push @clk_new, join ("_",@$row_ref); } print Dumper \@clk_new; __END__ $VAR1 = [ [ 'ux', 'prim', 'clk' ], [ 'ux', 'side', 'clk' ], [ 'ux', 'xtal', 'frm', 'refclk' ] ]; $VAR1 = 'ux_prim_clk'; $VAR2 = 'ux_side_clk'; $VAR3 = 'ux_xtal_frm_refclk';
    Could also be coded...this does the same thing:
    use warnings; use strict; use Data::Dumper qw(Dumper); my @clk = ('ux_prim_clk', 'ux_side_clk', 'ux_xtal_frm_refclk'); my @clk_output = map [ split /_/, $_ ], @clk; print Dumper \@clk_output; my @clk_new = map{my $line =join ("_", @$_); $line}@clk_output; print Dumper \@clk_new;
      Could also be coded...this does the same thing:
      ...
      my @clk_new = map{my $line =join ("_", @$_); $line}@clk_output;

      But isn't it also true that the statement
          my @clk_new = map{my $line =join ("_", @$_)}@clk_output;
      would do the same thing? In which case, there seems to be no point to assigning to a lexical within the map block, so we're back to
          my @clk_new = map{ join('_', @$_) }@clk_output;
      or
          my @clk_new = map join('_', @$_), @clk_output;

      Prior to the introduction of the  /r modifier for  s/// substitution in Perl version 5.14 (see Regexp Quote Like Operators in perlop), there was sometimes a need for a map statement like
          map { (my $r = $_) =~ s{ ... }{foo}xms;  $r; }
      to avoid changing the aliased referent of  $_ in a data flow, but it doesn't seem useful here. (I can't tell you how many times I've had to remember to do this when a bunch of unexpected '1' characters suddenly showed up in my output!)

      A similar situation might arise if one wanted to stick a debug print-point
          map { print "... $_ ...";  $_; }
      into the middle of a data flow.


      Give a man a fish:  <%-{-{-{-<

        All completely valid points.

        However, assigning to a simple lexical scalar within a map block is dirt cheap both in terms of memory and execution speed. Perl is probably gonna do something like that internally anyway, it just won't have a "name". If assigning a lexical name makes the code more clear, then why not?

        In this thread, I think the OP has some confusion about maps and foreach loops. So I showed a couple of ways for each significant loop in the code.