2

I'd like to pre-initialize the elements of an array of hashes so that when it comes time to filling in the data, I don't need to check for the existence of various members and initialize them every loop. If I can, I'd like to pre-initialize the general form of the datastructure which should look like this:

$num_sections = 4;
# Some magic to initialize $VAR1 to this:
$VAR1 = {
          'sections' => [
                          {},
                          {},
                          {},
                          {}
                        ]
        };

I would like this to work,

$ph->{sections} ||= [({} x $num_sections )];

but it results in

$VAR1 = {
          'sections' => 'HASH(0x21b6110)HASH(0x21b6110)HASH(0x21b6110)HASH(0x21b6110)'
        };

And no amount of playing with the () list context and {} empty hash reference seem to make it work.

This works but it's not quite a one-liner

unless ($ph->{sections})
{
    push @{ $ph->{sections}}, {} foreach (1..$num_sections);
}

There's probably some perl magic that I can use to add the unless to the end, but I haven't quite figured it out.

I feel I'm so close, but I just can't quite get it.

Update Oleg points out that this probably isn't necessary at all. See comments below.

Mort
  • 3,379
  • 1
  • 25
  • 40
  • 1
    Are you sure you even need to pre-initialize anything? Can you show "filling" part to demonstrate why simple autovivifaction is not enough? – Oleg V. Volkov Feb 28 '19 at 17:33
  • @OlegV.Volkov, I think you're right. I always worry about auto-vivification of complex datastructures in perl but I just tried this and it works fine: `my $test; $test->{sections}->[3]->{happy}= 'true';` I guess maybe it's only cases in the past when I've wanted to `push` onto a list where obviously it had to be initialized to an empty list first, and I've _erroneously_ mentally generalized this to "you can't access a deep member of a datastructure without pre-initializing it. – Mort Mar 01 '19 at 01:48

1 Answers1

5

If the left-hand side of x is not in parens, x repeats the string on its LHS and returns the concatenation of those strings.

If the left-hand side of x is in parens, x repeats the value on its LHS and returns the copies.

This latter approach is closer to what you want, but it's still wrong as you'll end up with multiple references to a single hash. You want to create not only new references, but new hashes as well. For that, you can use the following:

$ph->{sections} ||= [ map { +{} } 1..$num_sections ];
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Fantastic. What does the `+` do? – Mort Feb 28 '19 at 15:14
  • It makes sure that `{}` gets interpreted as a hash constructor rather than a bare loop. I don't know if it's necessary here. See [here](https://stackoverflow.com/q/51613242/589924) for a more thorough explanation. – ikegami Feb 28 '19 at 15:17
  • this description of `x` only applies in list context; in scalar context, `x` always returns concatenation of repeated strings – ysth Feb 28 '19 at 15:40