The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
NAME
    Tie::Hash::MultiKey - multiple keys per value

SYNOPSIS
      use Tie::Hash::MultiKey;

      $thm = tie %hash, qw(Tie::Hash::MultiKey) ,@optionalext;
      $thm = tied %hash;

      untie %hash;

      ($href,$thm) = new Tie::Hash::MultiKey;

      $hash{'foo'}        = 'baz';
            or
      $hash{'foo', 'bar'} = 'baz';
            or
      $array_ref = ['foo', 'bar'];
      $hash{ $array_ref } = 'baz';

      print $hash{foo};     # prints 'baz'
      print $hash{bar};     # prints 'baz'

      $array_ref = ['fuz','zup'];
      $val = tied(%hash)->addkey('fuz' => 'bar');
      $val = tied(%hash)->addkey('fuz','zup' => 'bar');
      $val = tied(%hash)->addkey( $array_ref => 'bar');

      print $hash{fuz}      # prints 'baz'

      $array_ref = ['foo', 'bar'];
      $val = tied(%hash)->remove('foo');
      $val = tied(%hash)->remove('foo', 'bar');
      $val = tied(%hash)->remove( $array_ref );

      $val = tied(%hash)->delkey(); alias for above

      @ordered_keys = tied(%hash)->keylist('foo')
      @allkeys_by_order = tied(%hash)->keylist();
      @slotlist = tied(%hash)->slotlist($i);
      @ordered_vals = tied(%hash)->vals();

      $num_vals = tied(%hash)->size;
      $num_vals = tied(%hash)->consolidate;

      ($newRef,$newThm) = tied(%hash)->clone();
      $newThm = tied(%hash)->copy(tied(%new),@optionalext);

      All of the above methods can be accessed as:

      i.e.  $thm->consolidate;

