Пересечение многомерного хеша в Perl

Один из способов сделать это - создать карту идентификаторов компаний и идентификаторов фабрики, а затем просто выполнить итерацию по массиву компаний и добавить соответствующую фабрику к объекту компании, например, так:

Большое преимущество это то, что ваши фабричные поиски будут O(1), и это O(n), чтобы построить карту. Весь ваш алгоритм будет O(n). Это делает это чрезвычайно быстро даже для очень больших наборов данных.

let factories = [
    {
        id: 1,
        name: "Xintang",
        short: "xin",
        companies: [0, 4, 101,198]
    },
    {
        id: 2,
        name: "Ohio Plant",
        short: "OHP",
        companies: [22, 27]
    },
    {
        id: 3,
        name: "Cincy",
        short: "Cin",
        companies: []
    }
];

let companies = [
    {
        id: 0,
        fund: "79588.96",
        name: "Microsoft"
    },
    {
        id: 1,
        fund: "166727.06",
        name: "Comcast"
    },
    {
        id: 2,
        fund: "131206.88",
        name: "Apple"
    },
    {
        id: 3,
        fund: "74095.75",
        name: "HP"
    },
    {
        id: 4,
        fund: "142556.86",
        name: "Dell"
    }
];

var factoryMap = factories.reduce((res, curr) => {
  return Object.assign(res, curr.companies.reduce((_res, _curr) => (_res[_curr] = curr.name, res), {}))
}, {});

var mappedCompanies = companies.map(company => Object.assign(company, {factory: factoryMap[company.id] || ""}));

console.log(mappedCompanies);

5
задан brian d foy 8 October 2008 в 07:00
поделиться

8 ответов

Вот опция. Это работает на произвольно глубокие хеши:

sub deep_keys_foreach
{
    my ($hashref, $code, $args) = @_;

    while (my ($k, $v) = each(%$hashref)) {
        my @newargs = defined($args) ? @$args : ();
        push(@newargs, $k);
        if (ref($v) eq 'HASH') {
            deep_keys_foreach($v, $code, \@newargs);
        }
        else {
            $code->(@newargs);
        }
    }
}

deep_keys_foreach($f, sub {
    my ($k1, $k2) = @_;
    print "inside deep_keys, k1=$k1, k2=$k2\n";
});
11
ответ дан 18 December 2019 в 05:27
поделиться

Подготовьте тот: не изобретать велосипед :)

Быстрый поиск на CPAN подбрасывает невероятно полезные Данные:: Обход. Определите подпрограмму для обработки каждого узла, и Вы отсортированы

use Data::Walk;

