2012-09-18 188 views
0

我想写一个可以接受哈希列表并创建一个基于任意数量的字段内容的嵌套列表的子。我无法正确地获得递归设置。我从数据库中获取了一堆缺陷数据,并希望将数据分组到任意字段列表(团队,优先级等)。我真的没有任何的示例代码,我认为即使是足够接近从哈希列表中创建一个多级别哈希

实例下

我有以下DS:

$ds = 
[ 
    { 
    foo => 'A', 
    bar => 'B', 
    baz => 'C', 
    }, 
    { 
    foo => 'A', 
    bar => 'B', 
    baz => 'F', 
    }, 
    { 
    foo => 'A', 
    bar => 'D', 
    baz => 'G', 
    }, 
    { 
    foo => 'R', 
    bar => 'J', 
    baz => 'G', 
    } 
] 

考虑下面的函数调用

# prototype groupBy(data, field-1,field-2,field-n) 
groupBy($ds,'foo','bar'); 

我想要以下输出

$res = { 
     A => { 
       B => [ 
         { 
         foo => 'A', 
         bar => 'B', 
         baz => 'C', 
         }, 
         { 
         foo => 'A', 
         bar => 'B', 
         baz => 'F', 
         } 
        ], 
       D => [ 
         { 
         foo => 'A', 
         bar => 'D', 
         baz => 'G', 
         } 
        ], 
       }, 
     R => { 
       J => [ 
         { 
          foo => 'R', 
          bar => 'J', 
          baz => 'G', 
         } 
       } 

     }; 

回答

1

这是使用递归方法非常简单

下面的代码说明

use strict; 
use warnings; 

my $ds = [ 
    { bar => "B", baz => "C", foo => "A" }, 
    { bar => "B", baz => "F", foo => "A" }, 
    { bar => "D", baz => "G", foo => "A" }, 
    { bar => "J", baz => "G", foo => "R" }, 
]; 

my $grouped = groupBy($ds, qw/ foo bar /); 

use Data::Dump; 
dd $grouped; 

sub groupBy { 

    my ($ds, $key, @rest) = @_; 
    return $ds unless $key; 

    my %groups; 
    push @{ $groups{$_->{$key}} }, $_ for @$ds; 
    $groups{$_} = groupBy($groups{$_}, @rest) for keys %groups; 

    return \%groups; 
} 

输出

{ 
    A => { 
     B => [ 
       { bar => "B", baz => "C", foo => "A" }, 
       { bar => "B", baz => "F", foo => "A" }, 
       ], 
     D => [{ bar => "D", baz => "G", foo => "A" }], 
     }, 
    R => { J => [{ bar => "J", baz => "G", foo => "R" }] }, 
} 
+0

感谢。正在密集。我喜欢使用\ @rest缩短\ @rest,并在递归调用时拉动按键。聪明。 – skarface

0

硬编码的解决方案:

my $res; 
for (@$ds) { 
    push @{ $res->{ $_->{foo} }{ $_->{bar} } }, $_; 
} 

但要支持密钥的长度可变的列表。只需添加一些循环。

sub groupBy { 
    my ($ds, @keys) = @_; 
    my $res; 
    for (@$ds) { 
     my $p = dive($res, @$_{ @keys }); 
     push @$$p, $_; 
    } 
    return $res; 
} 

其中dive要么是

sub dive { 
    my $p = \shift; 
    $p = \($$p->{$_}) for @_; 
    return $p; 
} 

use Data::Diver qw(DiveRef); 
sub dive { 
    $_[0] //= {}; 
    return DiveRef(shift, map \$_, @_); 
}