Perlでハッシュの取り出し順序を補償する方法

普通にやる場合、Tie::IxHashを使うのが楽ですが、
今回は自前実装してみました。

package Sorauta::Util::Iterator;

use 5.010000;
use strict;
use warnings;
use Carp qw/croak/;

our($YES, $NO) = qw/1 0/;

#=====================
#
#=====================
sub new {
  my $self = shift;

  bless {
    _last_id       => 0,
    _position      => 0,
    _item_key_list => [],
    _item_list     => {},
  }, $self;
}

# リストをセット
# ハッシュのリファレンスを渡す
sub hash2itr {
  my($self, $hash_ref) = @_;

  #$self->{_last_id} = 0;
  #$self->{_position} = 0;
  #$self->{_item_key_list} = [];
  #$self->{_item_list} = {};

  while (my($key, $val) = each(%$hash_ref)) {
    $self->add($key, $val);
  }
}

# 最後尾に追加する
sub add {
  my($self, $key, $val) = @_;

  $self->{_item_list}->{$key} = $val;
  $self->{_item_key_list}->[$self->{_last_id}++] = $key;

  return ;
}

# 削除する
sub del {
  my($self, $key) = @_;

  # ハッシュを削除
  delete($self->{_item_list}->{$key});

  # インデックス保持配列削除
  my $index = -1;
  foreach (0..@{$self->{_item_key_list}}) {
    if ($self->{_item_key_list}->[$_] eq $key) {
      $index = $_;
      last;
    }
  }
  if ($index == -1) {
    croak 'not found key';
  }
  splice(@{$self->{_item_key_list}}, $index, 1);

  # 個数を減らす
  $self->{_last_id}--;

  return ;
}

# キー指定で取得
# ※ただし、_positionの移動などは行わない
sub get {
  my($self, $key) = @_;
  
  return $self->{_item_list}->{$key};
}

# インデックス指定で取得
# ※ただし、_positionの移動などは行わない
sub get_by_index {
  my($self, $index) = @_;
  my $key = $self->{_item_key_list}->[$index];
  
  return $self->{_item_list}->{$key};
}

# 次へ
sub next {
  my $self = shift;
  my $key = $self->{_item_key_list}->[$self->{_position}++];
  
  if ($self->{_position} <= $self->{_last_id}) {
    return $self->{_item_list}->{$key};
  }
  else {
    return ;
  }
}

# 次があるか
sub hasNext {
  my $self = shift;

  return $self->{_position} < $self->{_last_id} ? $YES : $NO;
}

# ポジションを最初に戻す
sub reset {
  my $self = shift;

  $self->{_position} = 0;

  return;
}

# 全行取得
sub all {
  my $self = shift;
  my @list = ();

  foreach my $val(values(%{$self->{_item_list}})) {
    push(@list, $val);
  }
  return wantarray ? @list : \@list;
}

# 件数返却
sub count {
  my $self = shift;

  return $self->{_last_id};
}

1;


とりあえず必要そうな機能はある程度盛り込んだはず。
使い方はこんな感じ。

追加:
  my $itr = Sorauta::Util::Iterator->new;


  $itr->add('num1', { data => 1, test => "fuck" });
  $itr->add('num2', [ 'data', 1, 'test', "fuck" ]);
  $itr->add('num3', 'kurenai');
  $itr->add('num4', 109238402389);
  $itr->add('sub_ref', $sub_ref); # $sub_ref = sub { print 'hoge' };


削除:
  my $itr = Sorauta::Util::Iterator->new;


  $itr->del('key');


取得:
  my $itr = Sorauta::Util::Iterator->new;

  
  my $val = $itr->get('num1');
  my $val = $itr->get_by_index(3);


いてレータっぽくする:
  my $itr = Sorauta::Util::Iterator->new;
  

  while (my $val = $itr->next) {
    print Dumper($val);
  }


次持ってるか確認:
  say $itr->hasNext ? 'you have next value' : 'none';


配列にして全部取得:
  my $itr = Sorauta::Util::Iterator->new;
  my $list = $itr->all;
  or
  my @list = $itr->all;


件数取得:
  my $count = $itr->count;


ハッシュから変換する:
  my $itr2 = Sorauta::Util::Iterator->new;
  my $hash_list = {
    hoge => 1,
    sample => 2,
    wieht => [1,2,3,5,55,2524352345,23],
    hash => {shi => 1, weoith=>"2", weotj => "hoghehoge"}
  };
  $itr2->hash2itr($hash_list);

  while (my $val = $itr2->next) {
    print Dumper($val); #=> 1, 2, {shi=>1,...}...
  }


先頭に戻す:
  $itr->reset;


一応、CPANにもあげておきました。
車輪の再発明すぎて不要とは思いますが...
http://search.cpan.org/~yuki/Sorauta-Util-Iterator-0.01/lib/Sorauta/Util/Iterator.pm