my $data = { # some complex hash/array mess };

sub process {
   print "current node $_\n";
}

walk \&process, $data;

И Ваш дядя Bob's. Обратите внимание, что, если Вы хотите передать его хеш для обхода, необходимо будет передать ссылку на него (см. perldoc perlref), следующим образом (иначе это попытается обработать ключи хеша также!):

walk \&process, \%hash;

Для более комплексного решения (но тяжелее найти на первый взгляд в CPAN), используйте Данные:: Посетитель:: Обратный вызов или его родительский модуль - это имеет преимущество давания Вам более прекрасный контроль того, что Вы делаете, и (только для дополнительного уважения) записан с помощью Американского лося.

12
ответ дан 18 December 2019 в 05:27
поделиться

Это звучит мне как будто Данные:: Дайвер или Данные:: Посетитель является хорошими подходами для Вас.

6
ответ дан 18 December 2019 в 05:27
поделиться

Следует иметь в виду, что списки Perl и хеши не имеют размеров и так не могут быть многомерными. То, что Вы можете иметь, является объектом хеша, который установлен сослаться на другой хеш или список. Это может использоваться для создания поддельных многомерных структур.

После того как Вы понимаете это, вещи становятся легкими. Например:

sub f($) {
  my $x = shift;
  if( ref $x eq 'HASH' ) {
    foreach( values %$x ) {
      f($_);
    }
  } elsif( ref $x eq 'ARRAY' ) {
    foreach( @$x ) {
      f($_);
    }
  }
}

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

Один изящный способ сделать, в чем Вы нуждаетесь, состоит в том, чтобы передать ссылку кода, которую назовут из f. При помощи sub разработка прототипа Вы могли даже выполнить вызовы, похожи на grep Perl и функции карты.

2
ответ дан 18 December 2019 в 05:27
поделиться

Можно также уклониться от многомерных массивов, если у Вас всегда есть все значения ключа, или Вы просто не должны получать доступ к индивидуальным уровням как к отдельным массивам:

$arr{"foo",1} = "one";
$arr{"bar",2} = "two";

while(($key, $value) = each(%arr))
{
    @keyValues = split($;, $key);
    print "key = [", join(",", @keyValues), "] : value = [", $value, "]\n";
}

Это использует нижний разделитель "$"; как разделитель для нескольких значений в ключе.

2
ответ дан 18 December 2019 в 05:27
поделиться

Нет никакого способа получить семантику, которую Вы описываете потому что foreach выполняет итерации по списку одного элемента за один раз. Вы должны были бы иметь deep_keys возвратите LoL (список списков) вместо этого. Даже это не работает в общем случае произвольной структуры данных. Там мог варьироваться уровни подхешей, некоторые уровни могли быть судьями МАССИВА и т.д.

Способ Perlish сделать это состоял бы в том, чтобы записать функцию, которая может обойти произвольную структуру данных и применить обратный вызов в каждом "листе" (то есть, нессылочное значение). ответ bmdhack является начальной точкой. Точная функция варьировалась бы зависящий та, что Вы хотели сделать на каждом уровне. Это довольно просто, если все, о чем Вы заботитесь, является листовыми значениями. Вещи становятся более сложными, если Вы заботитесь о ключах, индексах, и т.д. который получил Вас к листу.

1
ответ дан 18 December 2019 в 05:27
поделиться

Достаточно легко, если все, что Вы хотите сделать, воздействуют на значения, но если Вы хотите воздействовать на ключи, Вам нужны спецификации того, как уровни будут восстанавливаемыми.

a. Например, Вы могли указать ключи как "$level1_key.$level2_key.$level3_key"- или любой разделитель, представляя уровни.

b. Или у Вас мог быть список ключей.

Я рекомендую последнему.

  • Уровень может быть понят под @$key_stack

  • и самый локальный ключ $key_stack->[-1].

  • Путь может быть восстановлен: join( '.', @$key\_stack )

Код:

use constant EMPTY_ARRAY => [];
use strict;    
use Scalar::Util qw<reftype>;

sub deep_keys (\%) { 
    sub deeper_keys { 
        my ( $key_ref, $hash_ref ) = @_;
        return [ $key_ref, $hash_ref ] if reftype( $hash_ref ) ne 'HASH';
        my @results;

        while ( my ( $key, $value ) = each %$hash_ref ) { 
            my $k = [ @{ $key_ref || EMPTY_ARRAY }, $key ];
            push @results, deeper_keys( $k, $value );
        }
        return @results;
    }

    return deeper_keys( undef, shift );
}

foreach my $kv_pair ( deep_keys %$f ) { 
    my ( $key_stack, $value ) = @_;
    ...
}

Это было протестировано в Perl 5.10.

1
ответ дан 18 December 2019 в 05:27
поделиться

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

Не зная, каковы Ваши требования к данным на самом деле, я собираюсь вслепую указать на Вас на учебное руководство для Дерева:: DAG_Node для запущения Вас.

Тем не менее Лесоруб корректен, hashwalk наиболее легко сделан с рекурсией. Вот пример для запущения Вас, если Вы чувствуете, что абсолютно необходимо решить проблему с хешами хешей хешей:

#!/usr/bin/perl
use strict;
use warnings;

my %hash = (
    "toplevel-1" => 
    { 
        "sublevel1a"  => "value-1a",
        "sublevel1b"  => "value-1b"
    },
    "toplevel-2" =>
    {
        "sublevel1c" => 
        {
            "value-1c.1" => "replacement-1c.1",
            "value-1c.2" => "replacement-1c.2"
        },
        "sublevel1d" => "value-1d"
    }
);

hashwalk( \%hash );

sub hashwalk
{
    my ($element) = @_;
    if( ref($element) =~ /HASH/ )
    {
        foreach my $key (keys %$element)
        {
            print $key," => \n";
            hashwalk($$element{$key});
        }
    }
    else
    {
        print $element,"\n";
    }
}

Это произведет:

toplevel-2 => 
sublevel1d => 
value-1d
sublevel1c => 
value-1c.2 => 
replacement-1c.2
value-1c.1 => 
replacement-1c.1
toplevel-1 => 
sublevel1a => 
value-1a
sublevel1b => 
value-1b

Обратите внимание, что Вы, которых предсказывает CAN НЕ, в каком порядке будут пересечены элементы хеша, если Вы не свяжете хеш через Связь:: IxHash или подобный — снова, если Вы собираетесь пройти так много работы, я рекомендую древовидный модуль.

1
ответ дан 18 December 2019 в 05:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: