Powerriegel
Powerriegel

Reputation: 627

Store an array in an object and use a getter method

I'm struggling with the difference between arrays and scalars.

I initialize an object from Config::IniFiles. One of the object properties is an array. During the init method, I can print the array values and the array size.

My ini file

[project]
junk = Perl
junk = is
junk = complicated

From the init method, called inside constructor

my $ini = Config::IniFiles->new( -file => "config.ini", -default => 'Default' );
my $junk = $ini->val( 'project', 'junk' );
my @junk = split( /\n/, $junk );

say scalar @junk;

foreach ( @junk ) {
    say $_;
}

$self->{_junk} = @junk;

Output, as expected

3
Perl
is
complicated

Now, I implemented the getter method:

sub getJunkPatterns {

    my ($self) = @_;
    return $self->{_junk};
}

And call it like this

my @patterns = $self->getJunkPatterns();    
say scalar @patterns;
say @patterns;

The result is not what I expected

3
3

For scalar (not array) properties this works fine. Could anyone tell me what I'm doing wrong with arrays? Does anyone know how to properly get arrays from Config::IniFiles without the need to split them?

Upvotes: 0

Views: 434

Answers (2)

zdim
zdim

Reputation: 66883

I take it that the code section with $ini->val(...) is inside of a class and that _junk that the data is written to is intended to be an array reference, the only meaningful option.

First, you can have val from Config::IniFiles return data either as a list or as a string

If you want a multi-line/value field returned as an array, just specify an array as the receiver:

@values = $cfg->val('Section', 'Parameter');

So instead of assigning to a scalar and splitting into lines just assign to an array.

Next, the assignment $self->{_junk} = @junk; is most likely wrong, as follows.

The array @junk is being assigned to a scalar, the only thing a hash value can be. This means that the operation is in the scalar context, in which case what gets assigned is the number of elements of the array. So after that line _junk has a number instead of being an arrayref, as seen in the ouput. See context in perldata.

So assign an arrayref, [@junk] (or more efficiently \@junk, if the surrounding code allows it).

Finally, in your getter you can simply choose to return those lines either as a list or as a reference to that list, as the answer by simbabque shows.

But you can also consider returning it like val does, as either a string or a list, depending on the context in which the method is called. To do this check for the context using wantarray. If the method is called in the list context we return a list, while in the scalar context we return a scalar.

sub getJunkPatterns {
    my ($self) = @_;
    return (wantarray)
        ? @{$self->{_junk}};
        : join "\n", @{$self->{_junk}};
}

The elements are joined by newline for the scalar return, to mimic what val does. If you'd rather return the reference change the scalar return to : $self->{_junk};.

Then you can use it either as

my @patterns = $object->getJunkPatterns();

or

my $pattern_string = $object->getJunkPatterns(); 

If you choose to return a reference to that list in the scalar context, instead of the string with lines, change the getter accordingly and then the returned scalar is an arrayref.

Upvotes: 3

simbabque
simbabque

Reputation: 54323

You are mixing up arrays and array references.

Your line $self->{_junk} = ['Test1', 'test2'] sets the value of the _junk key to an array reference. You can tell that from the square brackets [].

To get the array out of the reference, you need to dereference it. You can either do that in your getter like this:

return @{ $self->{_junk} };

Or you define your interface as an array reference and let the user deref it.

my @patterns = @{ $self->getJunkPatterns() };

Of course you need to have an array reference inside of that key in the first place. It's not possible to put an array inside anything. Data structures with more than one level have to be references in Perl.

So wherever you set it, it needs to look something like this:

sub setJunkPatterns {
    my ($self, @patterns) = @_;
    $self->{_junk} = \@patterns;
}

See perlref and perlreftut for more information.


You are using $self throughout the code, but it appears that you are not inside of a class. $self in Perl is used similar to this in some other languages. It's the reference to the instantiated object of a class that you are working with, inside of that class. So when you instantiate an object of a certain class outside, you would never call that $self.

Upvotes: 4

Related Questions