title: File path into JSON data structure
body: <p>I'm doing a disk space report that uses <code>File::Find</code> to collect cumulative sizing in a directory tree. </p>
  
  <p>What I get (easily) from <code>File::Find</code> is the directory name. </p>
  
  <p>e.g.:</p>
  
  <pre><code>/path/to/user/username/subdir/anothersubdir/etc
  </code></pre>
  
  <p>I'm running <code>File::Find</code> to collect sizes beneath: </p>
  
  <pre><code>/path/to/user/username
  </code></pre>
  
  <p>And build a cumulative size report of the directory and each of the subdirectories. </p>
  
  <p>What I've currently got is:</p>
  
  <pre><code>while ( $dir_tree ) {
     %results{$dir_tree} += $blocks * $block_size;
     my @path_arr = split ( "/", $dir_tree ); 
     pop ( @path_arr );
     $dir_tree = join ( "/", @path_arr ); 
  }
  </code></pre>
  
  <p>(And yes, I know that's not very nice.).</p>
  
  <p>The purpose of doing this is so when I <code>stat</code> each file, I add it's size to the current node and each parent node in the tree. </p>
  
  <p>This is sufficient to generate:</p>
  
  <pre><code>username,300M
  username/documents,150M
  username/documents/excel,50M
  username/documents/word,40M
  username/work,70M
  username/fish,50M,
  username/some_other_stuff,30M
  </code></pre>
  
  <p>But I'd like to now turn that in to JSON more like this:</p>
  
  <pre><code>{ 
      "name" : "username",
      "size" : "307200",
      "children" : [
          { 
              "name" : "documents",
              "size" : "153750",
              "children" : [
                    { 
                        "name" : "excel",
                        "size" : "51200"
                     }, 
                     {
                         "name" : "word",
                         "size" : "81920"
                     }
               ]
           }
      ]
  }
  </code></pre>
  
  <p>That's because I'm intending to do a D3 visualisation of this structure - loosely based on <a href="http://mbostock.github.io/d3/talk/20111116/pack-hierarchy.html" rel="nofollow">D3 Zoomable Circle Pack</a></p>
  
  <p>So my question is this - what is the neatest way to collate my data such that I can have cumulative (and ideally non cumulative) sizing information, but populating a hash hierarchically.</p>
  
  <p>I was thinking in terms of a 'cursor' approach (and using <code>File::Spec</code> this time): </p>
  
  <pre><code>use File::Spec; 
  my $data;
  my $cursor = \$data; 
  foreach my $element ( File::Spec -&gt; splitdir ( $File::Find::dir ) ) {
     $cursor -&gt; {size} += $blocks * $block_size;
     $cursor = $cursor -&gt; {$element} 
  }
  </code></pre>
  
  <p>Although... that's not quite creating the data structure I'm looking for, not least because we basically have to search by hash key to do the 'rolling up' part of the process. </p>
  
  <p>Is there a better way of accomplishing this?</p>
  
  <p>Edit - more complete example of what I have already:</p>
  
  <p></p>
  
  <pre><code>#!/usr/bin/env perl
  
  use strict;
  use warnings;
  
  use File::Find;
  use Data::Dumper;
  
  my $block_size = 1024;
  
  sub collate_sizes {
      my ( $results_ref, $starting_path ) = @_;
      $starting_path =~ s,/\w+$,/,;
      if ( -f $File::Find::name ) {
          print "$File::Find::name isafile\n";
          my ($dev,   $ino,     $mode, $nlink, $uid,
              $gid,   $rdev,    $size, $atime, $mtime,
              $ctime, $blksize, $blocks
          ) = stat($File::Find::name);
  
          my $dir_tree = $File::Find::dir;
          $dir_tree =~ s|^$starting_path||g;
          while ($dir_tree) {
              print "Updating $dir_tree\n";
              $$results_ref{$dir_tree} += $blocks * $block_size;
              my @path_arr = split( "/", $dir_tree );
              pop(@path_arr);
              $dir_tree = join( "/", @path_arr );
          }
      }
  }
  
  my @users = qw ( user1 user2 );
  
  foreach my $user (@users) {
      my $path = "/home/$user";
      print $path;
      my %results;
      File::Find::find(
          {   wanted   =&gt; sub { \&amp;collate_sizes( \%results, $path ) },
              no_chdir =&gt; 1
          },
          $path
      );
      print Dumper \%results;
  
      #would print this to a file in the homedir - to STDOUT for convenience
      foreach my $key ( sort { $results{$b} &lt;=&gt; $results{$a} } keys %results ) {
         print "$key =&gt; $results{$key}\n";
      }
  }
  </code></pre>
  
  <p>And yes - I know this isn't portable, and does a few somewhat nasty things. Part of what I'm doing here is trying to improve on that. (But currently it's a Unix based homedir structure, so that's fine). </p>