views:

90

answers:

3

Working on converting an array to another array. In PHP, this is easy, but Perl has some syntax that I am having a hard time getting my head around and understanding.

Here is my loop in Perl:

foreach my $r (@toindex){
    #print Dumper $r;
    %indexed{@$r[0]}{'image_id'} = @$r[0];     #Broken
    %indexed{"@$r[0]"}{'image_id'} = @$r[0];   #Broken
}

Here is my @toindex array

$VAR1 = [
      [
        3638584,
        'Aperture',
        'F13'
      ],
      [
        3638588,
        'Exposure Bias',
        '0 EV'
      ],
      [
        3638588,
        'Focal Length',
        '80.0 mm'
      ],
    ];

And here is what I want to do, but in PHP

foreach($indexrows as $k => $v){
    $indexed[$v['image_id']]['image_id'] = $v['image_id'];     
}

It seems so very simple in PHP, but moving it to Perl is proving to be quite a challenge for me.


Update

Thanks to the help of Sinan Ünür and DVK with that final little pointer, I have a working solution. I'm posting the complete script in case anyone might find some part of it useful in the future.

#!/usr/bin/perl
use strict; use warnings; use DBI; use Data::Dumper;

my $dbh = DBI->connect('dbi:Pg:dbname=database;host=serveraddress','user','password') or die;
my $sth;
my $sql = "SELECT id, field, data FROM table";

my $offset = 0; 
my $increment = 20;
my $toindex;

# This loop here is to solve a problem that was not part of the
# original question. I included it to illustrate the syntax for
# looping a database query
do{
    $sth = $dbh->prepare($sql . " LIMIT " . $increment . " OFFSET " . $offset);
    $sth->execute or die;
    $toindex = $sth->fetchall_arrayref;
    $offset = $offset + $increment;
}while(@$toindex == 0);

# Alternately, if you do not need a loop below is all you need
# $sth = $dbh->prepare($sql);
# $sth->execute or die;
# $toindex = $sth->fetchall_arrayref;

my %indexed;
foreach my $r ( @$toindex ) {
    #print Dumper $r;
    my ($id, $field, $value) = @$r;
    @{ $indexed{ $id } }{('image_id', $field)} = ($id, $value);
}

print Dumper %indexed; 

$dbh->disconnect;
+4  A: 
$indexed{ $r->[0] }{'image_id'} = $r->[0];
hobbs
@hobbs Global symbol "%indexed" requires explicit package name at ./db_index.pl line 79.
Ben Dauphinee
"my %indexed;" is enough to define it.
halkeye
@Ben: you were attempting to use the `%indexed` symbol in your example, so hobbs naturally assumed that you had already declared it.
Ether
@Ether @hobbs Sorry about that. I redeclared that as the %indexed, instead of $indexed.
Ben Dauphinee
+3  A: 

I am going to speculate that you are trying to convert that information to a hash table indexed by the image identifier:

#!/usr/bin/perl

use strict; use warnings;

my $table = [
      [ 3638584 => 'Aperture',      'F13'     ],
      [ 3638588 => 'Exposure Bias', '0 EV'    ],
      [ 3638588 => 'Focal Length',  '80.0 mm' ],
];

my %indexed;

for my $r ( @$table ) {
    @{ $indexed{ $r->[0] } }{('image_id', $r->[1])} = @$r[0,2];
}

use YAML;
print Dump \%indexed;

Output:

E:\Home> t
---
3638584:
  Aperture: F13
  image_id: 3638584
3638588:
  Exposure Bias: 0 EV
  Focal Length: 80.0 mm
  image_id: 3638588

You can write the for loop above less cryptically as:

for my $r ( @$table ) {
    my ($id, $field, $value) = @$r;
    @{ $indexed{ $id } }{('image_id', $field)} = ($id, $value);
}

which might save a lot of headaches a week from now.

See also the Perl Data Structures Cookbook. Perl comes with excellent documentation; use it.

Sinan Ünür
@Sinan I'm using dumper, but I get this result. It's a step forward, but it doesn't look quite right. $VAR1 = 'ARRAY(0x1ce7f88)'; $VAR2 = { 'image_id' => [ 3638584, 'Aperture', 'F13' ], 'ARRAY(0x1ce7f70)' => [ 3638584, 'Exposure Bias', '0 EV' ] };
Ben Dauphinee
@Ben: if you're getting that output, you're not using [Data::Dumper](http://search.cpan.org/perldoc?Data::Dumper).
Ether
@Ether I think he is interpolating array references. @Ben Did you write `$r` instead of `$r->[0]` in `$indexed{ $r->[0] }`? The example works just the same with `Data::Dumper`.
Sinan Ünür
@Sinan I added a print into the loop, and it seems to be setting $r to the same value as @$table, which makes no sense at all. I did a print Dumper $r; to verify that.
Ben Dauphinee
@Ben - the name of them method implies that it returns an array **reference** as opposed to an array, thus you'd need `$toindex = $sth->fetchall_arrayref;` and elsewhere use `@$toindex` to use the array and `$toindex->[$i]` to use its elements. This page provides a good example of using DBI in general and fetchall_arrayref() in particular: http://www.felixgers.de/teaching/perl/perl_DBI.html
DVK
@DVK Thanks for the help. Once again, you've provided a useful bit of info.
Ben Dauphinee
@DVK By useful, I mean pointed out the one thing that I wasn't paying attention to. I was using fetchall_arrayref, I just wasn't setting the $toindex properly.
Ben Dauphinee
+3  A: 

This does not directly answer the question, but it is useful for learning purposes so I am making it CW.

 foreach my $r (@toindex){
     #print Dumper $r;
     %indexed{@$r[0]}{'image_id'} = @$r[0];     #Broken
     %indexed{"@$r[0]"}{'image_id'} = @$r[0];   #Broken
 }
  1. In Perl 5 and earlier, you address an individual element of a hash %hash using the syntax $hash{key} because the element is a scalar.

  2. Hash keys are always stringified. So, used as key to a hash, @$r[0] and "@$r[0]" are identical.

  3. Given a reference to an array $r, there are two ways of accessing its first element. @$r[0] is not wrong, but adding sigils to the front gets tedious after a while. Therefore, I found $r->[0] preferable especially if $r->[0] contains a reference to a nested data structure (not the case here) so I can write $r->[0]{this}[1]{that}.

Sinan Ünür
Accessing an array reference with `@$r[0]` is actually accessing a slice of length one. When evaluated in scalar context, this acts like the `,` operator and returns the last element in the slice. So `@$r[0]` gets you the same thing as `$$r[0]`, but in a convoluted and weird way. Personally, I'd stick with `$r->[0]`, its easier all around.
daotoad