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

waw*_*awa 3 perl perl-data-structures

我现在一直在靠墙撞墙几个小时.

我有一个看起来像这样的数据结构(从"Data :: Dumper"输出).这是我自己的错,我正在创建数据结构,因为我正在解析一些输入.

print Dumper $data;

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

       }
Run Code Online (Sandbox Code Playgroud)
  • 有数千个"NAME"键.
  • 只有一个"id"和一个"total".
  • "结果"哈希中可能有一个或多个键/值对.

我想打印出逗号分隔列表,首先按"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);
Run Code Online (Sandbox Code Playgroud)

这很好并且工作得很好(除了提醒我为什么我再也不使用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
Run Code Online (Sandbox Code Playgroud)

然而!我注意到我正在获得关键冲突(在"结果"哈希中)并且由于我需要保留并报告所有数据,我决定尝试将"结果"更改为哈希数组...

print Dumper $data;

___OUTPUT___
$VAR = { 'NAME' => {
                    'id' => '1234',
                    'total' => 192,
                    'results' => [
                                   { 'APPLE'   => 48 },
                                   { 'KUMQUAT' => 61 },
                                   { 'ORANGE'  => 33 },
                                   { 'APPLE'   => 50 },
                                 ]
                   }
       }
Run Code Online (Sandbox Code Playgroud)
  • 有数千个"NAME"键.
  • 只有一个"id"和一个"total".
  • "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);
Run Code Online (Sandbox Code Playgroud)

如果你已经读过这篇文章,我向你致敬.如果你能提供帮助,我会为你鼓掌.

如果有人对数据结构的替代格式有任何建议,请告诉我!(如果你感兴趣的话......我正在使用"触发器"操作符捕获源文件的块,然后我逐行使用它来创建数据结构.我也称为外部程序来计算某些东西(没有Perl等价物)并存储结果.)

谢谢

Dav*_* W. 5

好的,我只是这样说过:当你有复杂的结构时总是使用对象

正如您所发现的那样,您的大脑会在尝试跟踪哈希数组阵列的哈希数组时爆炸.这是创建对象结构的完美原因.如果你永远不会重复使用它并不重要.它使您的编程任务变得更加容易.

以下花了我大约30分钟来编写和调试.如果你使用它,你可以节省很多心痛和调试.

作为奖励,当你发现你的错误假设(嘿,每个人都这样做!)你在RESULT数组中有多个具有相同键的项时,你只需要修改几行就可以轻松定位代码而不是通过你的整个程序试图将所有东西放在一起.

我使用了你的数据结构,除了我使结果成为一个包含两个项目(标签和数量)而不是哈希的数组的数组.我可以使用哈希,但这样,我可以返回一个包含两个项目的数组.现在,我想起来,无论如何都没有理由这样做.

#! /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;
}
Run Code Online (Sandbox Code Playgroud)

$obj->SortResults将对结果进行排序,但您仍可以使用它将结果作为排序列表进行检索.要按总计对项目进行排序,您可以使用:

my @sortedItems = sort {$obj->Total($a) <=> $obj->Total($b)} $obj->NameList();
Run Code Online (Sandbox Code Playgroud)

简而言之,你可以节省自己的时间和清洁女工一塌糊涂来清理.(爆炸的大脑很难从墙壁和天花板上擦洗).

我从经验中学到,当你开始谈论包含指向其他哈希的数组的哈希哈希时,是时候创建一个处理混乱的对象了.为这些类型的一次性工作创建对象似乎需要更长的时间,但根据我的经验,您通常可以生成所需的内容并在30分钟内进行测试,从而节省您以后的挫折时间.