Обрабатывать массив и группировать строки одним полем из CSV - PullRequest
1 голос
/ 12 марта 2020

Отредактируйте для ясности после прочтения нескольких комментариев и ответов

Рассмотрите приведенный ниже CSV

Column1,Column2,Column3
John,Doe,Developer
Joey,Doe,Manager
Joe,Doe,Developer

Мне нужно иметь возможность прочитать CSV в perl, чтобы иметь возможность визуализировать следующее:

---- My list ----

-> Person 1
-> Name: John Doe
-> Role: Developer

-> Person 2
-> Name: Joey Doe
-> Role: Manager

-> Person 3
-> Name: Joe Doe
-> Role: Developer

--- Groups ---

-> Developer
-> Members: John Doe, Joe Doe

-> Manager
-> Members: Joey Doe

--- Roles ---
-> Developer, Manager

Редактировать

Конечная среда имеет ограничения - Perl 5.10 и не может быть обновлена ​​- Невозможно установить дополнительные модули - Пришлось использовать «print» вместо «say» «

Ответы [ 3 ]

2 голосов
/ 12 марта 2020

Не понимаю, почему вы хотите прочитать файл дважды. Прочитайте это один раз, выполняя материал для каждой строки и сохраняя группы одновременно, а затем l oop поверх них? Пример:

#!/usr/bin/env perl
use warnings;
use strict;
use feature qw/say postderef/;
no warnings qw/experimental::postderef/;
use Text::CSV_XS;

my $csv = Text::CSV_XS->new({binary => 1, auto_diag => 1});
my %roles;
# Read the header line
$csv->column_names($csv->getline(\*DATA));

say "---- My list ----";
my $n = 0;
while (my $row = $csv->getline(\*DATA)) {
    # Do stuff for the row:
    $n += 1;
    say "-> Person $n";
    say "-> name $row->[0] $row->[1]";
    say "-> Role: $row->[2]\n";
    # Group for later
    push @{$roles{$row->[2]}}, [ $row->@[0,1] ];
}

say "--- Groups ---\n";
for my $role (sort keys %roles) {
    say "-> $role";
    say "-> Members: ", join(", ", map { "@$_" } $roles{$role}->@*), "\n";
}

say "--- Roles ---";
say "-> ", join(", ", sort keys %roles);

__DATA__
Column1,Column2,Column3
John,Doe,Developer
Joey,Doe,Manager
Joe,Doe,Developer

(Обратите внимание на использование модуля Text :: CSV_XS для анализа данных CSV вместо использования split, чтобы сделать его более надежным, и postderef доступ к ссылкам массива, если вы еще не видели этот синтаксис. Лично я считаю, что в некоторых случаях он чище, чем традиционный синтаксис отмены ссылок).

2 голосов
/ 13 марта 2020

Следующий код читает данные, разделяется на поля и строит га sh с ключами персона , позиция , роль .

Один раз ха sh сформировал вывод данных на консоль в соответствии с клавишами га sh.

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

my %hash;
my %seen;
my @header = split ',', <DATA>;

chomp @header;

while(<DATA>) {
    next if /^$/;
    chomp;
    my %data;
    @data{@header} = split ',';

    push @{$hash{person}}, \%data;
    push @{$hash{Position}{$data{Position}}}, "$data{Last} $data{First}";
    if( ! $seen{$data{Position}} ) {
        $seen{$data{Position}} = 1;
        push @{$hash{Role}}, $data{Position};
    }
}

say "--- My list ----\n";

my $count = 0;
for my $person ( @{$hash{person}} ) {
    $count++;
    say "-> Person: $count";
    say "-> Name:   $person->{First} $person->{Last}";
    say "-> Role:   $person->{Position}\n";
}

say "---- Groups ----\n";

while( my($p,$m) = each %{$hash{Position}} ) {
    say "-> $p: ";
    say '-> Members: ' . join(', ',@{$m}) . "\n";
}

say "---- Roles ----";

say '-> ' . join(', ',@{$hash{Role}});

__DATA__
First,Last,Position
John,Doe,Developer
Mary,Fox,Manager
Anna,Gulaby,Developer

Вывод

--- My list ----

-> Person: 1
-> Name:   John Doe
-> Role:   Developer

-> Person: 2
-> Name:   Mary Fox
-> Role:   Manager

