Nice :).
First:
What what? And please don't name your hashrefs $array ;-).my $out .= "var "; my $out .= "var ";
Just so we're clear. Your idea will work when the keys identify uniquely the associated values, so something like this would be valid perl, but invalid input:
The values in the Shapes array can either be hashes describing the shape, or an array of points, and you need to look inside the hashes to know the type of shape and the associated members.(Shapes => [ { Type => 'Circle', Diameter => 2, Center => [0,1] }, { Type => 'Square', Side => 3, Pos => [4,8] }, [ {x => 1, y => 1}, {x => 3, y => 1}, {x => 4, y => 2}, {x => 2, y + => 2} ] ] );
As can be seen, my crude approach of merging each element into a %giant_hash, while great for data if everything within the array hashes is a hash, falls down when arrays are encountered.Actually the hash is the issue, in %giant_hash = (%giant_hash, %$array); if there are keys in %$array that are already present in %giant_hash, they will overwrite them. This means that the kids of Marge Keefe will erase the kids of Tony Jones.
A hash in perl can only hold a single value (scalar) for each key. That value can be a reference that holds other values, but perl won't just do that on its own when you "merge" hashes, so in %giant_hash, you will only have the fname, last_name, occupation and set of kids. So you can't actually merge the hash that way before iterating over it.
The reason you merged the hashes in the first place is that they are of the same type, so contain similar data. That's also true for the kids (basically they have a name, an age, and might be vaccinated), so you should "merge" them, and show that "kids" may contrary may contain an arbitrary number of elements of type "kid". Which would make your output look like:
var is a HASH with 5 keys the keys are 'age', 'fname', 'kids', 'last_name', 'occupation' key 'kids' is an ARRAY containing HASHREFs: the keys are 'age', 'name', 'vaccinated' key 'vaccinated' is a SCALAR key 'age' is a SCALAR key 'name' is a SCALAR key 'fname' is a SCALAR key 'age' is a SCALAR key 'occupation' is a HASH with 2 keys the keys are 'title', 'years_on_job' key 'title' is a SCALAR key 'years_on_job' is a SCALAR key 'last_name' is a SCALAR
Here is my attempt at solving your problem. There are of course many ways to do it, keeping the list of keys down to the current point rather than a reference to the current level might be a better way to work (you don't have to provide the output hash as a parameter), but I just went where my fingers took me :D
use v5.14; use strict; use warnings; use Data::Dump qw( pp ); use YAML; sub introspect { my ($data, $output) = @_; if (ref $data eq 'ARRAY') { my $sub_out = ($output->{'ARRAY'} //= {}); introspect($_, $sub_out) for @{ $data }; } elsif (ref $data eq 'HASH') { my $hash_out = $output->{"HASH"} //= {}; for my $key (keys %$data) { my $sub_out = ($hash_out->{"$key"} //= {}); introspect($_, $sub_out) for $data->{$key}; } } elsif (ref $data) { $output->{ref($data).'REF'}=1; } else { $output->{SCALAR}=1; } } my @array = ({fname => 'bob', last_name => 'smith', foo => [\*main]}, {fname => 'tony', last_name => 'jones', age => 23, kids => [ {first_name => 'cheryl', middle_name => 'karen', age => 24 }, {name => 'jimmy', age => 17 } ], }, {fname => 'janet', last_name => 'marcos', foo => {}, occupation => { title => 'trucker', years_on_job => 12} }, {fname => 'Marge', last_name => 'Keefe', kids => [ {name => 'kate', age => 7, vaccinated => 'yes'}, {name => 'kim', age => 5} ] }); my %out; introspect(\@array, \%out); say pp \%out; say YAML::Dump(\%out);
{ ARRAY => { HASH => { age => { SCALAR => 1 }, fname => { SCALAR => 1 }, foo => { ARRAY => { GLOBREF => 1 }, HASH => {} }, kids => { ARRAY => { HASH => { age => { SCALAR => 1 }, first_name => { SCALAR => 1 }, middle_name => { SCALAR => 1 }, name => { SCALAR => 1 }, vaccinated => { SCALAR => 1 }, }, }, }, last_name => { SCALAR => 1 }, occupation => { HASH => { title => { SCALAR => 1 }, years_on_job => { SCALAR = +> 1 } }, }, }, }, } --- ARRAY: HASH: age: SCALAR: 1 fname: SCALAR: 1 foo: ARRAY: GLOBREF: 1 HASH: {} kids: ARRAY: HASH: age: SCALAR: 1 first_name: SCALAR: 1 middle_name: SCALAR: 1 name: SCALAR: 1 vaccinated: SCALAR: 1 last_name: SCALAR: 1 occupation: HASH: title: SCALAR: 1 years_on_job: SCALAR: 1
Edit: you can add this case to handle things like \\\\\{};
elsif (ref $data eq 'REF') { introspect($$data, ($output->{'REF'} //= {})); }
In reply to Re: How to improve introspection of an array of hashes
by Eily
in thread How to improve introspection of an array of hashes
by nysus
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |