Проблема со сравнением строк и ссылками в Perl

Вот с чего я начинаю. Я читаю массивы из базы данных по одному, используя цикл while. Я хочу подобрать элементы из базы данных, которые являются дубликатами (в определенных полях). Я хочу сохранить только те элементы, которые уникальны в этих полях. Затем я хочу распечатать данные, которые я сохранил определенным образом. Я создал код, который, как я думал, сделает это, но он дает мне все, включая элементы, которые дублируются в поле. Я искал и искал, и я не могу понять это, я думаю, как perl noob, мне не хватает чего-то простого. Код выглядит следующим образом:

my @uniques = ();
my $output;

while (my @itemArray = $sth->fetchrow_array() ) {
    my $duplicateFlag = 0;  
    foreach (@uniques){
        if(  ($itemArray[3] eq "$_->[3]") and ($itemArray[4] eq "$_->[4]")
               and ($itemArray[5] eq "$_->[5]" ) and ($itemArray[6] eq "$_->[6]" )
               and ($itemArray[7] eq "$_->[7]" ) and ($itemArray[8] == "$_->[8]" ) ){
            $duplicateFlag = 1;
        }
    }
    if( $duplicateflag == 0){
        $refToAdd = \@itemArray;
        push(@uniques, $refToAdd);
        $output .= "$itemArray[3]" . "\t$itemArray[8]" . "\t$itemArray[5]" . "\t$itemArray[7]\n";
    }
}
print $output

person Brian    schedule 09.05.2011    source источник
comment
код выглядит разумно; Вы можете показать данные, которые заставляют его плохо себя вести? в идеале, как вывод из Data::Dumper::Dumper({uniques=>\@uniques,refToAdd=>$refToAdd}) сразу после назначения $refToAdd для строки, которая должна была рассматриваться как дубликат, но не была   -  person ysth    schedule 10.05.2011
comment
Вы можете использовать этот совет для дедупликации в SQL. Это проще и намного эффективнее. stackoverflow.com/questions/306743/   -  person Schwern    schedule 10.05.2011
comment
@Schwern, похоже, это отличный способ сделать это. Я пытался компенсировать свой супер-нубизм в SQL нубизмом в perl. Я мог бы попытаться реализовать это, используя этот оператор SQL позже. Я буквально не видел ни одного оператора SQL до 2 дней назад, так что все это немного ново. Большое спасибо за предложение отличной стратегии.   -  person Brian    schedule 10.05.2011


Ответы (4)


Вы получаете все дубликаты, потому что $duplicateflag не определен в строке 13. Запуск синтаксической проверки вашего скрипта с включенным use strict; use warnings; приводит к следующему предупреждению:

Global symbol "$duplicateflag" requires explicit package name at t10.pl line 18.

И если мы внимательно изучим ваше определение «этой» переменной, оно гласит:

my $duplicateFlag = 0;

Другими словами, у вас есть заглавная буква F, что означает, что $duplicateflag не является той же самой переменной, что и $duplicateFlag. Проверка undef == 0 по-прежнему выдает истинное значение и вызывает ложное срабатывание.

Чтобы избежать подобных проблем, всегда запускайте свои скрипты с

use strict;
use warnings;
person TLP    schedule 09.05.2011
comment
Вау, не могу поверить, что пропустил это. Большое спасибо! Я усвоил урок о том, как не использовать strict и warning. - person Brian; 10.05.2011

Один из вариантов: используйте хэши, чтобы определить, был ли элемент замечен ранее. Немного упрощен из вашего кода:

my %dupHash;
while (my @itemArray = $sth->fetchrow_array() ) {
    my $uniqueItem = itemArray[4];
    if (not exists $dupHash{$uniqueItem}) {
        print "Item $uniqueItem\n";
        $dupHash{$uniqueItem} = \@itemArray;
    }
}

Хорошо, это очень упрощенно, но вы поняли идею. Используя хэш со значениями, которые я хочу проверить на уникальность, я могу избежать двойного цикла и O2 эффективность алгоритма. (Блин! Все эти годы в колледже, наконец, окупились!).

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

 # Probably could use join to make it more efficient...
 my $uniqueKay = "$item[3]:$item[4]:$item[5]:$item[6]:$item[7]:$item[8]";
 if (not exists $dupHash{$uniqueKey}) {

Главное — избегать перебора всех уникальных элементов снова и снова, если вы можете хранить их в хэше.

person David W.    schedule 09.05.2011
comment
Моя проблема была связана с заглавными буквами, так что это исправлено, фу! В любом случае, после того, как я заработал, я подумал, что попробую переделать его таким образом, и он заработал без проблем. Мне очень нравится эта стратегия, я видел несколько мест, где люди говорят, что используют хеш для дубликатов, но я не понял, как я могу использовать его для моей конкретной проблемы. Никогда бы не подумал, что можно вот так соединить предметы, честно говоря, я упустил это из виду. Большое спасибо, что указали на это. - person Brian; 10.05.2011
comment
Вы должны добавить use strict и используйте предупреждения в своих скриптах. Это поможет вам поймать эти ошибки капитализации. Использование хэшей для отслеживания уже установленных данных — это простой и быстрый способ избежать дублирования. - person David W.; 10.05.2011

Возможно:

$itemArray[8] == "$_->[8]"

должно быть:

$itemArray[8] eq "$_->[8]"

чтобы соответствовать всем остальным.

Еще одна вещь, которая может решить вашу проблему, — это удаление кавычек вокруг «$_->[8]». Зависит от того, какие у вас данные.

person ADW    schedule 09.05.2011
comment
Я думаю, что == или eq будут работать, так как это поле является числовым. Я просто выбрал == из-за этого. - person Brian; 10.05.2011
comment
Оператор интеллектуального сопоставления, возможно, можно как-то использовать... с этим вы можете сделать if (@a1 ~~ @a2) - person TLP; 10.05.2011

SQL group by или select distinct — это способ базы данных SQL сохранять уникальные строки.

Но если вы собираетесь делать это на Perl, я согласен, что хеши и ключи — это то, что вам нужно. Однако любой разделитель, который мы могли бы предложить, также может быть в данных. Это дает вам возможность для неоднозначного совпадения. Один основанный на хеше метод является однозначным и использует естественные структуры Perl для разграничения ваших полей.

Именно поэтому я представляю следующее.

my %uniq;

while ( my @r = $sth->fetchrow_array()) {
    next unless $uniq{ $r[3] }{ $r[4] }{ $r[5] }{ $r[6] }{ $r[7] }{ $r[8] }++; 
    # unique code here
    #...
}

Это устранило бы временную переменную. Таким образом, устранен результат ошибки во временной переменной. Однако для этих целей лучше подходит USUW: USUW="use strict; use warnings;".

person Axeman    schedule 10.05.2011