Как использовать псевдо-ха sh в современном perl? - PullRequest
2 голосов
/ 19 марта 2020

У меня есть данные, загруженные в следующем формате (вероятно, загруженный из .csv файла):

my $data =  [
   [ 'id', 'name', 'value' ],
   [   23,  'foo',      77 ],
   [   44,  'bar',   'dfd' ],
]

Я хочу получить доступ к таким данным:

$data->[$n]{ name }

Я знаю, что в старых perl Я мог бы использовать pha sh (псевдо-ха sh), но он устарел и был заменен на fields прагма.

Насколько как я вижу, что используется для объектов. В моем случае я не создаю объекты и не использую классы.

Как мне использовать fields в моем случае использования? Пожалуйста, приведите пример

Ответы [ 3 ]

6 голосов
/ 19 марта 2020

Использование модуля Text :: CSV_XS для чтения ваших CSV-данных и указания им, какие имена столбцов основаны на первой строке:

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Text::CSV_XS;

my $csv = Text::CSV_XS->new({binary => 1, auto_diag => 1});
$csv->column_names($csv->getline(\*DATA));
my $data = $csv->getline_hr_all(\*DATA);
say $data->[0]->{'name'}; # prints foo

__DATA__
id,name,value
23,foo,77
44,bar,dfd

Рассмотрите возможность зацикливания записей вместо того, чтобы читать весь файл одновременно. См. Документацию для getline_hr для нескольких способов сделать это.

4 голосов
/ 19 марта 2020

Если вы можете использовать Text :: CSV_XS, как показывает Шон , сделайте это.

У нас есть упражнение Средний Perl, которое делает это. Используйте первую строку, чтобы сопоставить имена заголовков с позицией, а затем используйте это ha sh, чтобы перевести другой путь. Вот он с интенсивным использованием разыменования постфикса :

use v5.24;

my $data =  [
   [ 'id', 'name', 'value' ],
   [   23,  'foo',      77 ],
   [   44,  'bar',   'dfd' ],
];

# ikegami's suggestion
my %name_to_index = map { $data->[0][$i] => $_ } 0..$data->[0]->$#*;

foreach my $i ( 1 .. $data->$#* ) {
    say $data->[$i][ $name_to_index{name} ]
    }

Вот версия с оберткой, которая работает с версиями до v5.24, но я думаю, что это уродливее (и вы должны использовать шестилетний, без поддержки Perl):

use v5.10;

my $data =  [
   [ 'id', 'name', 'value' ],
   [   23,  'foo',      77 ],
   [   44,  'bar',   'dfd' ],
];

# ikegami's suggestion
my %name_to_index = map { $data->[0][$i] => $_ } 0.. $#{ $data->[0] };

foreach my $i ( 1 .. $#{ $data } ) {
    say $data->[$i][ $name_to_index{name} ]
    }

Поскольку реальный код, вероятно, намного сложнее, я думаю, что часто легче понять, когда вы не углубляетесь в структуру данных повсюду. Если вы не возражаете против дополнительной работы (как в жарком l oop), вы можете превратить ваш ряд в ха sh, где ключи - это заголовки (аналогично тому, что делает Text::CSV_XS), и сыграть с этим ха sh, не задумываясь о всей цепочке разыменований. В этом примере используется фрагмент ha sh, чтобы заполнить все сразу. После этого вы играете с %hash вместо $data->[$i][...]:

use v5.24;

my $data =  [
   [ 'id', 'name', 'value' ],
   [   23,  'foo',      77 ],
   [   44,  'bar',   'dfd' ],
];

my @headers = $data->[0]->@*;
foreach my $i ( 1 .. $data->$#* ) {
    my %hash;
    @hash{ @headers } = $data->[$i]->@*;

    say $hash{name};
    }

Любопытно, что сразу после раздела Pseudoha sh в perlref , документы показывают пример с шаблонами функций , Вместо ha sh для сопоставления вы можете определить подпрограммы. Некоторым людям нравится, что имя индекса заголовка выглядит немного чище, но я не думаю, что это стоит дополнительного объяснения мягкого нарушения refs и объяснения типов globs:

use v5.24;

my $data =  [
   [ 'id', 'name', 'value' ],
   [   23,  'foo',      77 ],
   [   44,  'bar',   'dfd' ],
];

foreach my $name ( $data->[0]->@* ) {
    state $n = 0;
    my $m = $n++;      # don't reference $n
    no strict 'refs';  # Hey there!
    *{uc $name} = sub () { $m }; # runtime sub definition
    }

foreach my $i ( 1 .. $data->$#* ) {
    say $data->[$i][ NAME() ]
    }
0 голосов
/ 19 марта 2020

$data требует некоторого преобразования в массив хэшей.

Первая строка $data - это имя поля, оно будет использоваться в качестве ключа ha sh.

Далее нам нужно:

пройти по другим строкам массива

  • создать га sh из ключей и данные

  • pu sh га sh в новый массив

при обработке всех данных

возврат $new ссылка на окончательный массив

В этот момент элементы $new ссылки на массив могут быть доступны как $new->[0]{id}.

Пожалуйста, смотрите следующий код, демонстрирующий как желаемое хранение данных может быть достигнуто.

ПРИМЕЧАНИЕ: этот код не зависит от новых perl функций и должен давать желаемый результат в системах даже 20 лет (где perl обновление невозможно для действительная причина).

Last for l oop демонстрирует печать всех элементов $new ссылки на массив.

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $debug = 0;                             # debug = 1 - Debug mode ON

my $data =  [
   [ 'id', 'name', 'value' ],
   [   23,  'foo',      77 ],
   [   44,  'bar',   'dfd' ],
];

my $new = convert($data);

say Dumper($new) if $debug;

for ( 0..$#{$new} ) {                      # walk through result array
    say '-' x 35;
    say 'Id:    ' . $new->[$_]{id};
    say 'Name:  ' . $new->[$_]{name};
    say 'Value: ' . $new->[$_]{value};
    say '-' x 35;
}

sub convert {
    my $data = shift;

    my @fields = @{$data->[0]};            # used as hash key

    my @data;

    for ( 1..$#{$data} ) {                # walk through $data starting from index=1
        my %hash;
        @hash{@fields} = @{$data->[$_]};  # store data in hash
        push @data, \%hash;               # store hash into array
    }

    return \@data;                        # return reference to array of hashes
}

Output

-----------------------------------
Id:    23
Name:  foo
Value: 77
-----------------------------------
-----------------------------------
Id:    44
Name:  bar
Value: dfd
-----------------------------------
...