2010-07-08 210 views
2

在Perl中,我有散列 像陣列散列

0 HASH(0x98335e0) 
    'title' => 1177 
    'author' => 'ABC' 
    'quantity' => '-100' 


1 HASH(0x832a9f0) 
    'title' => 1177 
    'author' => 'ABC' 
    'quantity' => '100' 

2 HASH(0x98335e0) 
    'title' => 1127 
    'author' => 'DEF' 
    'quantity' => '5100' 


3 HASH(0x832a9f0) 
    'title' => 1277 
    'author' => 'XYZ' 
    'quantity' => '1030' 

數組現在我需要積累,其中標題和作者是相同的數量。 在與標題= 1177和作者=哈希上述結構「ABC」的數量可以累積成一個,並應在整個結構看起來像下面

0 HASH(0x98335e0) 
    'title' => 1177 
    'author' => 'ABC' 
    'quantity' => 0 

1 HASH(0x98335e0) 
    'title' => 1127 
    'author' => 'DEF' 
    'quantity' => '5100' 

2 HASH(0x832a9f0) 
    'title' => 1277 
    'author' => 'XYZ' 
    'quantity' => '1030' 

什麼是我能做到這一點的積累,這樣的最佳方式它被優化?數組元素的數量可能非常大。我不介意添加一個額外的密鑰來幫助相同的哈希,但我不想n查找。請告知

+0

你說「我不想n查找」,但是沒有訪問數組的每個成員都無法在整個數組中累積。 – 2010-07-08 15:48:27

+1

請將[perldoc perldsc](http://perldoc.perl.org/perldsc.html)和[perldoc perlreftut](http://perldoc.perl.org/perlreftut.html)添加到您的閱讀列表中。 – Ether 2010-07-08 16:12:51

回答

4
my %sum; 
for (@a) { 
    $sum{ $_->{author} }{ $_->{title} } += $_->{quantity}; 
} 

my @accumulated; 
foreach my $author (keys %sum) { 
    foreach my $title (keys %{ $sum{$author} }) { 
    push @accumulated => { title => $title, 
          author => $author, 
          quantity => $sum{$author}{$title}, 
         }; 
    } 
} 

不知道是否map使它看起來更好:

my @accumulated = 
    map { 
    my $author = $_; 
    map { author => $author, 
      title => $_, 
      quantity => $sum{$author}{$_}, 
     }, 
     keys %{ $sum{$author} }; 
    } 
    keys %sum; 
+1

這個例子只是癢了一些地圖/ grep的愛 – Daenyth 2010-07-08 16:36:03

+2

@Daenyth通常是的,但在這種情況下看起來不太好。 – 2010-07-08 17:38:44

1

如果你不想ñ查找,那麼你需要一個哈希函數 - 但是你需要他們與該散列函數。當你將它們放入列表(或數組)中時,就太遲了。你要麼走運,一直在,否則你將有N個查找。

或者插入他們進入散列上述下方。混合解決方案是將定位器作爲項目0存儲在列表/數組中。

my $lot = get_lot_from_whatever(); 
my $tot = $list[0]{ $lot->{author} }{ $lot->{title} }; 
if ($tot) { 
    $tot->{quantity} += $lot->{quantity}; 
} 
else { 
    push @list, $list[0]{ $lot->{author} }{ $lot->{title} } = $lot; 
}   

以前的所有,我們將重新格式化的

首先,使其可讀。

[ { title => 1177, author => 'ABC', quantity => '-100' } 
, { title => 1177, author => 'ABC', quantity => '100' } 
, { title => 1127, author => 'DEF', quantity => '5100' } 
, { title => 1277, author => 'XYZ', quantity => '1030' } 
] 

接下來,你需要打破這個問題。你想按作者和標題分組數量爲 。所以你需要這些東西唯一識別這些地段。 要重複說明,您需要名稱的組合以識別實體。因此,你需要一個散列來標識按名稱排列的東西。

既然我們有兩件事情,雙散列是一個很好的方法來做到這一點。

my %hash; 
foreach my $lot (@list) { 
    $hash{ $lot->{author} }{ $lot->{title} } += $lot->{quantity}; 
} 
# consolidated by hash 

要將其變回列表中,我們需要對這些級別進行分解。

my @consol 
    = sort { $a->{author} cmp $b->{author} || $a->{title} cmp $b->{title} } 
     map { 
      my ($a, $titles) = @$_; # $_ is [ $a, {...} ] 
      map { +{ title => $_, author => $a, quantity => $titles->{$_} } 
      keys %$titles; 
     } 
     map { [ $_ => $hash{$_} ] } # group and freeze a pair 
     keys %hash 
    ; 

# consolidated in a list. 

然後你回來了,我甚至爲你排序。當然,你也可以通過 來排序 - 發佈者就是這樣 - 遞減的數量。

sort { $b->{quantity} <=> $a->{quantity} 
    || $a->{author} cmp $b->{author} 
    || $a->{title} cmp $b->{title} 
    } 
0

我認爲重要的是退一步考慮數據的來源。如果數據來自數據庫,那麼您應該編寫SQL查詢,以便爲每個作者/標題組合使用數量字段中總數量的一行。如果您正在讀取文件中的數據,那麼您應該直接將其讀取到散列中,或者如果訂單很重要,則使用Tie::IxHash

一旦你有像你這樣的hashrefs數組中的數據,你將不得不創建一個輔助數據結構,並做一大堆查找,其成本可能會主宰你的程序的運行時間(不如果它每天運行15分鐘,那麼這很重要),並且可能會遇到內存問題。