3

ここ数時間、頭を壁にぶつけています。

このようなデータ構造があります (「Data::Dumper」からの出力)。それは私自身のせいです。入力を解析しているときにデータ構造を作成しています。

print Dumper $data;

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

       }
  • 何千もの「NAME」キーがあります。
  • 「id」と「total」は常に 1 つだけです。
  • 「結果」ハッシュには、1 つ以上のキーと値のペアが含まれる場合があります。

最初に「合計」でソートし、次に「結果」配列の各ハッシュの値でソートしたコンマ区切りのリストを出力したいと考えています。

次のコードは、既に保存されているデータ構造から 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);

これは問題なく、うまく機能しました (なぜ私がもう 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

でも!(「結果」ハッシュで)キーの衝突が発生していることに気付き、すべてのデータを保持して報告する必要があるため、「結果」をハッシュの配列に変更してみることにしました...

print Dumper $data;

___OUTPUT___
$VAR = { 'NAME' => {
                    'id' => '1234',
                    'total' => 192,
                    'results' => [
                                   { 'APPLE'   => 48 },
                                   { 'KUMQUAT' => 61 },
                                   { 'ORANGE'  => 33 },
                                   { 'APPLE'   => 50 },
                                 ]
                   }
       }
  • 何千もの「NAME」キーがあります。
  • 「id」と「total」は常に 1 つだけです。
  • "results" 配列には 1 つ以上のハッシュが含まれる場合があります。
  • "results" 配列内の各ハッシュには、名前と値のペアが 1 つだけ含まれます。

誰かがここまで読んだかどうかにかかわらず、これを書き留めるのはかなり治療的だと言わざるを得ないので、続けます... ;-)

新しいデータ構造については、ソート/印刷コードに問題があります。

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);

あなたがここまで読んだなら、私はあなたに敬意を表します。あなたが助けてくれるなら、私はあなたに拍手を送ります。

データ構造の代替フォーマットについて提案がある場合は、お知らせください。(興味がある方のために...「フリップフロップ」演算子を使用してソース ファイルのブロックをキャプチャし、それを行ごとに使用してデータ構造を作成しています。特定のものを計算し (Perl に相当するものはありません)、結果も保存するプログラム)。

ありがとう

4

3 に答える 3

5

わかりました、私はこれを一度だけ言います:複雑な構造があるときは常にオブジェクトを使用してください

あなたが発見したように、あなたの脳は、ハッシュの配列の配列のハッシュの配列を追跡しようとして爆発します。これが、オブジェクト構造を作成する完全な理由です。再利用しなくても問題ありません。これにより、プログラミング作業がはるかに簡単になります。

次のパッケージは、作成とデバッグに約 30 分かかりました。これを使用していれば、多くの心痛とデバッグを回避できたはずです。

おまけとして、RESULT 配列に同じキーを持つ複数のアイテムがあるという誤った仮定 (誰もがそうします!) を発見した場合、簡単にコードを見つけるために、いくつかの行を変更するだけで済みます。プログラム全体がすべてをまとめようとしています。

RESULTS を、ハッシュの代わりに 2 つの項目 (ラベルと金額) を持つ配列を含む配列にすることを除いて、データ構造を使用しました。ハッシュを使用することもできましたが、この方法では、2 つの項目を含む配列を返すことができます。今思えば、とにかくこれを行う理由は本当にありませんでした。

#! /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;
}

$obj->SortResults結果をその場でソートしますが、それを使用して結果をソートされたリストとして取得できます。アイテムを合計で並べ替えるには、次を使用できます。

my @sortedItems = sort {$obj->Total($a) <=> $obj->Total($b)} $obj->NameList();

要するに、あなたは自分の時間を節約し、掃除の女性が片付けるのを混乱させたでしょう. (爆発した脳は、壁や天井からこすり落とすのが非常に困難です)。

経験から、他のハッシュを指す配列を含むハッシュのハッシュについて話し始めるときはいつでも、混乱を処理するオブジェクトを作成するときだと学びました。この種の 1 回限りのジョブのオブジェクトを作成するには、はるかに時間がかかるように思えるかもしれませんが、私の経験では、通常、必要なものを大量に作成して 30 分でテストできるため、後でフラストレーションを何時間も節約できます。

于 2011-05-03T20:48:06.133 に答える
3
use strict;
use warnings;
# [...lots of other stuff...]

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

my $data = {
    'NAME' => {
        'id' => '1234',
        'total' => 192,
        'results' => [
            { 'APPLE'   => 48 },
            { 'KUMQUAT' => 61 },
            { 'ORANGE'  => 33 },
            { 'APPLE'   => 50 },
        ]
    }
};

# sort names by total, descending
for my $name ( sort { $data->{$b}{total} <=> $data->{$a}{total} } keys %{$data} )
{
    # sort results by count, ascending; is this what you want?
    for my $result ( sort { (%$a)[1] <=> (%$b)[1] } @{ $data->{$name}{results} } ) {
        my ($label, $count) = %$result;
        print $fh join(',', $name, $data->{$name}{id}, $label, $count, ''), "\n";
    }
    print $fh join(',', $name, $data->{$name}{id}, '', '', $data->{$name}{total}), "\n";
}

close($fh);
于 2011-05-03T17:47:13.873 に答える
2

次のようなデータ構造を使用すると、おそらく複雑さの層を減らすことができます。

$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'}} ) {

    }

}
于 2011-05-03T17:29:44.080 に答える