TheAdam122
TheAdam122

Reputation: 99

How to split a string into multiple hash keys in perl

I have a series of strings for example

my @strings;
$strings[1] = 'foo/bar/some/more';
$strings[2] = 'also/some/stuff';
$strings[3] = 'this/can/have/way/too/many/substrings';

What I would like to do is to split these strings and store them in a hash as keys like this

my %hash;
$hash{foo}{bar}{some}{more} = 1;
$hash{also}{some}{stuff} = 1;
$hash{this}{can}{have}{way}{too}{many}{substrings} = 1;

I could go on and list my failed attempts, but I don't think they add to the value to the question, but I will mention one. Lets say I converted 'foo/bar/some/more' to '{foo}{bar}{some}{more}'. Could I somehow store that in a variable and do something like the following?

my $var = '{foo}{bar}{some}{more}';
$hash$var = 1;

NOTE: THIS DOESN'T WORK, but I hope it only doesn't due to a syntax error.

All help appreciated.

Upvotes: 1

Views: 906

Answers (4)

user10096506
user10096506

Reputation: 1

I took the easy way out w/'eval':

use Data::Dumper;

%hash = ();
@strings = ( 'this/is/a/path', 'and/another/path', 'and/one/final/path' );
foreach ( @strings ) {
    s/\//\}\{/g;
    $str = '{' . $_ . '}';      # version 2: remove this line, and then
    eval( "\$hash$str = 1;" );  #   eval( "\$hash{$_} = 1;" );
}

print Dumper( %hash )."\n";

Upvotes: 0

daxim
daxim

Reputation: 39158

Just use a library.

use Data::Diver qw(DiveVal);
my @strings = (
    undef,
    'foo/bar/some/more',
    'also/some/stuff',
    'this/can/have/way/too/many/substrings',
);
my %hash;
for my $index (1..3) {
    my $root = {};
    DiveVal($root, split '/', $strings[$index]) = 1;
    %hash = (%hash, %$root);
}
__END__
(
    also => {some => {stuff => 1}},
    foo  => {bar => {some => {more => 1}}},
    this => {can => {have => {way => {too => {many => {substrings => 1}}}}}},
)

Upvotes: 0

Dave Cross
Dave Cross

Reputation: 69244

Identical logic to Shawn's answer. But I've hidden the clever hash-walking bit in a subroutine. And I've set the final value to 1 rather than an empty hash reference.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my @keys = qw(
  foo/bar/some/more
  also/some/stuff
  this/can/have/way/too/many/substrings
);

my %hash;

for (@keys) {
  multilevel(\%hash, $_);
}

say Dumper \%hash;

sub multilevel {
  my ($hashref, $string) = @_;

  my $curr_ref = $hashref;
  my @strings = split m[/], $string;
  for (@strings[0 .. $#strings - 1]) {
    $curr_ref->{$_} //= {};
    $curr_ref = $curr_ref->{$_};
  }

  $curr_ref->{@strings[-1]} = 1;
}

Upvotes: 5

shawnhcorey
shawnhcorey

Reputation: 3601

You have to use hash references to walk down thru the list of keys.

use Data::Dumper;

my %hash = ();

while( my $string = <DATA> ){
    chomp $string;
    my @keys = split /\//, $string;
    my $hash_ref = \%hash;
    for my $key ( @keys ){
        $hash_ref->{$key} = {};
        $hash_ref = $hash_ref->{$key};
    }
}
say Dumper \%hash;

__DATA__
foo/bar/some/more
also/some/stuff
this/can/have/way/too/many/substrings

Upvotes: 4

Related Questions