Perl: What is the easiest way to flatten a multidi

2019-01-24 03:28发布

问题:

What's the easiest way to flatten a multidimensional array ?

回答1:

Using List::Flatten seems like the easiest:

use List::Flatten;

my @foo = (1, 2, [3, 4, 5], 6, [7, 8], 9);
# @foo contains 6 elements, 2 of them are array references

my @bar = flat @foo;
# @bar contains 9 elements, same as (1 .. 9)

Use List::Flatten::Recursive to do it recursively.



回答2:

One level of flattening using map

$ref = [[1,2,3,4],[5,6,7,8]]; # AoA

@a = map {@$_} @$ref;         # flattens it

print "@a";                   # 1 2 3 4 5 6 7 8


回答3:

The easiest and most natural way, is to iterate over the values and use the @ operator to "dereference" / "unpack" any existing nested values to get the constituent parts. Then repeat the process for every reference value encountered.

This is similar to Viajayenders solution, but works for values not already in an array reference and for any level of nesting:

sub flatten {
  map { ref $_ ? flatten(@{$_}) : $_ } @_;
}

Try testing it like so:

my @l1 = [ 1, [ 2, 3 ], [[[4]]], 5, [6], [[7]], [[8,9]] ];
my @l2 = [ [1,2,3,4,5], [6,7,8,9] ];
my @l3 = (1, 2, [3, 4, 5], 6, [7, 8], 9);  # Example from List::Flatten

my @r1 = flatten(@l1);
my @r2 = flatten(@l1);
my @r3 = flatten(@l3);

if (@r1 ~~ @r2 && @r2 ~~ @r3) { say "All list values equal"; }


回答4:

if data is always like an example, I recommend List::Flatten too.

but data has more than 2 nested array, flat cant't work.

like @foo = [1, [2, [3, 4, 5]]]

in that case, you should write recursive code for it.

how about bellow.

sub flatten {
  my $arg = @_ > 1 ? [@_] : shift;
  my @output = map {ref $_ eq 'ARRAY' ? flatten($_) : $_} @$arg;
  return @output;
}

my @foo = (1, 2, [3, 4, 5, [6, 7, 8]], 9);
my $foo = [1, 2, [3, 4, 5, [6, 7, 8]], 9];
my @output = flatten @foo;
my @output2 = flatten $foo;
print "@output";
print "@output2";


回答5:

The easiest way to flatten a multidimensional array when it includes: 1. arrays 2. array references 3. scalar values 4. scalar references

sub flatten {
   map { ref $_ eq 'ARRAY' ? flatten(@{$_}) :
         ref $_ eq 'SCALAR' ? flatten(${$_}) : $_
   } @_;
}

The other flatten sub answer crashes on scalar references.



回答6:

Something along the lines of:

my $i = 0;

while ($i < scalar(@array)) {
    if (ref @array[$i] eq 'ARRAY') {
        splice @array, $i, 1, @$array[$i];
    } else {
        $i++;
    }
}

I wrote it blindly, no idea if it actually works but you should get the idea.



回答7:

Same as Vijayender's solution but will work on mixed arrays containing arrayrefs and scalars.

$ref = [[1,2,3,4],[5,6,7,8],9,10];
@a = map { ref $_ eq "ARRAY" ? @$_ : $_ } @$ref;
print "@a"

Of course you can extend it to also dereference hashrefs:

@a = map { ref $_ eq "ARRAY" ? @$_ : ref $_ eq "HASH" ? %$_: $_ } $@ref;

or use grep to weed out garbage:

@a = map { @$_} grep { ref $_ eq 'ARRAY' } @$ref;