Skip to content

Commit

Permalink
Simplify the code base (Thanks aristotle for the suggestion). Changed…
Browse files Browse the repository at this point in the history
… how as_hash(ref) behaves. It's now 16000 QPS vs 24000 QPS (normal). Pretty good
  • Loading branch information
miyagawa committed Dec 16, 2009
1 parent 4a46217 commit 3d57fac
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 50 deletions.
96 changes: 48 additions & 48 deletions lib/Hash/MultiValue.pm
Expand Up @@ -5,95 +5,95 @@ use 5.008_001;
our $VERSION = '0.01';

use Scalar::Util qw(refaddr);
my %obj;
my %items;

sub new {
my($class, @items) = @_;

my(%hash, %mhash, @keys, %seen);
while (@items) {
my($key, $value) = splice @items, 0, 2;
$hash{$key} = $value;
push @{$mhash{$key}}, $value;
push @keys, $key unless $seen{$key}++;
}
my %hash = @items; # yay, this should keep the last value

my $self = bless \%hash, $class;
$obj{refaddr $self} = [ \%mhash, \@keys ];
$items{refaddr $self} = \@items;

$self;
}

sub DESTROY {
my $self = shift;
delete $obj{refaddr $self};
delete $items{refaddr $self};
}

sub _iter {
my($self, $cb) = @_;
my @copy = @{$items{refaddr $self}};
while (@copy) {
my @pairs = splice @copy, 0, 2;
$cb->(@pairs);
}
}

sub obj {
sub items {
my $self = shift;
$obj{refaddr $self};
$items{refaddr $self};
}

sub get {
my($self, $key) = @_;
$self->{$key};
}

sub set {
my($self, $key, $value) = @_;
$self->{$key} = $value;

my $obj = $self->obj;
$obj->[0]->{$key} = $value;
sub getall {
my($self, $key) = @_;
my @values;
$self->_iter(sub { push @values, $_[1] if $_[0] eq $key });
(@values);
}

for my $k (@{$obj->[1]}) {
return if $key eq $k;
}
push @{$obj->[1]}, $key;
sub add {
my($self, $key, $value) = @_;
$self->{$key} = $value; # this should be the value since it's more "last"
push @{$self->items}, $key, $value;
}

sub remove {
my($self, $key) = @_;
delete $self->{$key};

my $obj = $self->obj;
delete $obj->[0]->{$key};

my @new;
for my $k (@{$obj->[1]}) {
push @new, $k if $key ne $k;
}
$obj->[1] = \@new;
}

sub getall {
my($self, $key) = @_;
(@{$self->obj->[0]->{$key}});
$self->_iter(sub { push @new, @_ if $_[0] ne $key });
@{$self->items} = @new;
}

sub keys {
my $self = shift;
@{$self->obj->[1]};
my(@keys, %seen);
$self->_iter(sub { push @keys, $_[0] unless $seen{$_[0]}++ });
(@keys);
}

sub flatten {
my $self = shift;
my %mhash = %{$self->obj->[0]};

my @list;
while (my($key, $value) = each %mhash) {
my @values = ref $value eq 'ARRAY' ? @$value : ($value);
for my $v (@values) {
push @list, $key, $v;
}
}

return @list;
@{$self->items};
}

sub as_hash {
my $self = shift;
%{$self->obj->[0]}; # dclone?

my %hash;
$self->_iter(sub {
my($key, $value) = @_;
if (exists $hash{$key}) {
if (ref $hash{$key} eq 'ARRAY') {
push @{$hash{$key}}, $value;
} else {
$hash{$key} = [ $hash{$key}, $value ];
}
} else {
$hash{$key} = $value;
}
});

%hash;
}

sub as_hashref {
Expand Down Expand Up @@ -132,7 +132,7 @@ Hash::MultiValue - Store multiple values per key
keys %$hash; # ('foo', 'bar') not guaranteed to be ordered
$hash->keys; # ('foo', 'bar') guaranteed to be ordered
# get a plain hash. values are all array references
# get a plain hash where values may or may not be an array ref
%hash = $hash->as_hash;
# get a pair so you can pass it to new()
Expand Down
3 changes: 1 addition & 2 deletions t/write.t
Expand Up @@ -8,8 +8,7 @@ my $hash = Hash::MultiValue->new(
bar => 'baz',
);

#$hash->{baz} = 33;
$hash->set(baz => 33);
$hash->add(baz => 33);
is $hash->{baz}, 33;

my $new_hash = Hash::MultiValue->new($hash->flatten);
Expand Down

0 comments on commit 3d57fac

Please sign in to comment.