StevieD
StevieD

Reputation: 7443

Recursively building nested hash from a simple array

Got this:

my @list = <one two three>;

my %hash;
my $item1 = @list.shift;
%hash{$item1} = {$item1 => 1};

my $item2 = @list.shift;
%hash{$item1} = {$item2 => 1};

my $item3 = @list.shift;
%hash{$item1}{$item2} = {$item3 => 1};

say %hash;

Outputs this desired data structure:

{one => {two => {three => 1}}}

Obviously, this would be better if it were recursive, so I wrote this:

sub get-hash(%parent-hash, $last-item, *@list) {
    my $item = @list.shift;
    %parent-hash{$last-item} = { $item => 1 };
    get-hash(%parent-hash{$last-item}, $item, @list) if @list;

    return %parent-hash<root>;
}

%hash = get-hash({}, 'root', @list2);

Output:

{one => {two => {three => 1}}}

Though it works, it feels inelegant, especially having to pass in a root argument to the sub and then removing it. Any suggestions?

Upvotes: 4

Views: 170

Answers (3)

wamba
wamba

Reputation: 4465

If you want ’key-value’ structure Pair

my @list = <one two three>;
say [=>] |@list, 1

If you really need Hash

<one two three>
andthen |$_, 1
andthen .reduce: sub ($x,$y) is assoc<right> { %( $x => $y ) }\
andthen .say

or

<one two three>
andthen 1, |.reverse
andthen .reduce:  { %( $^y => $^x ) }\
andthen .say

Upvotes: 2

Jonathan Worthington
Jonathan Worthington

Reputation: 29454

In the upcoming Raku version, there's a neat way to do this:

use v6.e.PREVIEW;
my @list = <one two three>;
my %hash;
%hash{||@list} = 1;
say %hash;

The || indicates that you want to use the list as multi-dimensional hash keys.

If you want to stick to things in the current released language versions, you can still call the operator directly, since it's only the syntax sugar that is missing:

my @list = <one two three>;
my %hash;
postcircumfix:<{; }>(%hash, @list) = 1;
say %hash

The output in either case is as you wish:

{one => {two => {three => 1}}}

Upvotes: 9

StevieD
StevieD

Reputation: 7443

OK, playing around with the order of the arguments helped simplify things a bit:

sub get-hash(@list, %parent-hash = {}, $last-item = 'root') {
    my $item = @list.shift;
    %parent-hash{$last-item} = { $item => 1 };
    get-hash(@list, %parent-hash{$last-item}, $item) if @list;

    return %parent-hash<root>;
}

my @list2 = <one two three>;
%hash = get-hash(@list2);

Upvotes: 2

Related Questions