Доступ к многомерному хешу с помощью строк

У меня есть большой многомерный хеш, который является импортом структуры JSON.

my %bighash;

В %bighash есть элемент, который называется:

$bighash{'core'}{'dates'}{'year'} = 2019.

У меня есть отдельная строковая переменная с именем core.dates.year, которую я хотел бы использовать для извлечения 2019 года из %bigash.

Я написал этот код:

my @keys  = split(/\./, 'core.dates.year');
my %hash = ();
my $hash_ref = \%hash;

for my $key ( @keys ){
    $hash_ref->{$key} = {};
    $hash_ref = $hash_ref->{$key};
}

который, когда я выполняю:

say Dumper \%hash;

выходы:

$VAR1 = {
          'core' => {
                   'dates' => {
                             'year' => {}
                           }
                 }
        };

Пока все хорошо. Но сейчас я хочу сказать:

print $bighash{\%hash};

Который я хочу вернуть в 2019 году. Но ничего не возвращается или я вижу ошибку «Использование неинициализированного значения в% bighash в конкатенации (.) или строку в script.pl, строка 1371, строка 17 (# 1) .. .

Может ли кто-нибудь указать мне, что происходит?

Мой проект включает в себя встраивание строк во внешний файл, который затем заменяется фактическими значениями из %bigash, так что это просто интерполяция строк.

Спасибо!


person Mario T.    schedule 26.02.2019    source источник
comment
См. также Hash::Flatten и Data::Diver   -  person Håkon Hægland    schedule 26.02.2019
comment
В свой ответ я включил более быстрые и простые в использовании версии Data::Diver Dive и DiveVal.   -  person ikegami    schedule 26.02.2019


Ответы (3)


В Perl нет многомерных хэшей. Хэши — это пары ключ/значение. Ваше понимание структур данных Perl является неполным.

Переосмыслите свою структуру данных следующим образом

my %bighash = (
    core => {
        dates => {
            year => 2019,
        },
    },
);

Есть разница между круглыми скобками () и фигурными скобками {}. Символ % в имени переменной указывает на то, что это хеш, то есть набор неупорядоченных пар ключ/значение. Круг () — это список. Внутри этого списка находятся два скалярных значения, то есть ключ и значение. Значение является ссылкой на другой, анонимный, хэш. Вот почему у него кудрявый {}.

Каждый из этих уровней представляет собой отдельную структуру данных.

Это переписывание вашего кода похоже на то, что икэгами написал в своем ответе, но менее эффективно и более подробно.

my @keys = split( /\./, 'core.dates.year' );

my $value = \%bighash;
for my $key (@keys) {
  $value //= $value->{$key};
}

print $value;

Он шаг за шагом углубляется в структуру и в конечном итоге дает вам окончательное значение.

person simbabque    schedule 26.02.2019
comment
Так что это сработало для меня. Я также попробовал варианты погружения, предлагаемые икегами, но погружение просто вернуло хэш HASH (0x7fe01084fec8), а не значение 2019. Поэтому я думаю, что ваше решение — это то, с которым мне следует пойти. Мне также нужно больше узнать о хэшах и структурах данных. - person Mario T.; 26.02.2019
comment
Re Значит, это сработало для меня. И ответ говорит, что это идентично моему. (Однако в dive_val была ошибка, которую я исправил) - person ikegami; 26.02.2019
comment
@МариоТ. вы хотите прочитать perldoc.perl.org/perlref.html и perldoc.perl.org/perlreftut.html - person simbabque; 26.02.2019
comment
@ikegami, ты знаешь, что я не пытаюсь украсть твой ответ, верно? :) - person simbabque; 26.02.2019
comment
@simbabque, у меня нет абсолютно никаких проблем с вашим ответом. Я просто оспаривал заявление Марио. - person ikegami; 26.02.2019
comment
Я использую только предоставленный вами метод погружения геттера, и он просто возвращает HASH (0x7fe01084fec8). Он не возвращает значение 2019. Так что не уверен, почему это так — я обязательно прочитаю две ссылки, предоставленные @simbabque — я не могу заставить этот метод погружения возвращать что-либо, кроме HASH (0x7fe01084fec8). - person Mario T.; 26.02.2019
comment
Я должен уточнить, что я не использую погружение_val для установки значения, поскольку я вытягиваю более крупный %bighash и ссылаюсь на него из погружения. Возможно, это проблема? - person Mario T.; 26.02.2019
comment
@МариоТ. Подпрограмма dive от ikegami делает то же самое, что и мой подробный код. Вы проходите в список. Вот и все. Точно так же должно работать. Вы, должно быть, использовали его неправильно. - person simbabque; 26.02.2019

Может ли кто-нибудь указать мне, что происходит [когда я использую $bighash{\%hash}]?

Хэш-ключи — это строки, а преобразование \%hash в строку похоже на HASH(0x655178). Единственный элемент в %bighash имеет ключ core, а не HASH(0x655178), поэтому поиск хэша возвращает undef.


Полезные инструменты:

sub dive_val :lvalue { my $p = \shift; $p //= \( $$p->{$_} ) for @_; $$p }   # For setting
sub dive { my $r = shift; $r //= $r->{$_} for @_; $r }                       # For getting

dive_val(\%hash, split /\./, 'core.dates.year') = 2019;
say dive(\%hash, split /\./, 'core.dates.year');
person ikegami    schedule 26.02.2019
comment
В %bigash есть запись: $bigash{'core'}{'dates'}{'year'} = 2019. Это то, к чему я пытаюсь получить доступ. Это многомерно. - person Mario T.; 26.02.2019
comment
Если я напечатаю $bighash{'core'}{'dates'}{'year'}, то получу 2019 год в качестве ответа. Что я хотел бы сделать, так это сказать, что напечатайте $bighash{\%hash}, чтобы получить тот же ответ. - person Mario T.; 26.02.2019
comment
Это может быть, но $bighash{\%hash} означает что-то другое. Ваш вопрос был «Может ли кто-нибудь указать мне, что происходит, когда кто-то делает $bighash{\%hash}». - person ikegami; 26.02.2019
comment
Возможно, вы захотите дать некоторые пояснения по поводу этих подводных лодок. Особенно lvalue довольно продвинутый. - person simbabque; 26.02.2019
comment
@simbabque, я уже задокументировал, как их использовать в своем ответе. Если вместо этого вас интересует, как это работает, есть объяснение здесь. - person ikegami; 26.02.2019

Здесь может помочь Hash::Fold. Вы можете «сгладить» свой хэш, а затем получить доступ ко всему с помощью одного ключа.

use Hash::Fold 'flatten';
my $flathash = flatten(\%bighash, delimiter => '.');
print $flathash->{"core.dates.year"};
person mob    schedule 26.02.2019