create a hierarchical list from a text file

Discussion in 'Perl Misc' started by Michael Friendly, Jan 15, 2006.

  1. I need to create hierarchical menu files from text input in a simple form
    of comma delimited lines with fields (topic, url, description) like

    Top1,,Desc1
    Item1
    Item11,url11,Description11
    Item12,url12,Description12
    Item13,url12,Description13
    Item2,url2,Description2
    Item21,url21,Description21
    Item3,url3,Description3
    Item31,url31,Description31
    Item32,url32,Description32
    Top2,,Desc2
    Item1,url1,Description1
    Item11,url11,Description11
    Item12,url12,Description12
    Item2,url2,Description2
    Item21,url21,Description21

    where the number of leading tabs in each item implicitly indicates level
    in the tree. The output must be in the form of an explicitly structured,
    comma-separated list, [ item, item, ... item ] where each item is either
    a simple item or another list, e.g.,
    [ item, [ item, item, ... item ], ... item ].
    Missing fields in an item are represented by 'null', so the example
    would appear as:

    [
    ['Top1', null, null
    , ['Item1', null, null
    , ['Item11', 'url11', 'Description11']
    , ['Item12', 'url12', 'Description12']
    , ['Item13', 'url12', 'Description13']
    ]
    , ['Item2', 'url2', 'Description2'
    , ['Item21', 'url21', 'Description21']
    ]
    , ['Item3', 'url3', 'Description3'
    , ['Item31', 'url31', 'Description31']
    , ['Item32', 'url32', 'Description32']
    ]
    ]
    , ['Top2', null, null
    , ['Item1', 'url1', 'Description1'
    , ['Item11', 'url11', 'Description11']
    , ['Item12', 'url12', 'Description12']
    ]
    , ['Item2', 'url2', 'Description2'
    , ['Item21', 'url21', 'Description21']
    ]
    ]
    ]

    I'm reading the items and creating a list of hashes with the subroutine
    below, but I can't figure out how to get from there to my desired
    output. There must be some perl modules I can use. Can someone help?

    sub read_items {
    $file = shift;
    open(IN, $file) || die("cant open $file\n");
    $nitems = 0;
    undef @items;
    while( $line = <IN>) {
    chomp($line);
    next if $line =~ /^#/;
    $line =~ s/(\t*)//;
    $level = length($1); # level in tree
    next unless $line;
    $nitems++;
    ($title, $url, $desc) = split(/,\s*/, $line);
    $desc =~ s/\'/\\'/g; # escape quotes in e.g., Murphy's Law
    $item = {
    level => $level,
    title => $title,
    url => $url,
    desc => $desc,
    };
    push @items, $item;
    }
    close IN;
    print STDERR "Read $nitems items\n";
    return @items;
    }


    --
    Michael Friendly Email:
    Professor, Psychology Dept.
    York University Voice: 416 736-5115 x66249 Fax: 416 736-5814
    4700 Keele Street http://www.math.yorku.ca/SCS/friendly.html
    Toronto, ONT M3J 1P3 CANADA
     
    Michael Friendly, Jan 15, 2006
    #1
    1. Advertising

  2. Michael Friendly

    Guest

    Michael Friendly wrote:
    > Top1,,Desc1


    > ['Top1', null, null


    Did you mean

    ['Top1', null, Desc1
     
    , Jan 15, 2006
    #2
    1. Advertising

  3. Michael Friendly

    Guest

    Michael Friendly wrote:
    > I need to create hierarchical menu files from text input in a simple form
    > of comma delimited lines with fields (topic, url, description) like
    >
    > Top1,,Desc1
    > Item1
    > Item11,url11,Description11
    > Item12,url12,Description12
    > Item13,url12,Description13
    > Item2,url2,Description2
    > Item21,url21,Description21
    > Item3,url3,Description3
    > Item31,url31,Description31
    > Item32,url32,Description32
    > Top2,,Desc2
    > Item1,url1,Description1
    > Item11,url11,Description11
    > Item12,url12,Description12
    > Item2,url2,Description2
    > Item21,url21,Description21
    >
    > where the number of leading tabs in each item implicitly indicates level
    > in the tree. The output must be in the form of an explicitly structured,
    > comma-separated list, [ item, item, ... item ] where each item is either
    > a simple item or another list, e.g.,
    > [ item, [ item, item, ... item ], ... item ].
    > Missing fields in an item are represented by 'null', so the example
    > would appear as:
    >
    > [
    > ['Top1', null, null
    > , ['Item1', null, null
    > , ['Item11', 'url11', 'Description11']
    > , ['Item12', 'url12', 'Description12']
    > , ['Item13', 'url12', 'Description13']
    > ]
    > , ['Item2', 'url2', 'Description2'
    > , ['Item21', 'url21', 'Description21']
    > ]
    > , ['Item3', 'url3', 'Description3'
    > , ['Item31', 'url31', 'Description31']
    > , ['Item32', 'url32', 'Description32']
    > ]
    > ]
    > , ['Top2', null, null
    > , ['Item1', 'url1', 'Description1'
    > , ['Item11', 'url11', 'Description11']
    > , ['Item12', 'url12', 'Description12']
    > ]
    > , ['Item2', 'url2', 'Description2'
    > , ['Item21', 'url21', 'Description21']
    > ]
    > ]
    > ]
    >


    (snipped)

    One way:

    use Text::CSV;
    use Data::Dumper;

    my $csv = Text::CSV->new();

    sub parse
    {
    my $status = $csv->parse(shift);
    if ($status)
    {
    my @fields = $csv->fields;
    return (3 == grep length $_, @fields)
    ? @fields
    : ($fields[0], 'null', 'null');
    }
    else
    {
    return undef;
    }
    }


    my @stack;
    while (<DATA>)
    {
    chomp;
    my $level = 0;
    $level = length $1 if s/^(\t+)//;
    my $data = [ parse($_) ];
    push @{$stack[$level]}, $data;
    $stack[$level+1] = $data;
    }

    print Dumper $stack[0];

    __DATA__
    Top1,,Desc1
    Item1
    Item11,url11,Description11
    Item12,url12,Description12
    Item13,url12,Description13
    Item2,url2,Description2
    Item21,url21,Description21
    Item3,url3,Description3
    Item31,url31,Description31
    Item32,url32,Description32
    Top2,,Desc2
    Item1,url1,Description1
    Item11,url11,Description11
    Item12,url12,Description12
    Item2,url2,Description2
    Item21,url21,Description21


    --
    Hope this helps,
    Steven
     
    , Jan 16, 2006
    #3
  4. Michael Friendly <> wrote:


    > sub read_items {
    > $file = shift;



    You should not use global variables without a good reason.

    my $file = shift;


    > open(IN, $file) || die("cant open $file\n");



    You should include the *reason* why the open() failed in your message:

    open(IN, $file) || die("cant open '$file' $!");


    > undef @items;



    If you weren't using a global variable there, then you wouldn't
    need to clear it out each time.

    You should have

    use strict;

    in all of your Perl programs, then declare each variable at its first use:

    my @items;



    > $line =~ s/(\t*)//;
    > $level = length($1); # level in tree



    You should never use the dollar-digit variables unless you
    have ensured that the match *succeeded*, otherwise there
    will be a stale, left-over value from some previous match.


    my $level = length($1) if $line =~ s/(\t*)//;


    But you don't even need regular expressions at all:

    my $level = $line =~ tr/\t//d;



    > $item = {
    > level => $level,
    > title => $title,
    > url => $url,
    > desc => $desc,
    > };
    > push @items, $item;



    You don't need the $item temporary variable if you just us
    an anonymous hash constructor in the first place:

    push @items, {
    level => $level,
    title => $title,
    url => $url,
    desc => $desc,
    };


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Jan 16, 2006
    #4
  5. Michael Friendly

    Eric Bohlman Guest

    Tad McClellan <> wrote in
    news::

    > my $level = length($1) if $line =~ s/(\t*)//;
    >
    >
    > But you don't even need regular expressions at all:
    >
    > my $level = $line =~ tr/\t//d;


    In this case it would be better to use a regex since the OP should have
    anchored it to the beginning of the line so that only leading tabs signify
    level. Without the anchor, the data can't contain embedded tabs without
    throwing the level calculations off, and IMHO it's best not to create such
    "unwritten law" constraints.

    my $level = length($1) if $line =~ s/^(\t*)//;
     
    Eric Bohlman, Jan 16, 2006
    #5
  6. Michael Friendly

    Anno Siegel Guest

    <> wrote in comp.lang.perl.misc:
    > Michael Friendly wrote:
    > > Top1,,Desc1

    >
    > > ['Top1', null, null

    >
    > Did you mean
    >
    > ['Top1', null, Desc1
    >


    I noticed that, too, while I was still deciding whether to deal with
    the posting or not. It tipped the scale.

    This kind of inconsistency makes dealing with a post potentially twice
    as time-consuming, because every aspect may have to be considered under
    two (or four, or eight...) variant readings.

    The effort in answering a post grows exponentially with the poster's
    sloppiness.

    Anno
    --
    If you want to post a followup via groups.google.com, don't use
    the broken "Reply" link at the bottom of the article. Click on
    "show options" at the top of the article, then click on the
    "Reply" at the bottom of the article headers.
     
    Anno Siegel, Jan 17, 2006
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Saunders
    Replies:
    0
    Views:
    388
    John Saunders
    Jul 15, 2004
  2. Michel Couché

    Hierarchical Drop down list

    Michel Couché, Jan 22, 2005, in forum: ASP .Net
    Replies:
    2
    Views:
    4,801
    Michel Couché
    Jan 22, 2005
  3. Scott Brady Drummonds

    Hierarchical Schema File Organization

    Scott Brady Drummonds, Nov 17, 2004, in forum: XML
    Replies:
    1
    Views:
    468
    Scott Brady Drummonds
    Nov 19, 2004
  4. Anonieko Ramos

    How do you create a drop-down hierarchical Data Grid?

    Anonieko Ramos, Nov 14, 2004, in forum: ASP .Net Datagrid Control
    Replies:
    0
    Views:
    246
    Anonieko Ramos
    Nov 14, 2004
  5. Baiju K U

    Create a hierarchical data grid

    Baiju K U, Feb 9, 2004, in forum: ASP .Net Web Controls
    Replies:
    1
    Views:
    198
    Ken Cox [Microsoft MVP]
    Feb 9, 2004
Loading...

Share This Page