DESCRIPTION
    Tie::Hash::MultiKey creates hashes that can have multiple ordered keys
    for a single value. As shown in the SYNOPSIS, multiple keys share a
    common value.

    Additional keys can be added that share the same value and keys can be
    removed without deleting other keys that share that value.

    STORE..ing a value for one or more keys that already exist will
    overwrite the existing value and add any missing keys to the key group
    for that value.

    WARNING: multiple key values supplied as an ARRAY to STORE and DELETE
    operations are passed by Perl as a single string separated by Perl's $;
    multidimensional array seperator. i.e.

            $hash{'a','b','c'} = $something;
      or
            @keys = ('a','b','c');
            $hash{@keys} = $something'

    This really means $hash{join($;, 'a','b','c')};

    Tie::Hash::MultiKey will do the right thing as long as your keys DO NOT
    contain binary data the may include the $; separator character.

    It is recommended that you use the ARRAY_REF construct to supply
    multiple keys for binary data. i.e.

            $hash{['a','b','c']} = $something;
      or
            $keys = ['a','b','c'];
            $hash{$keys} = $something;

    The ARRAY_REF construct is ALWAYS safe.

    * $thm = tie %hash,'Tie::Hash::MultiKey' ,%optional_ex
        Ties a %hash to this package for enhanced capability and returns a
        method pointer.

          my %hash;
          my $thm = tie %hash,'Tie::Hash::MultiKey';

        Extension of this module is discussed in detail below.

    * $thm = tied %hash;
        Returns a method pointer for this package.

    * untie %hash;
        Breaks the binding between a variable and this package. There is no
        affect if the variable is not tied.

        REMEMBER that if you have created a reference to the tied hash,
        untie will not work until that binding is broken. This means that
        the object will not be destroyed or garbage collected and the memory
        will not be reclaimed.

        i.e WRONG

          $thm = tie %h, 'Tie::Hash::MultiKey';
          ... code ...
          untie %h;

                RIGHT

          $thm = tie %h, 'Tie::Hash::MultiKey';
          ... code ...
          undef $thm;
          untie %h;

    * ($href,$thm) = new 'Tie::Hash::MultiKey' ,%optional_ex
        This method returns an UNBLESSED reference to an anonymous tied
        %hash.

          input:        none
          returns:      unblessed tied %hash reference,
                        object handle

        To get the object handle from \%hash use this.

                $thm = tied %{$href};

        In SCALAR context it returns the unblessed %hash pointer. In ARRAY
        context it returns the unblessed %hash pointer and the package
        object/method pointer.

    * $val = $thm->addkey('new_key' => 'existing_key');
        Add one or more keys to the shared key group for a particular value.

          input:        array or array_ref,
                        existing_key
          returns:      hash value
                    or  dies with stack trace

        Dies with stack trace if existing_key does not exist OR if new key
        belongs to another key set.

        Arguments may be a single SCALAR, ARRAY, or ARRAY_REF

    * $val = ->remove('key');
    * $val = ->delkey('key'); alias for above
        Remove one or more keys from the shared key group for a particular
        value If this operation removes the LAST key, then it performs a
        DELETE which is the same as:

                delete $hash{key};

        remove returns a reverse list of the removed value's by key

          i.e.  @val = remove(something);
           or   $val = remove(something);

        Arguments may be a single SCALAR, ARRAY or ARRAY_REF

    * @ordered_keys = $thm->keylist('foo');
    * @allkeys_by_order = $thm->keylist();
        Returns all the keys in the group that includes the KEY 'foo' in the
        order that they were added to the %hash;

        If no argument is specified, returns all the keys in the %hash in
        the order that they were added to the %hash

          input:        key or EMPTY
          returns:      @ordered_keys

          returns:      () if $key is not in the %hash

    * @keys = $thm->slotlist($i);
        Returns one key from each key group in position $i.

          i.e.
                $thm = tie %hash, 'Tie::Hash::MultiKey';

                $hash{['a','b','c']} = 'one';
                $hash{['d','e','f']} = 'two';
                $hash{'g'}           = 'three';
                $hash{['h','i','j']} = 'four';

                @slotkeys = $thm->slotlist(1);

          will produce ('b','e', undef, 'i')

        All the keys at index '1' for the groups to which they were added,
        in the order which the FIRST KEY in the group was added to the
        %hash. If there is no key in the specified slot, an undef is
        returned for that position.

    * $thm->size;
        Returns the number of ITEMS in the hash (not the number of keys).
        Should be faster than ... scalar @values

    * $thm->consolidate;
        USE WITH CAUTION

        Consolidate all keys with the same values into common groups.

          returns: number of consolidated key groups

    @ordered_vals = $thm->vals();
        Return a list of values in the order they were added.

    * ($href,$thm) = $thm->clone();
        This method returns an UNBLESSED reference to an anonymous tied
        %hash that is a deep copy of the parent object.

          input:        none
          returns:      unblessed tied %hash reference,
                        object handle

        To get the object handle from \%hash use this.

                $thm = tied %{$href};

        In SCALAR context it returns the unblessed %hash pointer. In ARRAY
        context it returns the unblessed %hash pointer and the package
        object/method pointer.

          i.e.
                $newRef = $thm->clone();

                $newRref->{'a','b'} = 'content'

                $newThm = tied %{$newRef};

    * $new_thm = $thm->copy(tie %new,'Tie::Hash::MultiKey');
        This method deep copies a MultiKey %hash to another new %hash. It
        may be invoked on an existing tied object handle or a reference to a
        tied %hash.

          input:        object handle OR reference to tied %hash
          returns:      object handle / method pointer

          i.e
                $thm = tie %hash,'Tie::Hash::MultiKey';
                $newThm = $thm->copy(tie %new,'Tie::Hash::MultiKey');
          or
                tie %new,'Tie::Hash::MultiKey');
                $newThm = $thm->copy(\%new);

        NOTE: this method duplicates the data stored in the parent %hash,
        overwriting and destroying anything that may have been stored in the
        copy target.

COMMON OPERATIONS
    A tied multikey %hash behave like a regular %hash for most operations;

    $value = $hash{$key} returns the key group value

    $hash{$key} = $value sets the value for the key group

      i.e. all keys in the group will return that value

    $hash{$key1,$key2} = $value assigns $value to the key key group
    consisting of $key1, $key2 if they do not. If at least one of the keys
    already exists, the remaining keys are assigned to the key group and the
    value is set for the entire group.

    Better syntax $hash{[$key,$key]} = $value;

    delete $hash{$key} deletes the ENTIRE key group to which $key belongs.

    delete $hash($key1,$key2) deletes ALL groups to which $key1 and $key2
    belong.

    Better syntax delete $hash{[$key1,$key2]};

    keys %hash returns all keys.

    values %hash returns all values

    NOTE: that this will not be the same number of items as returned by keys
    unless there are no key groups containing more than one key.

    ($k,$v) = each %hash behaves as expected.

    References to tied %hash behave in the same manner as regular %hash's
    except as noted for multiple key values above.

