Bodo
Bodo

Reputation: 172

Looking for an elegant way of conversion of multidimensional arrays into one dimensional arrays in Perl

I'm converting a one dimensional array into a multidimensional array. See the below codefor what I am doing. I'm still learning perl and so want to have an improvement on my code.

My Question: Can I do the conversion in one line?

I think Perl is able to do so. When writing a subroutine it's possible to assign:

    my ($one, $two, $three) = @_;

My code:

    #!/usr/bin/perl -w

    use strict;
    use warnings;
    use Data::Dumper;

    my @oneDim = (
      'COL', 'EUR', 'Fruit', 'apple',
      'HZP', 'USD', 'Fruit', 'banana',
      'HKG', 'USD', 'Food', 'burger',
      'IML', 'USD', 'IT', 'keyboard');

    #  (@fourDim[0],@fourDim[1],@fourDim[2],@fourDim[3]) = @oneDim;
    sub main() {
      my @fourDim = ([],[],[],[]);
      for (my $i = 0; 4 * $i + 3 < scalar(@oneDim); ++$i)
      {
        push @{$fourDim[0]}, $oneDim[$i * 4 + 0];
        push @{$fourDim[1]}, $oneDim[$i * 4 + 1];
        push @{$fourDim[2]}, $oneDim[$i * 4 + 2];
        push @{$fourDim[3]}, $oneDim[$i * 4 + 3];
      }

      print Dumper @fourDim;
    }

    main();

And this is the result:

    $VAR1 = [
      'COL',
      'HZP',
      'HKG',
      'IML'
    ];
    $VAR2 = [
      'EUR',
      'USD',
      'USD',
      'USD'
    ];
    $VAR3 = [
      'Fruit',
      'Fruit',
      'Food',
      'IT'
    ];
    $VAR4 = [
      'apple',
      'banana',
      'burger',
      'keyboard'
    ];

I found the reverse conversion here: https://stackoverflow.com/a/8611430/2764334

But I could not find an elegant way to convert a one dimensional array into a multidimensional. In my case I know that there is always the same order (code, currency, type, product)

Upvotes: 1

Views: 50

Answers (2)

ysth
ysth

Reputation: 98398

Almost one line, but easy to understand:

use List::MoreUtils 'part';
my $i = 0;
my @fourDim = part { $i++ / 4 } @oneDim;

As usual, reduce can do it in one line but is harder to understand:

use List::Util 'reduce';
my $fourDim = reduce { push @{ $a->[ @$a && @{$a->[-1]} < 4 ? -1 : @$a ] }, $b; $a } [], @oneDim;

though those are the ways that would work just as well on lists of unknown size; given an array @oneDim, you can just do:

my @fourDim = map [ @oneDim[$_*4..$_*4+3] ], 0..@oneDim/4-1;

Upvotes: 3

perreal
perreal

Reputation: 98028

Splice may help:

my @oneDim = ( 
    'COL', 'EUR', 'Fruit', 'apple',
    'HZP', 'USD', 'Fruit', 'banana',
    'HKG', 'USD', 'Food', 'burger',
    'IML', 'USD', 'IT', 'keyboard');

my @fourDim;
my $k = @oneDim / 4;
push @fourDim, [ splice @oneDim, 0, $k ] while @oneDim;
print join(", ", @$_), "\n" for @fourDim;

this gives:

COL, EUR, Fruit, apple
HZP, USD, Fruit, banana
HKG, USD, Food, burger
IML, USD, IT, keyboard

Upvotes: 1

Related Questions