2010-07-08 8 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», но нет никакого способа накопления по всему массиву без доступа к каждому члену массива. –

+1

Пожалуйста, добавьте [perldoc perldsc] (http://perldoc.perl.org/perldsc.html) и [perldoc perlreftut] (http://perldoc.perl.org/perlreftut.html) в свой список чтения. – Ether

ответ

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 love – Daenyth

+2

@Daenyth Обычно да, но в этом случае это выглядит не так хорошо. –

1

Если вы не хотите, N-запросов, то вам необходимо хэш-функции - однако вы должны магазин их с этой функцией хэша. К тому времени, когда у вас есть их в списке (или массиве), уже слишком поздно. Вам либо повезет, все время, или у вас будет поиск 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 минут один раз в день), и вы можете столкнуться с проблемами памяти.

Смежные вопросы