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

Sorry - this is probably a very bad way of doing things, but I want to be able to change a hash key by changing a hash value.
#!/usr/bin/perl -w use strict; use Data::Dumper; my $this = {'data'=>{}, 'name'=>'this'}; my $that = {'name'=>"that", 'value'=>"123"}; $this->{data}->{ $that->{'name'} } = $that; print Dumper($this); $that->{'name'} = "them"; print Dumper($this); This gives me $VAR1 = { 'name' => 'this', 'data' => { 'that' => { 'value' => '123', 'name' => 'that' } } }; $VAR1 = { 'name' => 'this', 'data' => { 'that' => { ^^^^ 'value' => '123', NOT CHANGED 'name' => 'them' } ^^^^ } CHANGED };

Is there any way of changing BOTH 'that' to 'them'? If not is there another way of creating a hash that is keyed on subsequent values in the hash that can be changed?

Thanks

Mark

Replies are listed 'Best First'.
Re: Dynamic hash keys
by moritz (Cardinal) on Aug 17, 2010 at 10:15 UTC
    Is there any way of changing BOTH 'that' to 'them'?

    Sure, just do two assignments. Or in the case of hash keys, delete the old one, and add the contents back in with a new name.

    But the real answer is that you should try to avoid redundancy in mutable data structures. Depending on your actual problem, there is a good chance that there's a better way to build your data structure, but without knowing what kind of operations you want to do with them, we can't tell.

    You can also read up on normalization forms. That's a concept that's often used in the area of databases, but it (partially) applies to any mutable data structure.

    Perl 6 - links to (nearly) everything that is Perl 6.
      Thanks for the reply. I don't think my requirements are particularly unusual, just that I am not doing it right.

      I have a sample that has multiple tests. When a test is assigned to a sample it has a temporary id. After it is verified it is given a permanent id.

      I want to put the tests in the hash indexed by their ids for quick retrival by their id and change the id when necessary.

      Did I mention that sample and test are objects (kind of - as I don't have any OO training they are quite quasimodo in their OOness). Which means if I normalise and the test id is set in the sample object then I cannot access that id from the test object.

      I am probably making things too hard for myself. I have read some of the OO tutorials, but when I get to the bits that I don't know and (definitely) need the language of the tutorial suddenly becomes "encrypted". I'll keep trying.

      Thanks again

        Rather than changing the ID, why not give the test another attribute ('validated' for example)?

        Then you don't have to worry about collisions with pre-existing IDs when validating.

        foreach my $currentTestID (keys %$tests) { $tests->{$currentTestID}{validated} = validate($tests->{$currentTe +stID}); }
        sub doValidatedTests { my $tests = shift; doTest($_) foreach ( grep {$tests{$_}{validated}} keys %$tests ); }

        When the IDs don't change, you're free to tell the test object what its ID is when you create it, and it will be valid until you drop the test from your hash and the test itself gets garbage collected shortly thereafter.

        One solution is to keep separate hashes for objects with temporary id, and for those which are are confirmed.

        When one object is verified, you delete it from the hash that is keyed by temporary ID, assign it its new, permanent ID, and insert it into the hash that holds on the confirmed objects.

        Perl 6 - links to (nearly) everything that is Perl 6.
Re: Dynamic hash keys
by JavaFan (Canon) on Aug 17, 2010 at 11:11 UTC
    You cannot change the value of hash keys. You can, however, do a delete and an insert:
    $this->{data}{them} = delete $this->{data}{$that->{name}}; $that->{name} = "them";
    It may of course be better to take a course of action where you don't duplicate data.
Re: Dynamic hash keys
by murugu (Curate) on Aug 17, 2010 at 12:04 UTC
    I dont know whether i understood the OP question correctly or not. I have written a snippet which will change all the "that" in keys/values to "them". Correct me if i am wrong.

    Added some more elements in to that hash for navigational purpose.

    #!/usr/bin/perl -w use strict; use Data::Dumper; my $this = {'data'=>{}, 'name'=>'this'}; my $that = {'name'=>"that", 'value'=>"123"}; $this->{data}->{ $that->{'name'} } = $that; $this->{data}->{that}->{abc}->{that}="them"; $this->{data}->{abc}->{that}->{that}->{that}->{abc}->{that} = "them"; $this->{data}->{abc}->{that}->{that}->{that} = "abc"; print Dumper $this; my $struct = &change($this,"that","them"); print Dumper($struct); sub change { my ($struct,$input,$output)=@_; if ((ref $struct)=~/HASH/){ foreach my $t (keys %{$struct}) { if (ref $struct->{$t}) { if ($t eq $input) { my $k = $struct->{$t}; $struct->{$output}=$k; delete $struct->{$t}; &change($struct->{$output},$input,$output); } &change($struct->{$t},$input,$output); } else { $struct->{$t} = $output if ($struct->{$t} eq $input +); if ($t eq $input) { my $k = $struct->{$t}; $struct->{$output}=$k; delete $struct->{$t}; } } } } return $struct; }

    Regards,
    Murugesan Kandasamy
    use perl for(;;);

Re: Dynamic hash keys
by markdibley (Sexton) on Aug 17, 2010 at 12:36 UTC
    Thank you for all the replies. As usual there are many ways in PERL to deal with this immediate problem. However, I think my question has now changed to what is the best way to index data.

    In my data model I have a Sample that has multiple Tests. When I am working with a Sample object I want to be able to link my Test objects to it in such a way that I can:-
    • ask the Sample object to pass over a Test object using a test id e.g. $sample->get_test("a1b2c3").
    • return an array of Test objects in order of a value (such as date) which is an attribute of Test.

    So I guess my data is structured just like a normalised database and I am looking for the mechanism (not the key) for joining the objects together.

    Sorry if I am not making much sense, but thanks for your help.
Re: Dynamic hash keys
by locked_user sundialsvc4 (Abbot) on Aug 17, 2010 at 19:50 UTC

    The best way to think of a hash is that it is “an index.”   And, what is stored in that index is a reference to some thing, such as an object.   You can (very efficiently) have more than one reference to any thing, and so, I suggest that this is what you should do.

    Create a Perl object (in any suitable fashion) that contains all of the information you need.   Then, add references to it into one or more purpose-built hashes (or lists, or what have you), according to the number of ways that you need to be able to find it.   All of these references are just that:   “references to” the one object-instance that you have created.   (They are not “copies.”)   Any of them can be used to find the object, and when you do so, the same object instance is found in all cases.

    The object will continue to exist until no more references to it remain.

    It will, of course, be advantageous to encapsulate this logic into a single Perl package, allowing the “implementation guts” to be neatly hidden away.   (After all, you simply want to be able to Do Things™ and It Just Works™ and, unless you are looking at that one package, Never Mind How.™)