http://www.perlmonks.org?node_id=382901


in reply to Re^2: Re-orderable keyed access structure?
in thread Re-orderable keyed access structure?

No, no. I mean this:

my @actual_data = ( [ ... ], [ ... ], [ ... ], ); my %index_of = ( foo => 0, bar => 1, baz => 2, ); my @key_for = qw( foo bar baz ); my $lookup_by_key = $actual_data[ $index_of{ 'bar' } ]; my $lookup_by_idx = $actual_data[ 1 ]; my $key_from_idx = $key_for[ 1 ];

When you move things around in @actual_data, you need to the move the keys around in exactly the same fashion in @key_for. You also need to collect all keys whose values changed positions and update them in %index_of accordingly. To just swap two elements, this is trivial:

sub swap_elements_by_idx { my ( $i, $j ) = @_; @actual_data[ $i, $j ] = @actual_data[ $j, $i ]; @key_for[ $i, $j ] = @key_for[ $j, $i ]; @index_of{ @key_for[ $i, $j ] } = @index_of{ @key_for[ $j, $i ] }; }

Reformulating this for atomic operations involving more than two elements is trickier, since the order of operations is then significant, but shouldn't be very hard either.

Makeshifts last the longest.

Replies are listed 'Best First'.
Re^4: Re-orderable keyed access structure?
by BrowserUk (Patriarch) on Aug 14, 2004 at 14:58 UTC

    Okay, I get where your going (I think). It work's but carries a fairly high cost.

    I was premature in accepting etcshadow's suggestion. When it came to implementing it, the shine fell away.

    This details the problem(s) as I see it so far:

    Using your technique,

    @actual %indexOf @keyFor 0 [ A_value ] { A => 0 } [ A ] 1 [ B_value ] { B => 1 } [ B ] 2 [ C_value ] { C => 2 } [ C ] 3 [ D_value ] { D => 3 } [ D ] 4 [ E_value ] { E => 4 } [ E ]

    Accessing C via key calls for C to become top item so calling getByKey( 'C' ); does:

    sub getByKey { my $key = shift; my $idx = $indexOf{ $key }; my $value = $actual[ $idx ]; push @actual, splice @actual, $idx, 1; push @keyFor, splice @keyFor, $idx, 1; ## Completely re-build %indexOf $indexOf{ $keyFor[ $_ ] } = $_ for 0 .. $#keyFor; return $value }

    Afterwards, all linkages are maintained:

    @actual %indexOf @keyFor 0 [ C_value ]* { A => 1 }* [ C ]* 1 [ A_value ] { B => 2 }* [ A ]* 2 [ B_value ] { C => 0 }* [ B ]* 3 [ D_value ] { D => 3 }* [ D ]* 4 [ E_value ] { E => 4 }* [ E ]*

    but requires two splices and the for loop is expensive!


    Using etcshadow's layout, if I use splice to reorder @keys, the linkages in the hashes are self maintaining; but:

    @keys %valByKey %keyByValRef 0 [ A ] { A => \A_value } { \A_value => A } 1 [ B ] { B => \B_value } { \B_value => B } 2 [ C ] { C => \C_value } { \C_value => C } 3 [ D ] { D => \D_value } { \D_value => D } 4 [ E ] { E => \E_value } { \E_value => E }

    If accessing C via key, calls for C to become top item getByKey( 'C' ); does?

    sub getByKey { my $key = shift; my $value = $valByKey{ $key }; ## In order to splice the C item to the top ## I need it's index, but I don't have a way ## to go from key 'C' to index? return $value; }

    If there was a version of splice that located the items to remove by its address rather than by index, I would be laughing.


    Using a hybrid of the two:

    @keys %valByKey %idxByVal 0 [ A ] { A => \A_value } { \A_value => 0 } 1 [ B ] { B => \B_value } { \B_value => 1 } 2 [ C ] { C => \C_value } { \C_value => 2 } 3 [ D ] { D => \D_value } { \D_value => 3 } 4 [ E ] { E => \E_value } { \E_value => 4 }

    Accessing C via key calls for C to become top item getByKey( 'C' ); does:

    sub getByKey { my $key = shift; my $value = $valByKey{ $key }; my $idx = $idxByVal{ $value }; push @keys, splice @keys, $idx, 1; ## To maintain linkage, it becomes necessary ## to completely re-build %idxByVal %idxByValue = map{ $valByKey{ $key[ $_ ] } => $_ } 0 .. $#keys; return $value; }

    Afterwards:

    @keys %valByKey %idxByVal 0 [ C ] { A => \A_value } { \A_value => 0 } 1 [ A ] { B => \B_value } { \B_value => 1 } 2 [ B ] { C => \C_value } { \C_value => 2 } 3 [ D ] { D => \D_value } { \D_value => 3 } 4 [ E ] { E => \E_value } { \E_value => 4 }

    One splice and a somewhat more expensive (because of double indirection) loop!

    I'm still looking at the other two suggestions.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

      You will only get away marginally cheaper at best. It's just the way things work in this universe. You need bidirectional lookup on keys and indices, so that's what you'll have to maintain one way or another.

      If the double slice my suggestion requires irks you, you can use a $idx => $key hash instead of an array for %key_for. If you have a million short digit-only keys, hash collisions might ruin your day anyway though.

      Makeshifts last the longest.

        I' not irked by the second slice, just exploring the options. Replacing @keyFor with a %keyFor wouldn't work. Once @array has been reordered, the key=>index mappings need an equivalent reordering (hence the second slice), but that would be impossible (or very expensive) using a hash to store them.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon