Converting and printing array of hashes - Perl

62 views Asked by At

I really dont know how to do it so I ended up here.

I want to convert this input:

my @sack_files_1 = (
    'mgenv/1_2_3/parent.dx_environment',
    'mgenv/1_2_3/doc/types.dat',
    'u5env/1_2_3/parent.dx_environment',
    'u5env/1_2_3/doc/types.dat',
);

To this:

my $sack_tree_1 = {
    'mgenv' => {
        '1_2_3' => [ 'parent.dx_environment', 'doc/types.dat' ],
    },
    'u5env' => {
        '1_2_3' => [ 'parent.dx_environment', 'doc/types.dat' ],
    }
};
3

There are 3 answers

5
Sobrique On BEST ANSWER

Something like this should do the trick:

use strict;
use warnings;
use Data::Dumper;

my @sack_files_1 = (
    'mgenv/1_2_3/parent.dx_environment',
    'mgenv/1_2_3/doc/types.dat',
    'u5env/1_2_3/parent.dx_environment',
    'u5env/1_2_3/doc/types.dat',
);

my %sack_tree_1;
foreach (@sack_files_1) {
    my ( $env, $number, @everything_else ) = split('/');
    push( @{ $sack_tree_1{$env}{$number} }, join( "/", @everything_else ) );
}

print Dumper \%sack_tree_1
4
JGNI On
my $sack_tree_1 = {};
foreach my $data (@sack_files_1) {
    my @path = split '/', $data;
    my ($file,$last_part) = pop @path, pop @path; # get the file name and last part of the path
    my $hash_part = $sack_tree_1;
    foreach my $path (@path) { # For every element in the remaining part of the path
        $hash_part->{$path} //= {}; # Make sure we have a hash ref to play with
        $hash_part = $hash_part->{$path} # Move down the hash past the current path element
    }
    $hash_part->{$last_part} = $file; # Add the file name to the last part of the path
 }

This handles all path lengths of 2 or more

0
Borodin On

This will do as you ask. It uses File::Spec::Functions to split each path into its components.

The first two elements of the hash are used directly as hash keys, relying on autovivication to create the necessary hash elements.

A simple push to an implied array reference also autovivifies the lowest-level hash element.

I have used Data::Dump to display the resulting hash. It is not part of the core Perl installation and you may need to install it, but it is much superior to Data::Dumper.

use strict;
use warnings;

use File::Spec::Functions qw/ splitdir catfile /;

my @sack_files_1 = (
  'mgenv/1_2_3/parent.dx_environment',
  'mgenv/1_2_3/doc/types.dat',
  'u5env/1_2_3/parent.dx_environment',
  'u5env/1_2_3/doc/types.dat',
);

my %paths;

for my $path (@sack_files_1) {
  my ($p1, $p2, @path) = splitdir $path;
  push @{ $paths{$p1}{$p2} }, catfile @path;
}

use Data::Dump;
dd \%paths;

output

{
  mgenv => { "1_2_3" => ["parent.dx_environment", "doc\\types.dat"] },
  u5env => { "1_2_3" => ["parent.dx_environment", "doc\\types.dat"] },
}