Массив хешей - PullRequest
       1

Массив хешей

2 голосов
/ 08 июля 2010

В 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'

Теперь мне нужно накопить количество, в котором заголовок и автор совпадают. В приведенной выше структуре для хэша с title = 1177 и author = '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 поисков. Просьба сообщить

Ответы [ 3 ]

4 голосов
/ 08 июля 2010
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 голос
/ 08 июля 2010

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

Я думаю, что важно сделать шаг назад и рассмотреть источник данных.Если данные поступают из базы данных, вам следует написать запрос SQL, чтобы он содержал одну строку для каждой комбинации «автор / название» с общим количеством в поле количества.Если вы читаете данные из файла, то вам следует либо прочитать их непосредственно в хеш, либо использовать Tie :: IxHash , если важен порядок.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...