带循环的数组哈希的perl哈希

时间:2014-12-11 18:36:27

标签: arrays perl hash

我在数组散列的哈希中有以下数据。数组中的数字数据代表了最后四个季度的财务信息。

我希望能够遍历数组,并按季度提取数据,以便准备好插入数据库。使用我的代码,我可以得到所有的季度,或者只有我特别称之为四分之一。当我尝试添加另一个循环来迭代数组散列的散列以仅返回数组子集值时,我得到所有的值,我不知道我做错了什么。请参阅代码:感谢您的帮助

my %comp_info = (
  CompanyA => {
    COGS    => ["175.00", "155.00", "125.00", "125.00"],
    Revenue => ["300.00", "200.00", "250.00", "225.00"],
  },
)

# The following works, but I have to specifically push one array subset at a time, 
# which makes passing values to db_insert subroutine ineffective.  
# (Id have to have 4 or 5 calls depending on number of quarters of data in each record).

sub get_insert_arrays {
  foreach $comp (keys %comp_info ) {
     foreach $column ( keys %{$comp_info{$comp}} ) {
        push (@insert_array, @{$comp_info{$sym}{$column}}[0] );
     }
  }
  my $valuelist = join(", ", @insert_array);
  &db_insert($valuelist);
  undef @insert_array;
}

#try to loop through, fails, I get all of the data in @insert_array instead of only one quarter.
sub get_insert_arrays {
  foreach $comp (keys %comp_info ) {
     foreach $column ( keys %{$comp_info{$comp}} ) {
        for my $i ( 0 .. $#{$comp_info{$comp}{$column}} ) {
           push (@insert_array, @{$comp_info{$sym}{$column}}[$i] ); 
        }
     }
    my $valuelist = join(", ", @insert_array);
    &db_insert($valuelist);
    undef @insert_array;  
    undef $valuelist;   
  }
}

2 个答案:

答案 0 :(得分:1)

我强烈建议您使用中间变量取消引用,并使用->语法。这两个都可以帮助你弄清楚发生了什么:

这是您使用解除引用的第一个子例程:

sub get_insert_arrays {
    my @insert_array;

    foreach $comp (keys %comp_info ) {      # References to a hash (Revenue, COGS)
        %columns = %{ $comp_info{$comp} };  # Dereference
        foreach $column ( keys %columns ) { # Reference to an Array (quarter data)
            my @values = @{ $column } ;     # Dereference
            push (@insert_array, $column );
            my $valuelist = join(", ", @insert_array);
            &db_insert($valuelist);
        }
    }
}

嗯......看着这个,很容易看出我可以做到:

    for my $comp (keys %comp_info ) {      # References to a hash (Revenue, COGS)
        %columns = %{ $comp_info{$comp} };  # Dereference
        for my $column ( keys %columns ) { # Reference to an Array (quarter data)
            my @values = @{ $column } ;     # Dereference
            db_insert(@values);
        }
    }
}

如果您需要查看特定数据,请使用->语法简化结构:

${$comp_info{$sym}{$column}}[$i];   # You had "@". Should be "$".

VS

$comp_info{$sym}->{$column}->[$i];

更容易阅读。

还可以在程序中使用warningsstrict pragma。它会捕获许多错误,包括未定义的变量和拼写错误的变量名称。

如果您逐个提取数据 ,您可能希望 COGS < em>收入 专栏:

#! /usr/bin/env perl
#

use strict;             # Lets you know when you misspell variable names
use warnings;           # Warns of issues (using undefined variables
use feature qw(say);

#
# Just initializing the data you had
#
my %comp_info = ( CompanyA => {
        Revenue => [
            300.00, 200.00, 250.00, 225.00
        ],
        COGS => [
            175.00, 155.00, 125.00, 125.00
        ],
    },
);

#
# Go through each company
#
for my $company ( keys %comp_info ) {
    my @revenues = @{ $comp_info{$company}->{Revenue} };  # Dereference
    my @cogss    = @{ $comp_info{$company}->{COGS} };     # Dereferenec
    say "Company: $company";
    #
    # I know the keys are "Revenue" and "COGS", so I don't need a loop.
    # I'll just go right to my quarters data. Note that dereferencing
    # makes my program a lot easier to write and maintain
    #
    for my $quarter ( (0..3) ) {
        my $revenue = $revenues[$quarter];
        my $cogs    = $cogss[$quarter];
        my $profit = $revenue - $cogs;
        say "    Quarter: " . ($quarter - 1)
             . " Revenue = $revenue  COGS = $cogs Profit = $profit";
    }
}

您如何进行数据库插入取决于您自己。但是,您可以看到如何进行一些解除引用并使用->来澄清您正在查看的内容。


附录

  

如何在不必指定收入,齿轮等的情况下仅提取季度数据,在某些情况下可能会有30多个字段,因此我不想在程序中指定每个字段。我只想抓住所有Q1字段,插入,抓取所有Q2字段,插入等

