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

后端 未结 7 1661
北荒
北荒 2020-12-16 13:27

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

相关标签:
7条回答
  • 2020-12-16 14:07

    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(@$_) : $_ } @_;
    }
    
    0 讨论(0)
  • 2020-12-16 14:07

    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
    
    0 讨论(0)
  • 2020-12-16 14:09

    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 讨论(0)
  • 2020-12-16 14:12

    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";
    
    0 讨论(0)
  • 2020-12-16 14:15

    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.

    0 讨论(0)
  • 2020-12-16 14:19

    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"; }
    
    0 讨论(0)
提交回复
热议问题