Выборочный подсчет значений полей с разделителями и создание хэша с использованием карты - PullRequest
1 голос
/ 26 октября 2009

У меня есть текстовый файл с разделителями, содержащий, помимо прочего, дату и число, обозначающее последовательность строк в другом месте программы. Я надеюсь, что из этого файла создайте хеш, используя год в качестве ключа и значение, являющееся максимальной последовательностью для этого года (по сути, мне нужно реализовать автоинкрементный ключ в год), например, с

2000|1
2003|9
2000|5
2000|21
2003|4

Я бы закончил с таким хешем, как:

%hash = {
    2000 => 21,
    2003 => 9
}

Мне удалось разделить файл на части года и последовательности (я думаю, не очень хорошо):

my @dates = map {
    my @temp = split /\|/;
    join "|", (split /\//, $temp[1])[-1], $temp[4] || 0; #0 because some records
                                                         #mightn't have a sequence
} @info

Могу ли я сделать что-то лаконичное, чтобы создать хеш, используя эти данные?

Спасибо

Ответы [ 5 ]

3 голосов
/ 26 октября 2009

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

my %hash = 
    map @$_,
    sort { $a->[1] <=> $b->[1] }
    map {
        my @temp = split /\|/;
        my $date = (split /\//, $temp[1])[-1];
        my $seq = $temp[4] || 0; #0 because some records mightn't have a sequence
        [ $date, $seq ]
    } @info;

Но просто итерация с помощью for и установка хэша, только если текущая последовательность выше предыдущего максимума для этой даты, вероятно, является лучшей идеей.

Будь осторожен с этими {}; где ты сказал

%hash = {
    2000 => 21,
    2003 => 9
}

Вы имели в виду () вместо этого (или для назначения ссылки $hash), поскольку {} создает анонимный хэш и возвращает ссылку на него.

2 голосов
/ 26 октября 2009

map работает с каждым элементом в списке и создает список результатов для передачи. Таким образом, вы не можете выполнять те проверки, которые вам нужны (сохранить максимальное значение последовательности), пока вы не создадите чистый хэш, в котором содержатся именно те данные, которые вы пытаетесь создать в качестве возвращаемого значения `map .

my %results = map {

    my( $y, $s ) = split '[|]', $_;

    seq_is_gt_year_seq( $y, $s ) 
       ? ( $y, $s )
       : ();
} @year_pipe_seq;

Для реализации seq_is_gt_year_seq() нам понадобится создать временный хеш, который будет хранить каждый год и его максимальное значение последовательности для поиска.

Вы должны использовать подход, который строит поиск постепенно, как цикл for или while.

2 голосов
/ 26 октября 2009

Вот как вы могли бы написать это .. не слишком уверен, почему вы хотите / должны использовать карту (пожалуйста, объясните)

#!/usr/bin/perl -w
use strict;
use warnings;

my %hash;

while(<DATA>) {
   chomp();
   my ($year,$sequence)=split('\|');

    $sequence = 0 unless (defined ($sequence));       

    next if (exists $hash{$year} and $sequence < $hash{$year});

   $hash{$year}=$sequence;
}


__DATA__
2000|1
2003|9
2000|5
2000|21
2003|4

Я добавил $ sequence = 0, если не определено ($ sequence); из-за этого комментария в вашем фрагменте. Я полагаю, что я мог бы понять ваше намерение там (либо формат ввода действителен / согласован, либо нет)

1 голос
/ 26 октября 2009

map { BLOCK } LIST всегда обычно (если BLOCK иногда не оценивается как пустой список) возвращает список, который меньше всего равен LIST и может не быть Если вы хотите просто перезаписать дубликаты ключей самыми последними данными. Что-то вроде:

    my %hash;
    for (@info) {
        my @temp = split /\|/;
        my $key = (split /\//, $temp[1]);
        my $value = $temp[4] || 0;
        $hash{$key} = $value unless defined $hash{$key} && $hash{$key}>=$value;
    }

будет работать. Последняя строка условно обновляет хеш-таблицу, что вы не можете (или, по крайней мере, не можете сделать очень удобно) внутри оператора map.

0 голосов
/ 27 октября 2009

Если есть вероятность, что вы сможете выполнить эту обработку при чтении файла, я бы это сделал. Примерно так:

my %year_count;
while (my $line = <$fh>){
    chomp $line;
    my ($year, $num) = split /\|/, $line;
    if ($num > $year_count{$year} || !defined $year_count{$year})
        $year_count{$year} = $num;
    }
}

Если вы хотите использовать массив, карта на самом деле не лучший выбор (поскольку вы не преобразуете список, вы обрабатываете его до чего-то другого). Честно говоря, наиболее разумная обработка массива, вероятно, будет такой же, как указано выше, но вместо этого в foreach:

my %year_count;
foreach my $line (@info){
    my ($year, $num) = split /\|/, $line;
    if ($num > $year_count{$year} || !defined $year_count{$year})
        $year_count{$year} = $num;
    }
}
...