Incognito has asked for the wisdom of the Perl Monks concerning the following question:
I couldn't find this problem on the Q&A section, so I'm wondering
how to flatten a multi (x)-dimensional array into one array? We do not know
the structure of the array up front, but ultimately want it to end up as one
array containing only elements that contain data.
Rather than having this array:
$VAR1 = [
'',
''
];
$VAR1 = [
'',
''
];
$VAR1 = [
'funct1',
'',
''
];
$VAR1 = [
'',
''
];
$VAR1 = [
'',
[
'',
[
'funct2a',
'funct2b',
''
],
''
],
''
];
$VAR1 = 'funct3';
$VAR1 = 'funct4';
$VAR1 = 'funct5';
$VAR1 = 'funct6';
$VAR1 = 'funct7';
I would like to make the above array (printed using Data::Dumper) into the following:
$VAR1 = 'funct1';
$VAR1 = 'funct2a';
$VAR1 = 'funct2b';
$VAR1 = 'funct3';
$VAR1 = 'funct4';
$VAR1 = 'funct5';
$VAR1 = 'funct6';
$VAR1 = 'funct7';
Is this possible? Of course, I just don't know how...
Re: How to flatten an x-dimensional array?
by abstracts (Hermit) on Mar 12, 2002 at 03:34 UTC
|
And now for something completely lispish :-)
my @ar = grep length, flatten(@array);
print Dumper(@ar);
sub flatten{
return unless @_;
my ($car,@cdr) = @_;
if(ref($car) eq 'ARRAY'){
flatten(@$car, @cdr);
} else {
$car, flatten(@cdr);
}
}
| [reply] [d/l] |
|
To Perl-ize this wonderful Lisp solution *grins*
sub flatten {
return unless @_;
my $val = shift;
return flatten(@$val, @_) if UNIVERSAL::isa($val, 'ARRAY');
return $val, flatten(@_);
}
------ We are the carpenters and bricklayers of the Information Age. Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement. | [reply] [d/l] |
|
++abstracts -- ($car, @cdr) indeed! :-)
The Sidhekin
print "Just another Perl ${\(trickster and hacker)},"
| [reply] [d/l] [select] |
•Re: How to flatten an x-dimensional array?
by merlyn (Sage) on Mar 12, 2002 at 14:44 UTC
|
Hmm. So far, far too many recursive solutions, when a simple iterative solution works fine:
sub flatten {
my @result;
while (@_) {
my $first = shift;
if (UNIVERSAL::isa($first, "ARRAY")) { # fixed, thanks particle
unshift @_, @$first;
} else {
push @result, $first;
}
}
@result;
}
-- Randal L. Schwartz, Perl hacker | [reply] [d/l] |
|
that is, it works fine if you call UNIVERSAL::isa properly ;)
it should be:
if (UNIVERSAL::isa($first, "ARRAY")) {
~Particle ;̃
| [reply] [d/l] |
|
| [reply] |
|
sub flatten {
my @flattened; # Will be in reverse order
while (@_) {
my $last = pop;
if (UNIVERSAL::isa($last, "ARRAY")) {
push @_, @$last;
}
else {
push @flattened, $last;
}
}
return reverse @flattened;
}
| [reply] [d/l] |
|
I think you missed a reverse there:
push @_, @$last;
needs to be
push @_, reverse @$last;
since you are reversing the result. Trivial example: $last = [1,2,3,4,5] must flatten as 5, 4, 3, 2, 1 so it can be reversed in the final result to 1, 2, 3, 4, 5 again.
-- Randal L. Schwartz, Perl hacker | [reply] [d/l] [select] |
|
Re: How to flatten an x-dimensional array?
by AidanLee (Chaplain) on Mar 12, 2002 at 03:24 UTC
|
consider a recursive solution:
my @flattened_array = flatten($nested_array_ref);
sub flatten {
my $array = shift;
my @results = ();
foreach my $element ( @$array ) {
if( ref $element eq 'ARRAY' ) {
push @results, flatten($element);
}
elsif( $element ) {
push @results, $element;
}
}
return @results;
}
update: I investigated per rob_au's concerns with the following code using the above subroutine:
use Data::Dumper;
use strict;
my $nested_array_ref = [
'',
[
'a',
[
'b',
'c'
],
'',
'd'
],
'value'
];
my @flattened_array = flatten($nested_array_ref);
print Dumper(\@flattened_array);
and my output was this:
$VAR1 = [
'a',
'b',
'c',
'd',
'value'
];
the example i gave explicitly passed an array_ref into the function to begin with, and @results is local to the subroutine, and does not get blown away during recursion. | [reply] [d/l] [select] |
|
This doesn't appear to work, primarily because of the clearing of the @results array with each iteration through the flatten function - In addition to this, when called with a unreferenced array, an error is generated when run under strict because of the dereferencing of a string in the foreach loop (@$array).
Update - Hrmmm, I'll dig into this a bit further and see why my tests failed - Thanks for the update AidanLee :-)
perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'
| [reply] [d/l] [select] |
(jeffa) Re: How to flatten an x-dimensional array?
by jeffa (Bishop) on Mar 12, 2002 at 04:00 UTC
|
use strict;
use Data::Dumper;
my $ref = [
['',''],
['',''],
['funct1','',''],
['',''],
['',['',['funct2a','funct2b',''],''],''],
'funct3',
'funct4',
'funct5',
'funct6',
'funct7',
];
my @flat = Dumper($ref) =~ /'(\w+)/g;
print join("\n",@flat),"\n";
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] |
Re: How to flatten an x-dimensional array?
by Anonymous Monk on Mar 12, 2002 at 05:19 UTC
|
map and grep are your friends.
sub flatten { map ref eq 'ARRAY' ? flatten(@$_) : $_, grep defined && length, @_ }
Cheers,
-Anomo | [reply] [d/l] [select] |
Re: How to flatten an x-dimensional array?
by rob_au (Abbot) on Mar 12, 2002 at 03:24 UTC
|
Yes, this is certainly possible - The following simple example uses recursion to iterate through the data object.
my @array = (
[
'', ''
],
[
'', ''
],
[
'funct1', '', ''
],
[
'', ''
],
[
'',
[
'',
[
'funct2a', 'funct2b', ''
],
''
],
''
],
'funct3',
'funct4',
'funct5',
'funct6',
'funct7',
);
print join "\n", _flatten( @array ), "\n";
{
my @results;
sub _flatten {
foreach (@_) {
if (ref $_ eq 'ARRAY') {
_flatten( @{ $_ } );
next;
}
push @results, $_ if $_;
}
return @results;
}
}
If however, this code is to be used in a production environment, I would consider either rewriting the above to include depth checking and/or a shift towards a iterative rather than recursive loop, or alternatively, reevaluate the code generating this complex data structure in the first place.
perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print' | [reply] [d/l] |
|
use overload;
use Carp;
sub flatten {
my $ref = shift;
my $seen = shift || "";
return map {
(UNIVERSAL::isa($_,"ARRAY")) ? do {
my $ref_str=overload::StrVal($_);
Carp::croak "Cant flatten cyclic data structure !"
if index($seen,$ref_str)>=0;
flatten($_,$seen."$ref_str");
} : $_ #change to #} : $_ ? $_ :()# to simulate grep
} @$ref;
}
require Data::Dumper;
print Data::Dumper::Dumper([
flatten([qw(array of elements),
[qw(and arrays)],
bless [qw(even blessed ones)],"FooArray"
])
]);
__END__
$VAR1 = [
'array',
'of',
'elements',
'and',
'arrays',
'even',
'blessed',
'ones'
];
Fixed spelling mistake.
Yves / DeMerphq
--
When to use Prototypes?
Advanced Sorting - GRT - Guttman Rosler Transform | [reply] [d/l] |
|
|