0

I'm reading a json file in perl and having trouble using the results.

I want to know how many views are present in the json file. there should be 2

my $data = decode_json($json);
print Dumper($data);

my @tmp=$data->{'views'};
my $nviews=scalar @tmp;
print "nviews : $nviews\n";

gives me

$VAR1 = {
    'views' => [
        {
            'key' => 0,
            'value' => {
                'ptr_wrapper' => {
                     'data' => {
                         'width'    => 776,
                         'height'   => 1024,
                         'id_view'  => 0,
                         'filename' => '000118800_15821618907.jpg',
                         'id_pose'  => 0,
                         'id_intrinsic' => 13,
                         'local_path' => '/'
                     },
                     'id' => 2147483649
                 }
             }
        },
        {
             'key'   => 1,
             'value' => {
                 'ptr_wrapper' => {
                      'data' => {
                          'id_pose'      => 1,
                          'id_intrinsic' => 11,
                          'filename'     =>  '000132800_22050281512.jpg',
                          'id_view'      => 1,
                          'local_path'   => '/',
                          'width'        => 850,
                          'height'       => 1024
                      },
                      'id' => 2147483650
                  }
             }
         }
    ]
};
nviews : 1

thanks in advance for any help.luc

Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • Possible duplicate of [How do I determine the number of elements in an array reference?](http://stackoverflow.com/questions/5885794/how-do-i-determine-the-number-of-elements-in-an-array-reference) – Matt Jacob Jan 26 '16 at 18:54

2 Answers2

5
my $views = $data->{views};
my $nbViews = scalar @$views;

$data->{views} is a reference to an array, not an array. Store the reference, and dereference it when needed.

Alternatively:

my @views = @{$data->{views}};
my $nbViews = scalar @views;

As pointed out by Borodin, note that this alternative makes a copy of the original array (potentially using a lot of memory). In some cases that may be what you actually want to do (if you need to modify it while leaving the original one intact, though note this would not be a deep copy), but most often (especially if you just want to count the items or iterate over them), you probably don't want to do that and just stick to keeping a reference (as in the first code snippet above).

Community
  • 1
  • 1
jcaron
  • 17,302
  • 6
  • 32
  • 46
  • 1
    What about `my $nbViews = scalar @{$data->{views}}` wouldn't that work as well? – Andreas Louv Jan 26 '16 at 19:56
  • @Borodin Point taken. – Andreas Louv Jan 26 '16 at 19:59
  • 4
    @dev-null: That `scalar` is unnecessary, so I would choose `my $nbViews = @{ $data->{views} }` if I didn't need to access the array elsewhere, but in practice that's very unlikely – Borodin Jan 26 '16 at 20:02
  • 2
    @jcaron: I think you should make it clear that your *Alternatively* makes a duplicate of the array, which is often unnecessary and may be devastating if `@$data` is very large. It is certainly not something to do if you just need a count of elements in the array – Borodin Jan 26 '16 at 20:18
4

When you write this

my $data = decode_json($json);

my @tmp = $data->{'views'};

you are creating a Perl array with a single element $tmp[0] that is identical to $data->{'views'}.

Your $data->{'views'} is spot on, so you have correctly understood how to access the views element of the hash referred to by your $data variable, and as long as the hash key views looks like a Perl identifier you can forget the quotes around it

But the values of Perl hashes and arrays are always scalar, so you must dereference them if you need to access the array, and the same applies to any substructures that you need to access

If you are dealing with complex data structures, it is usually best to keep a temporary scalar variable that tracks where you are within the structure. For instance, I would write

my $views = $data->{views};

Now I have a simple array reference to code with. I can dereference it to an array with @$views, and I can access its elements with $views->[0] etc.

Here's a rewrite of your own code that does what I have described. I have also used Data::Dump to reformat the data that you show. It is generally far more concise and readable than the output from Data::Dumper, but it isn't a core module so you will probably have to install it

I pull out $value->{ptr_wrapper} as a lexical variable $wrapper, and in turn $wrapper->{data} as lexical variable $data. The rest of the code becomes so much simpler, as I could otherwise be writing stuff like

say $data->{views}[0]{value}{ptr_wrapper}{data}{filename}


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

my $data = {
  views => [
    {
      key => 0,
      value => {
        ptr_wrapper => {
          data => {
            filename     => "000118800_15821618907.jpg",
            height       => 1024,
            id_intrinsic => 13,
            id_pose      => 0,
            id_view      => 0,
            local_path   => "/",
            width        => 776,
          },
          id => 2147483649,
        },
      },
    },
    {
      key => 1,
      value => {
        ptr_wrapper => {
          data => {
            filename     => "000132800_22050281512.jpg",
            height       => 1024,
            id_intrinsic => 11,
            id_pose      => 1,
            id_view      => 1,
            local_path   => "/",
            width        => 850,
          },
          id => 2147483650,
        },
      },
    },
  ],
};

my $views = $data->{views};
my $nviews = scalar @$views;

print "nviews : $nviews\n\n";

for my $view ( @$views ) {

    my $key     = $view->{key};
    my $value   = $view->{value};
    my $wrapper = $value->{ptr_wrapper};
    my $data    = $wrapper->{data};

    say "key      = $key";
    say "ID       = $wrapper->{id}";
    say "filename = $data->{filename}";
    say "height   = $data->{height}";
    say "width    = $data->{width}";
    print "\n";
}

output

nviews : 2

key      = 0
ID       = 2147483649
filename = 000118800_15821618907.jpg
height   = 1024
width    = 776

key      = 1
ID       = 2147483650
filename = 000132800_22050281512.jpg
height   = 1024
width    = 850
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • perfect.just a note , I wouldn't use my $data = $wrapper->{data}; as it overwrites the original $data and I need it later. – lulutreizequinze Jan 27 '16 at 12:28
  • @lulutreizequinze: It doesn't “overwrite” the original `$data`. It introduces a new `$data` that disappears at the end of the enclosing block -- the `for` loop. After that the old `$data` and its original value are accessible once again. You shouldn't need the outermost value of `$data` inside the loop -- that is the point of *scoping* – Borodin Jan 27 '16 at 14:12