所以有两个循环:

  • 和以前一样,我的外部循环遍历每个公司,恰好是%comp_info哈希的关键。
  • %comp_info哈希中的每个都是对数据类型(COGS,收入等)键入的另一个哈希的引用。同样,我只是循环遍历内部哈希的键(在解除引用后使其更容易理解)。
  • 既然我有公司名称(该%comp_info哈希的关键字,以及该内部哈希中的密钥列表,我可以简单地提取每个公司和每种数据类型的第一季度数字。获取季度值很简单:$comp_info{$company}->{$type}->[$quarter]。请注意,有三个级别的数据,我的变量中有三个部分,每个部分由->分隔。
    • 我可以看到最外面的部分是一个简单的哈希,它由公司名称键入:($comp_info{$company})。
    • %comp_info哈希指向哈希引用(->{type}),该引用由数据类型(COGS,收入等)键入。
    • 该哈希引用指向每个季度(->[$quarter])的数组引用。看看它是如何工作的以及为什么我喜欢->语法?它清楚地说明了我在做什么。
    • 这只是第一季度的结果。如果我想通过每个季度,我可以有一个外环for my $quarter (0..3) {

这就是它的样子。这是一个完整的程序,所以你可以减少它,并尝试自己运行它,看看你是否能弄清楚发生了什么。

use strict;             # Lets you know when you misspell variable names
use warnings;           # Warns of issues (using undefined variables
use feature qw(say);

my $quarter = 0;        #First Quarter is 0. Last quarter is 3
my %comp_info = ( CompanyA => {
        Revenue => [
            300.00, 200.00, 250.00, 225.00
        ],
        COGS => [
            175.00, 155.00, 125.00, 125.00
        ],
    },
);

for my $company ( keys %comp_info ) {
    say "Company: $company";
    %types = %{ $company_info{$company} };
    for my $type ( keys %types ) {   # COGS, Revenue, etc.
        say "   $type for quarter " 
            . ($quarter + 1) . ": "
            . $comp_info{$company}->{$type}->[$quarter];
    }
}

每个季度再次将数据插入数据库:

使用use strict并使用my声明变量意味着变量仅对有限范围有效。 my @type_data;声明了一个数组,其中包含用于插入数据库的类型值。但是,由于它在for my $quarter循环内声明,因此数组及其值随着循环的每次迭代而消失。无需删除数据或重新初始化变量。它自己完成所有这一切!

查看Lexically Scoped Variables的工作原理。

for my $quarter ( (0..3) ) {
    my @type_values;
    for my $company ( keys %comp_info ) {
        my %types = %{ $comp_info{$company} };
        for my $type ( keys %types ) {   # COGS, Revenue, etc.
            push @type_values, $comp_info->{$company}->{$type}->{quarter};
    }
    insert_data( @type_values );  # Database insert you wanted
}

答案 1 :(得分:1)

您最近添加的内容 - undef @insert_array; undef $valuelist;是对undef的误用。把它放在之前这样的变量会强制进行垃圾收集循环,这是你不想做的事情 - 最好让Perl自己管理事物。

阵列应该使用@insert_array = ()而不是使用undef清空,对于标量,您应该$valuelist = undef。但是这些变量在子例程之外是无关紧要的,所以你应该在中声明它们,在这种情况下,首先不需要重新初始化它们。

请记住我在有占位符的SQL语句中调用prepare时所说的内容。你的代码看起来应该是这样的

my $insert = $dbh->prepare('INSERT INTO table VALUES (?, ?)');

以后

my @insert_array = (175.00, 300.00);
$insert->execute(@insert_array);

但是我已经写了这个,我想你做了什么,创建一个$valuelist字符串作为你自己的代码。由于您不需要哈希,因此迭代会更加整洁。 db_insert子例程是一个虚拟对象,只打印传递给它的参数值。

use strict;
use warnings;
use 5.010;

my %comp_info = (
  CompanyA => {
    COGS    => ["175.00", "155.00", "125.00", "125.00"],
    Revenue => ["300.00", "200.00", "250.00", "225.00"],
  },
);

my @values = map values %$_, values %comp_info;

for my $i (0 .. $#{$values[0]}) {
  my @insert = map $_->[$i], @values;
  db_insert(join ', ', @insert);
}

sub db_insert {
  say "db_insert('@_')";
}

<强>输出

db_insert('175.00, 300.00')
db_insert('155.00, 200.00')
db_insert('125.00, 250.00')
db_insert('125.00, 225.00')

<强>更新

符合新规范:

use strict;
use warnings;
use 5.010;

my %comp_info = (
  CompanyA => {
    COGS    => ["175.00", "155.00", "125.00", "125.00"],
    Revenue => ["300.00", "200.00", "250.00", "225.00"],
  },
  CompanyB => {
    COGS    => ["175.00", "155.00", "125.00", "125.00"],
    Revenue => ["300.00", "200.00", "250.00", "225.00"],
  },
  CompanyC => {
    COGS    => ["175.00", "155.00", "125.00", "125.00"],
    Revenue => ["300.00", "200.00", "250.00", "225.00"],
  },
);

my @columns = qw/ COGS Revenue /;

for my $comp (keys %comp_info) {
  my $data = $comp_info{$comp};
  for my $i (0 .. $#{(values %$data)[0]}) {
    my @values = ( $comp, map $_->[$i], @{$data}{@columns} );
    db_insert(join ', ', @values);
  }
}

sub db_insert {
  say "db_insert('@_')";
}

<强>输出

db_insert('CompanyC, 175.00, 300.00')
db_insert('CompanyC, 155.00, 200.00')
db_insert('CompanyC, 125.00, 250.00')
db_insert('CompanyC, 125.00, 225.00')
db_insert('CompanyA, 175.00, 300.00')
db_insert('CompanyA, 155.00, 200.00')
db_insert('CompanyA, 125.00, 250.00')
db_insert('CompanyA, 125.00, 225.00')
db_insert('CompanyB, 175.00, 300.00')
db_insert('CompanyB, 155.00, 200.00')
db_insert('CompanyB, 125.00, 250.00')
db_insert('CompanyB, 125.00, 225.00')