帮助遍历/排序复杂的Perl数据结构(HoH with AoH fun)

帮助遍历/排序复杂的Perl数据结构(HoH with AoH fun),perl,perl-data-structures,Perl,Perl Data Structures,我已经把头撞在墙上几个小时了 我有一个类似这样的数据结构(“data::Dumper”的输出)。这是我自己的错,我在解析输入时创建了数据结构 print Dumper $data; ___OUTPUT___ $VAR = { 'NAME' => { 'id' => '1234', 'total' => 192, 'results' => {

我已经把头撞在墙上几个小时了

我有一个类似这样的数据结构(“data::Dumper”的输出)。这是我自己的错,我在解析输入时创建了数据结构

print Dumper $data;

___OUTPUT___
$VAR = { 'NAME' => {
                    'id' => '1234',
                    'total' => 192,
                    'results' =>  { 
                                     'APPLE'   => 48 ,
                                     'KUMQUAT' => 61 ,
                                     'ORANGE'  => 33 ,
                                  }
                   }

       }
  • 有数千个“名称”键
  • 只有一个“id”和一个“总计”
  • “结果”散列中可能有一个或多个键/值对
我想打印一个逗号分隔的列表,首先按“total”排序,然后按“results”数组中每个散列的值排序

以下代码用于从已存储的数据结构打印CSV

use strict;
use warnings;
# [...lots of other stuff...]

open (my $fh, >out.csv);
print $fh "Name, ID, Label, Count, Total\n";

foreach ( sort { $data->{$b}->{total} <=> $data->{$a}->{total} }
    keys %{$data} )
{
    my $name = $_;
    foreach (
        sort {
            $data->{$name}->{results}->{$a} <=> $data->{$name}->{results}
              ->{$b}
        } values %{ $data->{$name}->{results} }
      )
    {

        print $fh $name . ","
          . $data->{$name}->{id} . "," . "'"
          . $_ . ","
          . $data->{$name}->{results}->{$_} . "," . "\n";
    }
    print $fh $name . ","
      . $data->{$name}->{id} . "," . "," . ","
      . $data->{$name}->{total} . "\n";
}

close($fh);
然而!我注意到我遇到了键冲突(在“结果”散列中),由于我需要保存和报告所有数据,我决定尝试将“结果”更改为一个散列数组

print Dumper $data;

___OUTPUT___
$VAR = { 'NAME' => {
                    'id' => '1234',
                    'total' => 192,
                    'results' => [
                                   { 'APPLE'   => 48 },
                                   { 'KUMQUAT' => 61 },
                                   { 'ORANGE'  => 33 },
                                   { 'APPLE'   => 50 },
                                 ]
                   }
       }
  • 有数千个“名称”键
  • 只有一个“id”和一个“总计”
  • “results”数组中可能有一个或多个哈希
  • “results”数组中的每个哈希将只有一个名称/值对
不管有没有人读过这篇文章,我得说把它写下来是相当有治疗作用的,所以我会继续…;-)

对于新的数据结构,排序/打印代码有问题

use strict;
use warnings;
# [...lots of other stuff...]

open (my $fh, >out.csv);
print $fh "Name, ID, Label, Count, Total\n";

foreach ( sort { $data->{$b}->{total} <=> $data->{$a}->{total} }
    keys %{$data} )
{
    my $name = $_;
    foreach (
        sort {
            $data->{$name}->{results}->{$a} <=> $data->{$name}->{results}
              ->{$b}
        } values %{ $data->{$name}->{results} }
      )
    {
    # .... HELP ME FOR THE LOVE OF ALL THAT IS GOOD IN THE WORLD! ....
    # I'm at the point now where my brain is starting to slowly dribble from my
    # ears...
    }
    print $fh $name . "," 
      . $data->{$name}->{id} . "," . "," . ","
      . $data->{$name}->{total} . "\n";
}

close($fh);
使用严格;
使用警告;
#[…很多其他东西…]
打开(my$fh,>out.csv);
打印$fh“名称、ID、标签、计数、总数\n”;
foreach(sort{$data->{$b}->{total}$data->{$a}->{total}
键%{$data})
{
我的$name=$\ux;
弗雷奇(
分类{
$data->{$name}->{results}->{$a}$data->{$name}->{results}
->{$b}
}值%{$data->{$name}->{results}
)
{
#…为了世界上一切美好的事物的爱,帮助我吧。。。。
#现在我的大脑开始慢慢地从我的大脑中运出
#耳朵。。。
}
打印$fh$名称。“,”
.$data->{$name}->{id}.,“,”和“,”
.$data->{$name}->{total}.\n;
}
收盘价($fh);
如果你读到这里,我向你致敬。 如果你能帮忙,我为你鼓掌

如果有人对数据结构的替代格式有建议,请告诉我!(如果您感兴趣,我将使用“触发器”操作符捕获源文件的块,然后逐行使用这些块来创建数据结构。我还调用外部程序来计算某些内容(没有Perl等价物)并存储结果。)


多亏了

这样的数据结构可能会降低一层复杂性:

Name, ID,  Label,   Count, Total
foo, 1234, ORANGE,    33,
foo, 1234, APPLE,     48,
foo, 1234, KUMQUAT,   61,
foo, 1234,     ,        ,  142
bar, 1101, BIKE,      20,
bar  1101,     ,        ,  20
$VAR = [
         {
           'name' => 'foo',
           'id' => '1234',
           'total' => 192,
           'results' => [
                          { 'label' => 'APPLE', 'score' => 48 },
                          { 'label' => 'KUMQUAT', 'score' => 61 },
                          { 'label' => 'ORANGE', 'score' => 33 },
                          { 'label' => 'APPLE', 'score' => 50 },
                        ]
         },
       ];
因此,如果我还记得我的Perl,您会看到如下内容:

foreach my $row ( sort( $a->{'total'} <=> $b->{'total'} ) @data ) {

    foreach my $result ( sort( $a->{'score'} <=> $b->{'score'} ) @{$row->{'results'}} ) {

    }

}
foreach my$行(排序($a->{'total'}$b->{'total'})@data){
foreach my$result(排序($a->{'score'}$b->{'score'})@{$row->{'results'}){
}
}
严格使用;
使用警告;
#[…很多其他东西…]
打开(my$fh,“>”,“out.csv”);
打印$fh“名称、ID、标签、计数、总数\n”;
我的$data={
“名称”=>{
'id'=>'1234',
“总计”=>192,
“结果”=>[
{'APPLE'=>48},
{'KUMQUAT'=>61},
{'ORANGE'=>33},
{'APPLE'=>50},
]
}
};
#按总数降序排列名称
对于我的$name(排序{$data->{$b}{total}$data->{$a}{total}}}键%{$data})
{
#按计数、升序对结果排序;这是您想要的吗?
对于我的$result(排序{(%$a)[1]($b)[1]}@{$data->{$name}{results}){
我的($label,$count)=%$result;
打印$fh join(',',$name,$data->{$name}{id},$label,$count',),“\n”;
}
打印$fh join(“,”,$name,$data->{$name}{id},”,“,”,$data->{$name}{total}),“\n”;
}
收盘价($fh);

好吧,我只说一次:当你有复杂的结构时,一定要使用对象

正如你所发现的,你的大脑会在试图追踪哈希数组的时候爆炸。这是创建对象结构的完美理由。不管你是否永远不会重复使用它。它使您的编程任务变得容易得多

下面的包花了我大约30分钟的时间编写和调试。如果你使用它,你会省去很多心痛和调试

另外,当您发现错误的假设(嘿,每个人都这么做!)即结果数组中有多个具有相同键的项时,您只需修改几行易于定位的代码,而不必遍历整个程序,试图将所有内容保持在一起

我使用了您的数据结构,只是将结果设置为一个数组,该数组包含两个项目(标签和金额),而不是散列。我本可以使用散列,但通过这种方式,我可以返回一个包含两个项的数组。现在,我想起来了,真的没有理由这么做

#! /usr/bin/env perl

use warnings;
use strict;
use feature qw(say);
use Data::Dumper;


my %hash;
my $obj;

$obj = structure->new();
$obj->Name("foo");
$obj->Total("foo", 142);
$obj->Id("foo", 1234);
$obj->Push(qw(foo  ORANGE  33));
$obj->Push(qw(foo  APPLE   48));
$obj->Push(qw(foo  APPLE   50));
$obj->Push(qw(foo  KUMQUAT 61));
$obj->SortResults("foo");

$obj->Name("bar");
$obj->Total("bar", 20);
$obj->Id("bar", 1100);
$obj->Push(qw(bar BIKE    20));
$obj->SortResults("bar");

say Dumper($obj);
exit 0;

########################################################################
package structure;

use Data::Dumper;

#
# New Structure containing all data
# 
sub new {
    my $class = shift;

    my $self = {};

    bless $self, $class;
    return $self;
}

#
# Either adds a new name object or returns name object;
#
sub Name {
    my $self = shift;
    my $name = shift;

    if (not defined $self->{$name}) {
        $self->{$name}->{ID} = undef;
        $self->{$name}->{TOTAL} = undef;
        $self->{$name}->{RESULTS} = [];
    }
    return $self->{$name};
}

#
# Returns a list of Names
#
sub NameList {
    my $self = shift;

    return keys %{$self};
}
#
# Either returns the id or sets $name's id
#
sub Id {
    my $self = shift;
    my $name = shift;
    my $id = shift;

    my $nameObj = $self->Name($name);
    if (defined $id) {
        $nameObj->{ID} = $id;
    }
    return $nameObj->{ID};
}

#
# Either returns the total for $name or sets $name's total
#
sub Total {
    my $self = shift;
    my $name = shift;
    my $total = shift;

    my $nameObj = $self->Name($name);
    if (defined $total) {
        $nameObj->{TOTAL} = $total;
    }
    return $nameObj->{TOTAL};
}

#
# Pushes new product and amount on $name's result list
#
sub Push {
    my $self = shift;
    my $name = shift;
    my $product = shift;
    my $amount = shift;

    my $nameObj = $self->Name($name);
    my @array = ("$name", "$amount");
    push @{$nameObj->{RESULTS}}, \@array;
    return @array;
}

#
# Pops product and amount on $name's result list
#
sub Pop {
    my $self = shift;
    my $name = shift;

    my $nameObj = $self->Name($name);
    my $arrayRef = pop @{$nameObj->{RESULTS}};
    return @{$arrayRef};
}

sub SortResults {
    my $self = shift;
    my $name = shift;

    my $nameObj = $self->Name($name);
    my @results = @{$nameObj->{RESULTS}};
my @sortedResults = sort {$a->[1] <=> $b->[1]} @results;
my $nameObj->{RESULTS} = \@sortedResults;
    return @sortedResults;
}
简言之,你可以节省自己的时间,也可以让清洁女工们收拾得一团糟。(爆炸的大脑很难从墙壁和天花板上擦掉)


我从经验中了解到,每当您开始谈论包含指向其他散列的数组的散列时,都是时候创建一个对象来处理这个混乱。为这类一次性作业创建对象似乎需要花费更长的时间,但根据我的经验,通常你可以在30分钟内完成所需的内容并进行测试,这会让你在以后的几小时里省去很多挫折。

当你说有1000个名称键时,我假设你的意思是每个名称都是唯一的,否则,数据结构不可能。如果我理解正确,您可以使用perl收集数据,将其存储在perl变量中,然后在完成后将此数据打印到已排序的CSV文件中?您真的需要在打印数据之前存储数据吗?@stevecomrie-是的,我在数据结构输出中将变量放在大写字母中@
#! /usr/bin/env perl

use warnings;
use strict;
use feature qw(say);
use Data::Dumper;


my %hash;
my $obj;

$obj = structure->new();
$obj->Name("foo");
$obj->Total("foo", 142);
$obj->Id("foo", 1234);
$obj->Push(qw(foo  ORANGE  33));
$obj->Push(qw(foo  APPLE   48));
$obj->Push(qw(foo  APPLE   50));
$obj->Push(qw(foo  KUMQUAT 61));
$obj->SortResults("foo");

$obj->Name("bar");
$obj->Total("bar", 20);
$obj->Id("bar", 1100);
$obj->Push(qw(bar BIKE    20));
$obj->SortResults("bar");

say Dumper($obj);
exit 0;

########################################################################
package structure;

use Data::Dumper;

#
# New Structure containing all data
# 
sub new {
    my $class = shift;

    my $self = {};

    bless $self, $class;
    return $self;
}

#
# Either adds a new name object or returns name object;
#
sub Name {
    my $self = shift;
    my $name = shift;

    if (not defined $self->{$name}) {
        $self->{$name}->{ID} = undef;
        $self->{$name}->{TOTAL} = undef;
        $self->{$name}->{RESULTS} = [];
    }
    return $self->{$name};
}

#
# Returns a list of Names
#
sub NameList {
    my $self = shift;

    return keys %{$self};
}
#
# Either returns the id or sets $name's id
#
sub Id {
    my $self = shift;
    my $name = shift;
    my $id = shift;

    my $nameObj = $self->Name($name);
    if (defined $id) {
        $nameObj->{ID} = $id;
    }
    return $nameObj->{ID};
}

#
# Either returns the total for $name or sets $name's total
#
sub Total {
    my $self = shift;
    my $name = shift;
    my $total = shift;

    my $nameObj = $self->Name($name);
    if (defined $total) {
        $nameObj->{TOTAL} = $total;
    }
    return $nameObj->{TOTAL};
}

#
# Pushes new product and amount on $name's result list
#
sub Push {
    my $self = shift;
    my $name = shift;
    my $product = shift;
    my $amount = shift;

    my $nameObj = $self->Name($name);
    my @array = ("$name", "$amount");
    push @{$nameObj->{RESULTS}}, \@array;
    return @array;
}

#
# Pops product and amount on $name's result list
#
sub Pop {
    my $self = shift;
    my $name = shift;

    my $nameObj = $self->Name($name);
    my $arrayRef = pop @{$nameObj->{RESULTS}};
    return @{$arrayRef};
}

sub SortResults {
    my $self = shift;
    my $name = shift;

    my $nameObj = $self->Name($name);
    my @results = @{$nameObj->{RESULTS}};
my @sortedResults = sort {$a->[1] <=> $b->[1]} @results;
my $nameObj->{RESULTS} = \@sortedResults;
    return @sortedResults;
}
my @sortedItems = sort {$obj->Total($a) <=> $obj->Total($b)} $obj->NameList();