Making a UL from a nested hash

120 views Asked by At

Hi I have a hash that looks somthing like this. This has was generated using a RoseDB call to a database and then condensed using looping :

$data =   'Default'=> {
                        'Record' => {
                                      'PInner' => 1,
                                      'LOuter' => 1,
                                      'POuter' => 1,
                                    },
                        'Elect' => {
                                      'LTurn' => 1,
                                      'RTurn' => 1,
                                      'L'=> 1,
                                       'R'=> 1,
                                    },
                      }

I have to make an unordered list from it which would look something like:

<ul>
 <li>Default
 <ul>
     <li>Record
     <ul>
         <li>PInner</li>
         <li>LOuter</li>
         <li>POuter</li>
     </ul>
     </li>
     <li>Elect
          <ul>
              <li> LTurn </li>
              <li> RTurn </li>
              <li> L     </li>
              <li> R     </li>
          </ul>
     </li>
</ul>
</li>

I can simply loop through this and make a long cluttered piece of code. But the problem arises when i try to make it generic, and instead of having 3 levels i have "n" levels. So So I was think of looping using recursion . Is it possible in anyway to make that happen

Please advice Thanks

2

There are 2 answers

1
choroba On BEST ANSWER

You cannot assign a hash without parentheses (to a hash variable) or curly brackets (to a scalar variable).

This does what you want, just keep in mind that hash keys are not ordered, so the items in the lists might come out in a different order. If it is a problem, use arrays instead of hashes.

#!/usr/bin/perl
use warnings;
use strict;


my %data = ( Default => {
                         Record => {
                                    PInner => 1,
                                    LOuter => 1,
                                    POuter => 1,
                                   },
                         Elect => {
                                   LTurn => 1,
                                   RTurn => 1,
                                   L     => 1,
                                   R     => 1,
                                  },
                        });


listify(0, %data);

sub listify {
    my ($level, %data) = @_;
    print '  ' x $level, "<ul>\n";
    for my $k (keys %data) {
        print '  ' x $level, '  <li>', $k, "</li>\n";
        if (ref $data{$k}) {
            listify($level + 1, %{ $data{$k} });
        }
    }
    print '  ' x $level, "</ul>\n";
}
0
Borodin On

This program will do what I think you need. The required output you show in your question isn't valid HTML so I have created what I think you mean.

Note that a Perl hash is inherently unordered, so your values will not be sorted.

use strict;
use warnings;

my $data =   {
  Default => {
    Record => { PInner => 1, LOuter => 1, POuter => 1 },
    Elect  => { LTurn => 1, RTurn => 1, L => 1, R => 1 },
  },
};

make_ul($data);

sub make_ul {
  my ($data, $level) = (@_, 0);
  my $tab = '  ';
  my $indent = $tab x $level;

  print "$indent<ul>\n";
  while (my ($key, $val) = each %$data) {
    print "$indent$tab<li>$key</li>\n";
    make_ul($val, $level + 1) if ref $val eq 'HASH';
  }
  print "$indent</ul>\n";
}

output

<ul>
  <li>Default</li>
  <ul>
    <li>Elect</li>
    <ul>
      <li>RTurn</li>
      <li>LTurn</li>
      <li>R</li>
      <li>L</li>
    </ul>
    <li>Record</li>
    <ul>
      <li>LOuter</li>
      <li>PInner</li>
      <li>POuter</li>
    </ul>
  </ul>
</ul>