I have a Perl class that contains a hash instance variable for storing other objects. I would like to have a method to print the number of elements in the hash but I am getting the following error message on the line return keys($self->{'_things'});
Type of arg 1 to keys must be hash (not hash element)
package MyClass;
use strict;
sub new {
my ($class) = #_;
my $self = {
_things => undef
};
$self->{'_things'} = ();
bless $self, $class;
return $self;
}
sub get_count {
my ( $self ) = #_;
return keys($self->{'_things'});
}
Use
return scalar(keys(%{$self->{'_things'}}));
$self->{'_things'} is just a reference to a hash, and keys() expects a hash as its argument—so, you have to dereference it first by wrapping it in %{…}. Finally, since you want to count items, you have to make sure the return value of keys() (which is a list) is interpreted in scalar context, by wrapping it in scalar(…).
If I understand you correctly, $self->{_things} should contain a hash data structure. If so, you have two problems:
sub new {
my ($class) = #_;
my $self = {
# Initialize _things to be a reference to an empty hash.
_things => {},
};
bless $self, $class;
return $self;
}
sub get_count {
my ( $self ) = #_;
# Here's the way to get the N of keys.
# The %{ FOO } syntax will take a hash reference (FOO in this case) and
# convert it to a hash, on which we can then call keys().
return scalar keys %{ $self->{'_things'} };
}
The error message is totally correct! ;) You need to 'dereference' the element into a hash, also you need to call keys() in a scalar context to get the count.
$ perl -wle 'use strict; my $href = { foo => undef }; $href->{foo} = (); print sclar keys $href->{foo}'
Type of arg 1 to keys must be hash (not hash element) at -e line 1, at EOF
Execution of -e aborted due to compilation errors.
vs.
$ perl -wle 'use strict; my $href = { foo => undef }; $href->{foo} = (); print scalar keys %{ $href->{foo} }'
0
You might want to use a hash ref instead for $self->{_things} to avoid accidental list flattening and other issues though.
$ perl -wle 'use strict; my $href = { foo => undef }; $href->{foo} = { bar => 1 }; print scalar keys %{ $href->{foo} };'
1
Related
I have a .pm Package Test file which contains:
sub new{
my $hash = shift;
my $self = {};
bless($self,$class);
$self->{hash} = %hash;
return $self;}
and
sub printer{
my $self = shift;
print("Test: ",$self->{hash},"\n");
return;}
On my main.pl I use:
$test = Test->new(%myhash);
I don't know if explained it properly but the problem is that I can't print my hash using my printer function.
I really appreciatte some help about it and if more information about it is needed I can paste all the files here.
The first argument to ->new is the class name. The arguments to the constructor come next. Not hardwiring the class name also makes inheritance possible.
Do you understand the difference between a hash and a hash reference? %hash is a hash, \%hash is a hash reference. If $test->{hash} contains a hash reference, you can dereference it (i.e. retrieve the hash from it) with %{ $test->{hash} }. Hash values must be scalars, which means you can't make a hash a value of a hash - but you can make a hash reference the value.
I'd also recommend to indent the code properly.
#! /usr/bin/perl
use warnings;
use strict;
{
package Test;
sub new {
my ($class, %hash) = #_;
bless { hash => \%hash }, $class;
}
sub printer {
my $self = shift;
print "Test: ", %{ $self->{hash} }, "\n";
}
}
my %hash = ( a => 11, b => 12 );
my $t = 'Test'->new(%hash);
$t->printer; # a11b12 or b12a11
In this answer I found a recommendation for a simple TO_JSON method, which is needed for serializing blessed objects to JSON.
sub TO_JSON { return { %{ shift() } }; }
Could anybody please explain in detail how it works?
I changed it to:
sub TO_JSON {
my $self = shift; # the object itself – blessed ref
print STDERR Dumper $self;
my %h = %{ $self }; # Somehow unblesses $self. WHY???
print STDERR Dumper \%h; # same as $self, only unblessed
return { %h }; # Returns a hashref that contains a hash.
#return \%h; # Why not this? Works too…
}
Many questions… :( Simply, I’m unable to understand 3-liner Perl code. ;(
I need the TO_JSON but it will filter out:
unwanted attributes and
unset attributes too (e.g. for those the has_${attr} predicate returns false)
This is my code – it works but I really don't understand why the unblessing works…
use 5.010;
use warnings;
use Data::Dumper;
package Some;
use Moo;
has $_ => ( is => 'rw', predicate => 1,) for (qw(a1 a2 nn xx));
sub TO_JSON {
my $self = shift;
my $href;
$href->{$_} = $self->$_ for( grep {!/xx/} keys %$self );
# Same mysterious unblessing. The `keys` automagically filters out
# “unset” attributes without the need of call of the has_${attr}
# predicate… WHY?
return $href;
}
package main;
use JSON;
use Data::Dumper;
my #objs = map { Some->new(a1 => "a1-$_", a2 => "a2-$_", xx=>"xx-$_") } (1..2);
my $data = {arr => \#objs};
#say Dumper $data;
say JSON->new->allow_blessed->convert_blessed->utf8->pretty->encode($data);
EDIT: To clarify the questions:
The %{ $hRef } derefences the $hRef (getting the hash pointed to by the reference), but why get a plain hash from a blessed object reference $self?
In other words, why the $self is a hashref?
I tried to make a hash slice like #{$self}{ grep {!/xx/} keys %$self} but it didn't work. Therefore I created that horrible TO_JSON.
If the $self is a hashref, why the keys %$self returns only attributes having a value, and not all declared attributes (e.g. the nn too – see the has)?
sub TO_JSON { return { %{ shift() } }; }
| | |
| | L_ 1. pull first parameter from `#_`
| | (hashref/blessed or not)
| |
| L____ 2. dereference hash (returns key/value list)
|
L______ 3. return hashref assembled out of list
In your TO_JSON() function { %h } returns a shallow hash copy, while \%h returns a reference to %h (no copying).
Perl implemented object orientation by simply making it possible for a reference to know which package it came from (with bless). Knowing that a reference came from the Foo package means that methods are really functions defined in that package.
Perl allows any kind of reference to get blessed; not just hash references. It's very common to bless hash references; a lot of documentation shows doing exactly that; and Moose does it; but, it's possible to bless an array reference, or a subroutine reference, or a filehandle, or a reference to a scalar. The syntax %{$self} only works on hash references (blessed or not). It takes the hash reference, and dereferences it as a hash. The fact that the original reference may have been blessed is lost.
I need the TO_JSON but what will filter out:
unwanted attributes
and unset attributes too (e.g. for those the has_${attr} predicate returns false.
Pre-5.20, hash slices only give you the values and not the keys from the original hash. You want both keys and values.
Assuming you have a hash, and want to filter out undef values and keys not on a whitelist, there are a few options. Here's what I have, using the JSON module:
use strict; # well, I used "use v5.18", but I don't know which version of Perl you're using
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_, 1 } qw{foo bar};
my %bar = map { $_ => $foo->{$_} }
grep { defined $foo->{$_} && exists $whitelist{$_} }
keys %$foo;
print to_json(\%bar) . "\n";
# well, I used say() instead of print(), but I don't know which version of Perl you're using
The maps and greps aren't necessarily pretty, but it's the simplest way I could think of to filter out keys not on the whitelist and elements without an undef value.
You could use an array slice:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my #whitelist = qw{foo bar};
my %filtered_on_keys;
#filtered_on_keys{#whitelist} = #$foo{#whitelist};
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";
Or if you like loops:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_ => 1 } qw{foo bar};
my %bar;
while (my ($key, $value) = each %$foo) {
if (defined $value && exists $whitelist{$key}) {
$bar{$key} = $value;
}
}
print to_json(\%bar) . "\n";
It seems like a good time to bring up Larry wall's quote, "Perl is designed to give you several ways to do anything, so consider picking the most readable one."
However, I made a big point that not all objects are hashes. The appropriate way to get data from an object is through its getter functions:
use strict;
use warnings;
use JSON;
my $foo = Foo->new({ foo => undef, bar => 'baz', quux => 5 }); # as an example
my %filtered_on_keys;
#filtered_on_keys{qw{foo bar}} = ($foo->get_foo(), $foo->get_bar());
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";
I'm doing a class assignment to learn about Object Oriented programming in Perl. I've got a real basic class that looks like this.
sub new{
my $class = shift;
my $self = {
'Sides' => 3,
'SL' => \#sidelengths};
bless $self, $class;
return $self;
}
I've got two modules to change the sides and length(can't figure out how to modify the sidelegnths with an accessor though) but I have a requirement for my work that I have a method like this
"a method: toString() which returns all of the file attributes in a printable
string. If this is done correctly, the PERL
print $file->toString() . "\n";
should print a readable summary of the file."
I already think I want to use Data::Dumper to do this and that works within a script but it sounds like I need to use it within a module and call that to print a string of whats in the object. So far I have this
sub toString{
my $self = #_;
Dumper( $self );
}
Which just prints out "$VAR1 = 1"
What you want here is to shift an argument out of #_.
sub toString {
my $self = shift #_;
Dumper( $self );
}
When you have $var = #array, that evaluates the array in a scalar context, and that returns the number of elements in the array. So, your statement my $self = #_; set $self to the number of arguments passed to toString, which in this case was 1. (The $self argument.)
Alternately, you can capture the first element of #_ this way:
sub toString {
my ($self) = #_;
Dumper( $self );
}
What this does is evaluate #_ in list context since it uses list assignment. It assigns the first element of #_ to $self.
my $self = #_;
is a scalar assignment operator, so it #_ in scalar context, which is the number of elements it contains. You want to use the list assignment operator.
sub toString {
my ($self) = #_;
return Dumper( $self );
}
I am new to OO perl. I am trying to write one simple program but getting the error.
Created a package Employee.pm as
package Employee;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub get_names {
my $self = #_;
print " getting the names \n";
return $self;
}
sub set_names {
my ($self, $last_name) = #_;
$self->{last_name} = $last_name;
return $self->{$last_name};
}
1;
And created a .pl file as
use strict;
use warnings;
use Employee;
my $obj = new Employee("name" => "nitesh", "last_name" => "Goyal");
my $val = $obj->get_names();
print %$val;
my $setName = $obj->set_names("kumar");
print "$setName \n";
I am getting error as
"Can't use string ("1") as a HASH ref while "strict refs" in use at class1.txt line 10."
The error
"Can't use string ("1") as a HASH ref ..
Comes from this part:
sub get_names {
my $self = #_;
When an array is put in scalar context, it returns its size. Since you call the sub with
$obj->get_names();
Only one argument is passed, which is the object, so #_ contains 1 argument, and its size is 1, therefore in the sub get_names, the variable $self is set to 1. Hence the error. What you probably should do is
my $self = shift;
But then, that will not do anything, because you never stored the names in your constructor. As mpapec said, you should do
my $self = { #_ };
in the constructor sub new.
Also, in get_names, you simply return the object, which is not very useful. You should perhaps return $self->{name} and $self->{last_name}.
I have an instance variable, properties, that is being declared and instantiated like so:
$self->{properties}{$key1} = $value;
My understanding that this will declare the properties field, and also set it to a Hash primitive, containing one key value pair.
I'm trying to write a getter for the properties instance variable, that will return the hash:
sub getProperties{
my $self = shift;
my %myhash = $self->{properties};
return %myhash;
}
And subsequently call the getter like so:
my %properties = $properties->getProperties();
When I try to compile this i get:
"Odd number of elements in hash assignment at 70..."
line 70 being: my %myhash = $self->{properties};
In this line of code:
my %myhash = $self->{properties};
%myhash is a hash whereas $self->{properties} is a hash reference. So you're effectively returning a hash with one key/value pair where the key is a reference to a hash and the value is undef.
If you really want to return a hash, do this:
my %myhash = %{$self->{properties}};
Alternatively, return a hash reference. That's generally preferable to returning a hash, since it doesn't make a copy of the original hash and therefore is more memory efficient as the hash gets larger. Here's how it looks:
sub getProperties {
my $self = shift;
return $self->{properties};
}
Then in your calling code instead of this:
my %properties = $properties->getProperties();
$somevalue = $properties{'somekey'};
do this:
# getProperties returns a reference, so assign to a scalar
# variable ($foo) rather than a hash (%foo)
my $properties = $properties->getProperties();
# Use -> notation to dereference the hash reference
$somevalue = $properties->{'somekey'};
isn't $self->{properties} a hashref not a hash?
$ perl t4.pl
size -> 42
t4.pl
#!/usr/bin.perl
use strict;
use warnings;
use t4;
my $t4 = t4->new();
my %hash = $t4->getProperties();
for my $key (keys %hash) {
print "$key -> $hash{$key}\n";
}
t4.pm
package t4;
sub new {
my $class = shift;
my $self = {};
$self->{properties}{size} = 42;
bless ($self, $class);
}
sub getProperties {
my $self = shift;
my %myhash = %{$self->{properties}};
return %myhash;
}
1;