Perl - удаляет дубликаты из ссылочного @array - PullRequest
0 голосов
/ 14 сентября 2018

Если у меня есть массив perl со следующей структурой (Дата, Время, Пользователь), отсортированный по пользователю:

open my $fh, '<', $file;
while( <$fh> ) {
  my @lines = split /\n/;
  my ($user, $y, $m, $d, $time) = $lines[0] =~ /\A(\w);(\d+)\/(\d+)\/(\d+);(\d+:\d+:\d+.\d+)/;   # Encapsulate values
  push @evts, { user => $user, date => "$y/$m/$d", time => $time};  # Array loader
} # This was missing.
close($fh);
my @by_usr = sort { $a->{user} cmp $b->{user} } @evts;

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

$VAR1 = {
          'time' => '08:08:36.120',
          'date' => '2018/08/06',
          'user' => 'USER1'
        };
$VAR2 = {
          'time' => '08:08:36.120',
          'date' => '2018/08/06',
          'user' => 'USER1'
        };
...(and more)

Я пробовал использовать уникальную функцию, но она не работает:

sub uniq {
    my %seen;
    grep !$seen{$_}++, @_;
}
my @unique_events = uniq (@by_usr);

Я доступен для любых разъяснений.

Ответы [ 3 ]

0 голосов
/ 14 сентября 2018

  • Вы сделали это намного сложнее, чем необходимо, используя регулярное выражение для анализа ваших данных: вам не нужно ничего, кроме split /;/

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

  • Вы также должны use strict и use warnings 'all' наверху каждой написанной вами Perl-программы и объявлять каждую переменную как можно ближе к ее первой точке использования с my

  • Вы должны всегда проверять, что open вызов был успешным, и вызывать die при любой ошибке со значением $! в строке штампа, чтобы сказать , почему это не удалось. Не имеет смысла продолжать работу большинства программ, если источник входных данных недоступен

Чтобы составить список уникальных записей, вы можете использовать функцию uniq_by из List::UtilsBy модуль. Это не основной модуль, и, вероятно, потребуется установить

Вот как бы я написал ваш код

use strict;
use warnings 'all';

use List::UtilsBy 'uniq_by';

my $file = 'evts.txt';

my @evts;

{
    open my $fh, '<', $file or die qq{Unable to open "$file" for input: $!};

    while ( <$fh> ) {
        chomp;

        my ( $user, $date, $time ) = split /;/;

        push @evts, {
            user => $user,
            date => $date,
            time => $time,
        };
    }
}

my @uniq = uniq_by { $_->{time} } @evts;

use Data::Dump;
dd \@uniq;

evts.txt

USER1;2018/08/06;08:08:36.120
USER1;2018/08/06;08:08:36.120

выход

[
  { date => "2018/08/06", time => "08:08:36.120", user => "USER1" },
]
0 голосов
/ 14 сентября 2018

Вы проверяете, являются ли ссылки на хеш-коды (только если они строковые) уникальными.Чтобы проверить уникальное время, просто сделайте это.

grep !$seen{$_->{'time'}}++, @_;

ответ simbabque проверяет, отличается ли любое значение, а не только время (при условии, что все хэши имеют одинаковые ключи и никакие значения не содержат ___).

0 голосов
/ 14 сентября 2018

Этот ответ предназначен для удаления полных дубликатов хеш-ссылок!

Если вы хотите, чтобы ключ time был уникальным, см. ysth's answer .

Ваша реализация uniq работает только в том случае, если эти ссылки указывают на одну и ту же память.Но, скорее всего, это не так, они просто содержат одно и то же значение.

Вам нужно взглянуть на значения внутри каждого элемента и построить свой uniq таким образом.Самый простой способ - просто объединить все значения известным способом.Вы можете добавить разделитель между полями, как при экспорте в CSV.Вы также можете хешировать это с помощью алгоритма дайджеста (например, MD5 , который в целом не рекомендуется, но здесь не должно быть высокой вероятности столкновения).

sub uniq {
  my %seen;
  grep {
    my $e = $_;
    my $key = join '___', map { $e->{$_}; } sort keys %$_;
    !$seen{$key}++
  } @_;
}

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

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

...