LIMITATIONS
    SLICE operations will produce unusual results if you try to use regular
    ARRAYS to specify key groups in the slice. Tie::Hash::MultiKey %hash's
    only accept SCALAR or ARRAY_REF arguments for SLICE and direct
    assigment.

      i.e.
            %WRONG = (
                    one     => 1,
                    two     => 2,
                    (3,4,5) => 12 # expands to 3 => 4, 5 => 12
            );

            %hash = ( # OK
                    one     => 1,
                    two     => 2,
                    [3,4,5] => 12
            );

    will produce a psuedo hash of the form:

            %hash = (
                    one     => 1,
                    two     => 2,
                    3       => 12, --|
                    4       => 12, --|
                    5       => 12  --|
            );

    where the operation $hash{4} = 99 will change the hash to:

            %hash = (
                    one     => 1,
                    two     => 2,
                    3       => 99, --|
                    4       => 99, --|
                    5       => 99  --|
            );

    Example: $hp = \%hash;

      @{$hp}{'one','two','[3,4,5]} = (1,2,12);

    produces the same result as above. If the hash already contains a KEY of
    the same name, the value will be changed for all other shared keys.

     --------------------------

    If you are using ARRAY_REF's as keys (not as pointers to keys as above)
    they must be blessed into some other package so that

            ref $key ne 'ARRAY'

    i.e. bless $key, 'KEY'; # or anything other than 'ARRAY'

     --------------------------

    Example SLICE assignments

    TO tied hash

            @tiedhash{@keys} = @values;

            $hp = \%tiedhash;
            @{$hp}{@keys} =  @values;

    FROM tied hash

            @values = @tiedhash{@keys};

            $hp = \%tiedhash;
            @values = @{$hp}{@keys};

    NOTE: when assigning TO the hash, keys may be ARRAY_REF's as described
    above.

Extension of this module
    This module has extension capabilities that allow adding features to the
    characteristics of the elements within the tied hash. For example,
    knowing the order that items in the hash are accessed as in a cache
    where older items are timed out and removed from the cache.

    The extensions can be customized to a particular instance of a tied
    object. This means that extensions can be embodied as a new module or as
    customization within a Perl program for a particular object instance.

    Requirements:

    An extension 6 Required and 7 Optional callback subrefs to support the
    following operations:

      TIE       O   create the tied object extension
      FETCH     R   recall value operations
      STORE     R   save and update operations
      DELETE    R   delete key set + value operations
      EXISTS    O   checking to see if key exists
      NEXT      O   iterative operations (Perl each)
      COPY      R   hash copy and clone operations
      CLEAR     R   hash clear operations
      ADDKEY    O   add a key to existing key set
      DELKEY    O   delete a key from an existing key set
      REORDERK  O   operation to re-order the key indices
                    that tracks the order that keys are
                    added to the tied hash
      REORDERV  R   operation to re-order the value indices
                    for values belonging to unique key sets
      ...one or more data elements with any key name
         as required by the extension
      CONSOLD   O   operation to consolidate keys that
                    have a common value

      DATAn         any scalar, array_ref, hash_ref

    Usage:

      require Tie::Hash::MultiKey;

      tie %x, 'Tie::Hash::MultiKey',
            TIE      =>     $subref_tie,
            FETCH    =>     $subref_fetch,
            STORE    =>     $subref_store,
            DELETE   =>     $subref_delete,
            EXISTS   =>     $subref_exists,
            NEXT     =>     $subref_next,
            CLEAR    =>     $subref_clear
            COPY     =>     $subref_copy,
            ADDKEY   =>     $subref_addkey,
            DELKEY   =>     $subref_delkey
            REORDERK =>     $subref_Korder,
            REORDERV =>     $subref_Vorder,
            CONSOLD  =>     $subref_consolidate;

      The extension may also be provisioned as a hash_ref.

    NOTE: about internal re-ordering.

    If the tied object has new keys or key sets added more than 2^48 times,
    the internal accounting mechanism will re-order the indices to prevent
    the pointers from converting from unique integer value to floats.
    Extensions that are tied either to the order of key addition or values
    for a key set must correct their associated pointers to match internal
    re-ordering.

      See:  t/Extension.t for usage and testing examples
      See:  Tie::Hash::MultiKeyCache for implementation

    The callbacks return the following arguments:

            $sub___tie->($self)
            $sub_clear->($self)

      A pointer to pre-extension blessed tied hash object

      IMPORTANT: add extension storage to

            $self->[16] and beyond
     -
            $sub_fetch->($self,$key,$valueindex)
            $sub__next->($self,$key,$valueindex)

      next is called ONLY if the key exists and
      is immediately followed by a call to the internal
      FETCH method. Normally no action should be done.

      A pointer to the the tied hash object
      The original key used for the call to fetch
      The internal value index hash key

    NOTE: the primary key hash $self->[0] must not be touched by the
    $sub_next extension or it will mess up the Perl iterator.

     -
            $sub_store->($self,\@keys,$valueindex)

      A pointer to the tied hash object
      A pointer to an array of the keys for the store
      The internal value index hash pointer
     -
            $sub_delete->($self,$kp,$vp)

      A pointer to the tied hash object
      A pointer to an ordered array of the deleted keys
      A pointer to an ordered array of the deleted values
     -
            $sub_exists->($self,$key)

      exists is called ONLY if the key exists;

      A pointer to the the tied hash object
      The original key used for the operation
     -

            $sub_addkey->($self,$key,$valueindex,\@newkeys)

      A pointer to the tied hash object
      The reference key used to identify the key set
      The internal value index for key set
      A list of new keys added
     -

            $sub_delkey->($self,$key,$vi)

      A pointer to the tied hash object
      The value of the key being deleted
      The internal value index for the key set
      else false

    Calls extension_sub_delete if the key is the last key of a key set.

     -
            $sub_copy->($self,$copy,\@valueindex)

      A pointer to the tied hash object
      A pointer to the tied hash copy object
      A pointer to an array internal value index keys
     -
            $sub_Korder->{$self,\%reorderK)

      A pointer to the tied hash object
      A pointer to a hash of the reorder
      key order transfomation

            key => new_order_value
     -
            $sub_Vorder->($self,\%reorderV)

      A pointer to the tied hash object
      A pointer to a hash of the reorder to
            value hash transformation

            old_order => new_ord

     -
            $sub_consolidate->($self,\%kbo,\%ko,\%n2o)

      A pointer to the tied hash object
      A pointer to a hash as consolidated of
            value => [keys]
      A pointer to hash as consolidated of 
            keys => order
      A pointer to hash of
            new vi => [old vi order]
      %n2o is a map of new value indices after
      consolidation to an array of old value
      indices. i.e. if there were tow values
      belonging to different key sets then there
      would be two vi's in the old order array
      represented by the single vi key.
     -

    The internal structure of the tied hash object is as follows:

    [

     0  =>  {       # $kh
            key     => vi     # value index for 1 & 2 below
            },
     1  =>  {       # $vh
            vi      => value, # contains value for the key set
            },
     2  =>  {       # $sh   pointer to hash list keys in a key set
            vi      = {key1 => order1, key2 => order2, ...},
            },
     3  =>  vi,     # numeric value of next value index
     4  =>  or,     # numeric value of next key order
     5  =>  crumbs  # STORE key value
     6  =>  reserved
     7  =>  {       # extensions
       FETCH    => subref,  # required
       STORE    => subref,  # required
       DELETE   => subref,  # required
       COPY     => subref,  # required
       CLEAR    => subref,  # required
       REORDERV => subref,  # required
       TIE      => subref,  # optional
       EXISTS   => subref,  # optional
       NEXT     => subref,  # optional
       ADDKEY   => subref,  # optional
       DELKEY   => subref,  # optional
       REORDERK => subref,  # optional
       CONSOLD  => subref, # optional
     ... one or more data keys
       DATAn     => scalar, array_ref, hash_ref
     }
    ];

    Extension writers should store new information in the indices 16 and up.

    Developers of extensions are encouraged to read the code.

AUTHOR
    Michael Robinton, <miker@cpan.org>

COPYRIGHT
    Copyright 2014, Michael Robinton

    This program is free software; you may redistribute it and/or modify it
    under the same terms as Perl itself.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.