Вычисление количества массивов, содержащих элемент в perl - PullRequest
3 голосов
/ 27 июля 2010

Мне кажется, я слишком долго смотрел на это.У меня есть некоторые данные, которые выглядят так:

@a = (
    { name => 'ethan', depth => 0 },
    { name => 'victoria', depth => 1 },
    { name => 'stephen', depth => 2 },
    { name => 'christopher', depth => 3 },
    { name => 'isabella', depth => 2 },
    { name => 'ethan', depth => 3 },
    { name => 'emma', depth => 0 },
    { name => 'michael', depth => 1 },
    { name => 'olivia', depth => 2 },
    { name => 'alexander', depth => 3 },
    { name => 'stephen', depth => 1 },
    { name => 'sophia', depth => 0 },
    { name => 'michael', depth => 1 },
    { name => 'ava', depth => 1 },
    { name => 'joshua', depth => 2 }
);

Это простая «древовидная» структура данных.Каждый раз, когда «глубина» = 0, это начало нового «дерева».Что я хотел бы знать, так это то, во скольких из этих деревьев появляется каждое из названий?Желаемым результатом будет один хеш с именами в качестве ключа и подсчетом в качестве значения.

Изюминка в этом, если вы посмотрите внимательно, первое дерево дважды содержит имя 'ethan'.Любое дерево может иметь любое имя более одного раза, но это должно считаться только как 1, так как все они встречаются в одном и том же дереве.Тем не менее, у «Майкла» было бы число 2, поскольку он появляется в двух разных деревьях.

Я могу придумать несколько способов сделать это, но все они включают в себя несколько циклов и кажутся грубой силой и не элегантными.,Надеюсь, кто-то еще может найти лучшее решение.

Ответы [ 5 ]

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

Я не уверен на 100% в вашей спецификации проблемы - это правильный вывод?

  alexander 1
        ava 1
christopher 1
       emma 1
      ethan 1
   isabella 1
     joshua 1
    michael 2
     olivia 1
     sophia 1
    stephen 2
   victoria 1

Если так, то этот код, похоже, выполняет свою работу:

my @names = (
  # your @a above
);

my (%seen, %count);

for my $entry (@names) {
  if ($entry->{depth} == 0) {
    ++$count{$_} for keys %seen;
    %seen = ();
  }
  ++$seen{ $entry->{name} };
}

++$count{$_} for keys %seen;
print "$_\t$count{$_}\n" for sort keys %count;

то есть, просто ведите учет имен, которые перетасовываются только в глобальный счет, когда мы достигаем корня дерева.

1 голос
/ 27 июля 2010

Я думаю, что это самое короткое и простое решение

my (%count, %seen);
for my $e (@a) {
  %seen = () unless $e->{depth};
  $count{$e->{name}}++ unless $seen{$e->{name}}++;
}

print "$_ => $count{$_}\n" for sort keys %count;

Результат:

alexander => 1
ava => 1
christopher => 1
emma => 1
ethan => 1
isabella => 1
joshua => 1
michael => 2
olivia => 1
sophia => 1
stephen => 2
victoria => 1
1 голос
/ 27 июля 2010

Вот еще один способ:

#!/usr/bin/perl

use strict; use warnings;

my @data = (
    { name => 'ethan', depth => 0 },
    { name => 'victoria', depth => 1 },
    { name => 'stephen', depth => 2 },
    { name => 'christopher', depth => 3 },
    { name => 'isabella', depth => 2 },
    { name => 'ethan', depth => 3 },
    { name => 'emma', depth => 0 },
    { name => 'michael', depth => 1 },
    { name => 'olivia', depth => 2 },
    { name => 'alexander', depth => 3 },
    { name => 'stephen', depth => 1 },
    { name => 'sophia', depth => 0 },
    { name => 'michael', depth => 1 },
    { name => 'ava', depth => 1 },
    { name => 'joshua', depth => 2 }
);

my @trees;

for my $x ( @data ) {
    push @trees, {} unless $x->{depth};
    $trees[-1]->{ $x->{name} } = undef;
}

my @names = keys %{ { map { $_->{name} => undef } @data } };
for my $name ( sort @names ) {
    printf "%s appears in %d tree(s)\n",
        $name, scalar grep { exists $_->{$name} } @trees;
}
1 голос
/ 27 июля 2010
%count = ();
for (@a)
{
    %found = () unless $_->{depth};
    my $name = $_->{name};
    unless ($found{$name}) 
    {
        ++$count{$name};
        $found{$name} = 1;
    }
}
return %count;

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

0 голосов
/ 27 июля 2010

Чтобы решить эту проблему, я бы использовал рекурсивную функцию, которую вы отображаете в массиве. Край в случае - это глубина 0, в этом случае вы возвращаетесь. в противном случае удерживайте корневое имя, увеличивая его значение в хеш-счетчике, а затем вернитесь в поддерево, где вы увеличиваете это имя до тех пор, пока оно не совпадает с корневым именем, затем повторяете снова и т. д.

...