ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:

I'm building a hash, %student, whose key is a student ID, and whose value is a reference to an array of course descriptions of every course they've taken.

I want to print each student ID, and their course numbers ... and a lot of extra information about each course that can be found using the course number but is expensive to do.

Many students will have taken the same courses, and I only want to gather that extra information once per course. So I keep another hash, %catalog, whose key is the course number and whose value is "a lot of extra information that was expensive to gather". If exists $catalog{$course_number}, I needn't look up the extra information again.

Now I want for $student{$student_id} to be a reference to an array of references to the key/value pair stored at $catalog{$course_number}. I'm having a lot of trouble concocting the Perl grammar to create this.

# if this course ID hasn't yet been added to the catalog if (!exists $catalog{$course_id}) { # add course ID and complex description to catalog $catalog{$course_id} = 1; } # add a reference to the catalog entry to the student's course array push @{$students{$student_id}}, \$catalog{$course_id};
When I look at %students in the debugger with the 'x' command, this is how it looks:
0 10002538 1 HASH(0x1d3a850) 'SCALAR(0x1d21458)' => 1 'SCALAR(0x1d3a868)' => 1 2 681462925 3 HASH(0x1bcea58) 'SCALAR(0x1010cc0)' => 1 'SCALAR(0x1d21458)' => 1 'SCALAR(0x1d215c0)' => 1 'SCALAR(0x1d21608)' => 1 'SCALAR(0x1d216f8)' => 1 'SCALAR(0x1d3a868)' => 1 'SCALAR(0x1d3ac70)' => 1
What I expected to be a course number is coming out 'SCALAR(0x1d3ac70)'.

(In the time it's taken to write this, it occurred to me I could store the course number, rather than a catalog reference, in the student's array of courses. But assuming there *might* be a reason to do it as I described, how do you do it?

Replies are listed 'Best First'.
Re: How to make a reference to a hash cell
by haukex (Archbishop) on Apr 28, 2018 at 08:18 UTC

    I agree with hippo that the keys of the %catalog hash make a decent reference, and that's probably how I would have done it, although those "references" are only like symbolic references. If you want to use "hard" references and have everything stored in one data structure, that's possible too, although there is a caveat: If you store the values of %catalog as elements of the $student{$student_id} array, you lose the key (the course number). One common solution is to additionally store the key as part of the value (often a hash reference). In the following, I'm using the property of Foreach Loops that the loop iterator variable is an alias, so that I can overwrite elements of the array by assigning to the iterator variable.

    use warnings; use strict; use Data::Dump; my %expensive_lookup = ( math01 => { instructor=>'alice' }, sci99 => { instructor=>'bob' }, gym55 => { instructor=>'carol' }, cs101 => { instructor=>'larry' }, ); my %students = ( sid01 => [qw/ math01 gym55 /], sid007 => [qw/ gym55 sci99 /], sid42 => [qw/ cs101 math01 /], ); my %catalog = (); for my $student_id (keys %students) { for my $course_number ( @{ $students{$student_id} } ) { if ( not exists $catalog{$course_number} ) { $catalog{$course_number} = $expensive_lookup{$course_number}; $catalog{$course_number}{course_number} = $course_number; } $course_number = $catalog{$course_number}; } } dd \%catalog, \%students; __END__ do { my $a = { cs101 => { course_number => "cs101", instructor => "larry" }, gym55 => { course_number => "gym55", instructor => "carol" }, math01 => { course_number => "math01", instructor => "alice" }, sci99 => { course_number => "sci99", instructor => "bob" }, }; ( $a, { sid007 => [$a->{gym55}, $a->{sci99}], sid01 => [$a->{math01}, $a->{gym55}], sid42 => [$a->{cs101}, $a->{math01}], }, ); }
      Thanks - good illustration.

      (Never heard of Data::Dump -- much nicer than Dumper.)

Re: How to make a reference to a hash cell
by hippo (Archbishop) on Apr 28, 2018 at 07:57 UTC
    Now I want for $student{$student_id} to be a reference to an array of references to the key/value pair stored at $catalog{$course_number}.

    I don't see the need for that. Why not just store the key in the array and then use that to look up the value from %catalog whenever you need it?

    push @{$students{$student_id}}, $course_id; # ... then to retrieve later ... for my $cid (@{$students{$student_id}}) { print "Course ID $cid, Tutor: $catalog{$cid}{tutor}\n"; # or whate +ver }
      Yes, I came to that eventually! Straightforward.
Re: How to make a reference to a hash cell (OOP)
by LanX (Saint) on Apr 28, 2018 at 12:30 UTC
    Reading between the lines ...

    I think you might be interested to do this in an object oriented way with a Student and a Course class.

    For an object $student you could call a method ->enroll to add a course.

    Something like print $_->instructor for $student->courses would become straight forward.

    A method $student->timetable would be easily implemented.

    And if you decide one day to switch to another representation of the underlying data (like SQL instead of hashes) you'll just need to change the corresponding classes.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Wikisyntax for the Monastery

Re: How to make a reference to a hash cell
by Marshall (Canon) on Apr 28, 2018 at 22:24 UTC
    I basically agree with the posts from hippo, haukex and LanX

    There is no need to put a reference to a course hash table in the student structure.
    Put the course hash key instead.
    A Perl hash lookup is very fast..

    If the number of students or courses is "small", there won't be a performance problem.
    "small" in this situation is like 100,000.
    If one of the tables is more than that, then an SQL DB will yield better performance.

Re: How to make a reference to a hash cell
by Anonymous Monk on Apr 27, 2018 at 23:42 UTC
    \$catalog{$course_id} is a reference to the value. Perl has no possibility to reference a hash-value pair.