-> Person: 3
-> Name:   Anna Gulaby
-> Role:   Developer

---- Groups ----

-> Manager:
-> Members: Fox Mary

-> Developer:
-> Members: Doe John, Gulaby Anna

---- Roles ----
-> Developer, Manager

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

Обнаружена причина проблемы: входные данные eol в форме DOS \r\n в системе Linux. В Linux для некоторых версий perl [v5.22.1] - chomp удаляет только \n и оставляет \r, который остается частью ключа для Position \ r поля. Спасибо Шон за указание на это.

Было обнаружено, что не все версии perl сталкиваются с этой проблемой. Новое сообщение было инициировано для демонстрации проблемы.

Следующее исправление работает для Linux / Windows (не тестировалось на других платформах).

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

my $debug = 0;

say "
Perl:  $^V
OS: $^O
-------------------
" if $debug;         # for debug purpose to show perl version and OS

my %hash;
my %seen;
my @header = split ',', <DATA>;

$header[2] = snip_eol($header[2]);    # problem fix

while(<DATA>) {
    next if /^\s*$/;

    my $line = snip_eol($_);          # problem fix

    my %data;
    @data{@header} = split ',',$line;

    push @{$hash{person}}, \%data;
    push @{$hash{Position}{$data{Position}}}, "$data{First} $data{Last}";
    if( ! $seen{$data{Position}} ) {
        $seen{$data{Position}} = 1;
        push @{$hash{Role}}, $data{Position};
    }
}

#say Dumper($hash{Position});

my $count = 0;
for my $person ( @{$hash{person}} ) {
    $count++;
    say "-> Name:   $person->{First} $person->{Last}";
    say "-> Role:   $person->{Position}\n";
}

say "---- Groups ----\n";

while( my($p,$m) = each %{$hash{Position}} ) {
    say "-> $p";
    my $members = join(',',@{$m});
    say "-> Members: $members\n";
}

say "---- Roles ----";

say '-> ' . join(', ',@{$hash{Role}});

sub snip_eol {                            # problem fix
    my $data = shift;

    #map{ say "$_ => " . ord } split '', $data if $debug;
    $data =~ s/\r// if $^O eq 'linux';
    chomp $data;
    #map{ say "$_ => " . ord } split '', $data if $debug;

    return $data;
}

__DATA__
First,Last,Position
John,Doe,Developer
Mary,Fox,Manager
Anna,Gulaby,Developer
2 голосов
/ 12 марта 2020

Я не на 100% уверен в том, что вы хотите, но, похоже, это создает нечто вроде вашей структуры данных.

#!/usr/bin/perl

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

use Data::Dumper;

my @headers = split (/,/, <DATA>);
chomp(@headers);

my %data;

while (<DATA>) {
  chomp;
  my %config;
  @config{@headers} = split /,/;

  push @{$data{$config{Column3}}}, [ $config{Column1}, $config{Column2} ];
}

say Dumper \%data;

__DATA__
Column1,Column2,Column3
John,Doe,Developer
Joey,Doe,Manager
Joe,Doe,Developer

Вывод:

$VAR1 = {
          'Developer' => [
                           [
                             'John',
                             'Doe'
                           ],
                           [
                             'Joe',
                             'Doe'
                           ]
                         ],
          'Manager' => [
                         [
                           'Joey',
                           'Doe'
                         ]
                       ]
        };

И я только необходимо прочитать файл один раз.

Обновление: А вот версия со всеми необходимыми данными.

#!/usr/bin/perl

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

use Data::Dumper;

my @headers = split (/,/, <DATA>);
chomp(@headers);

my %data;

say "---- My list ----\n";

while (<DATA>) {
  chomp;
  my %config;
  @config{@headers} = split /,/;

  push @{$data{$config{Column3}}}, [ $config{Column1}, $config{Column2} ];

  say "-> Person ", $. - 1;
  say "-> Name: $config{Column1}, $config{Column2}";
  say "-> Role: $config{Column3}\n";
}

say "--- Groups ---\n";

for (keys %data) {
  say "-> $_";
  say "-> Members: ", join ', ', map { "@$_" } @{$data{$_}};
  say '';
}

say "--- Roles ---\n";
say join ', ', keys %data;
say '';

__DATA__
Column1,Column2,Column3
John,Doe,Developer
Joey,Doe,Manager
Joe,Doe,Developer
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...