-1

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' ],
    }
};
huksha
  • 13
  • 5

3 Answers3

1

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
Sobrique
  • 52,974
  • 7
  • 60
  • 101
  • 3
    `my ( $env, $number, $rest ) = split m#/#, $_, 3;` – TLP Nov 12 '14 at 13:41
  • All this time with perl - and I never realised there was a `LIMIT` option to split. – Sobrique Nov 12 '14 at 13:43
  • The simple and portable way of separating a path into steps is `File::Spec::splitpath`. This solution won't work outside a Linux-like file system. – Borodin Nov 12 '14 at 14:03
  • 1
    It's also worth noting that `Data::Dump`, written by the author of the awesome `LWP` library, is far superior to `Data::Dumper`. It's not part of core Perl at present, but it's a pure-Perl module and will install on anything. – Borodin Nov 12 '14 at 14:06
  • 1
    @Sobrique The LIMIT option is useful sometimes. – TLP Nov 12 '14 at 14:29
1

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"] },
}
Borodin
  • 126,100
  • 9
  • 70
  • 144
0
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

JGNI
  • 3,933
  • 11
  • 21
  • I think you're needing a few more `%`s in there. – Sobrique Nov 12 '14 at 13:51
  • Really must read the question. This answers a completely different problem :-( – JGNI Nov 12 '14 at 13:56
  • @JGNI: Splitting a path on slashes `/` is a non-expressive and Linux-specific way of splitting paths into their components. The [`File::Spec`](https://metacpan.org/module/File::Spec) module has always been part of core Perl 5, and the canonical way is to use `File::Spec->splitpath` – Borodin Nov 12 '14 at 14:14
  • @Borodin Yer I know that, If you track down my one and only CPAN distribution You'll see I use File::Spec, but given the data and no other info I went for the simpler option. Also from the info given it's not 100% clear that those are file paths. – JGNI Nov 12 '14 at 17:34