Perl; cara memfilter hash berdasarkan nilai (menentukan kondisi)

Saya tidak terlalu ahli dalam bahasa Perl tetapi saya mengalami masalah yang tidak dapat saya perbaiki, bahkan setelah melakukan penelitian panjang di web. Singkatnya, saya memiliki hash seperti ini:

my %HoH = (
    chr1 => { start => 30, end => 55, },
    chr1 => { start => 18, end => 21, },
    chr1 => { start => 30, end => 80, }
);

Saya hanya ingin menemukan cara untuk memfilternya (maksud saya, mendapatkan hash baru dari hash dalam output) untuk nilai tertentu. Secara khusus, dengan interval, katakanlah 40-60, saya ingin hash hash baru dengan hanya elemen yang tumpang tindih dengan interval ini.

dengan kata lain saya ingin mendapatkan output:

my %HoH = (
    chr1 => { start => 30, end => 55, },
    chr1 => { start => 30, end => 80, }
);

Sebagai upaya pertama, saya berpikir untuk mencoba sesuatu seperti ini:

identifikasi lalu hapus semua elemen dengan "end" < 40 dan: identifikasi lalu hapus semua elemen dengan "start" > 60.

Jadi saya baru saja mencoba:

grep { $HoH{$_}{"end"} < 40 } keys(%HoH); 
delete $HoH{$_} for grep { $HoH{$_}{"end"} < 40} keys(%HoH);

Tetapi setelah filter pertama dari dua filter saya menemukan di output hanya elemen terakhir dan saya benar-benar tidak mengerti di mana kesalahannya:

hash size is 1
chr1: start=30 end=80 

dicetak dengan yang berikut:

my $len = keys %HoH;
print "hash size is $len\n";

foreach my $chr ( keys %HoH ) {
   print "$chr: ";
   for my $position ( keys %{ $HoH{$chr} } ) {
      print "$position=$HoH{$chr}{$position} ";
   }
   print "\n";
}

Tampaknya cukup rumit bagi saya kali ini, saya akan senang jika ada di antara Anda yang dapat membantu saya.


person Francesco Gandolfi    schedule 25.06.2015    source sumber
comment
Anda tidak mungkin memiliki struktur data tersebut karena semua kuncinya sama   -  person fugu    schedule 25.06.2015


Jawaban (2)


Seperti yang disebutkan oleh poster lain - masalah Anda bukan pada penggabungan hash Anda, melainkan hash tidak dapat memiliki kunci duplikat:

use strict;
use warnings;
use Data::Dumper;

my %HoH = (
    chr1 => { start => 30, end => 55, },
    chr2 => { start => 18, end => 21, },
    chr3 => { start => 30, end => 80, }
);


grep { $HoH{$_}{"end"} < 40 } keys(%HoH); 
delete $HoH{$_} for grep { $HoH{$_}{"end"} < 40} keys(%HoH);

print Dumper \%HoH;

Ini berfungsi dengan benar - perhatikan kunci hash yang berbeda. Namun saya akan mencatatnya - Anda mengulangi kunci Anda, mengambilnya, lalu menghapusnya. Mungkin lebih baik untuk:

foreach my $element ( keys %HoH ) {
    delete $HoH{$element}
        unless ( $HoH{$element}{start} < 40
              or $HoH{$element}{end}   > 60 );
}

print Dumper \%HoH;

Anda dapat melakukan apa yang Anda coba lakukan melalui serangkaian hash:

use strict;
use warnings;
use Data::Dumper;

my @AoH = (
    { start => 30, end => 55, },
    { start => 18, end => 21, },
    { start => 30, end => 80, }
);

print Dumper \@AoH;

my @filtered = grep { $_->{start} > 40 or $_->{end} < 60 } @AoH;
print Dumper \@filtered;

Catatan - dalam contoh asli Anda, baris grep/delete Anda melakukan hal yang sama, dan Anda dapat melakukan gabungan grep untuk menguji kedua kondisi tersebut.

person Sobrique    schedule 25.06.2015
comment
Ya, Anda benar, saya tidak ingat fitur hash yang tidak dapat menyimpan kunci duplikat di dalamnya. Solusi terakhir ini tampaknya sangat membantu dan dengan sedikit operasi! terima kasih banyak!!!! - person Francesco Gandolfi; 25.06.2015

Periksa hash Anda menggunakan Data::Dumper dan Anda akan melihat bahwa Anda tidak memiliki struktur data seperti yang Anda kira:

use strict;
use warnings;
use Data::Dumper;

my %HoH = (
          chr1 => {
                   start => 30,
                   end => 55,
          },
          chr1 => {
                   start => 18,
                   end => 21,
                   },
          chr1 => {
                   start => 30,
                   end => 80,
                   },
            );
            
print Dumper \%HoH;     

$VAR1 = {
          'chr1' => {
                      'start' => 30,
                      'end' => 80
                    }
        };

Apa yang terjadi adalah ia mengambil entri unik terakhir untuk chr1. Kunci hash harus unik

person fugu    schedule 25.06.2015