开发者

Perl: What is the easiest way to flatten a multidimensional array?

What's the easiest way t开发者_开发问答o flatten a multidimensional array ?


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


Using List::Flatten seems like the easiest:

use List::Flatten;

my @foo = (1, 2, [3, 4, 5], 6, [7, 8], 9);        
my @bar = flat @foo;  # @bar contains 9 elements, same as (1 .. 9)

Actually, that module exports a single simple function flat, so you might as well copy the source code:

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

You could also make it recursive to support more than one level of flattening:

sub flat {  # no prototype for this one to avoid warnings
    return map { ref eq 'ARRAY' ? flat(@$_) : $_ } @_;
}


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"; }


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";


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.


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.


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;

As of List::MoreUtils 0.426 we have an arrayify function that flattens arrays recursively:

@a = (1, [[2], 3], 4, [5], 6, [7], 8, 9);
@l = arrayify @a; # returns 1, 2, 3, 4, 5, 6, 7, 8, 9

It was introduced earlier but was broken.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