0

I have this hash that contains some information:

my %hash = (
    key_1 => {
        year => 2000,
        month => 02,
    }, 
    key_2 => {
        year => 2000,
        month => 02,
    }, 
    key_3 => {
        year => 2000,
        month => 03,
    }, 
    key_4 => {
        year => 2000,
        month => 05,
    }, 
    key_5 => {
        year => 2000,
        month => 01,
    }
);

I wan't to create an array of hashes in which each of the array elements, lists every single hash key/value pairs that has the same year and month.

So basically I want to create something like this:

$VAR1 = [
    'key_1' => {
        'month' => 2,
        'year' => 2000
    },
    'key_2' => {
        'month' => 2,
        'year' => 2000
    }
], [
    'key_3' => {
        'month' => 3,
        'year' => 2000
    }
], [
    'key_4' => {
        'month' => 3,
        'year' => 2000
    }
], [
    'key_5' => {
        'year' => 2000,
        'month' => 1
    }
];

The real question here is: How can I compare a hash key key value's to other key key value's and make a map out of it.

Thank you for your time! =)

rnarcos
  • 86
  • 1
  • 2
  • 12
  • So why aren't key_3 and key_4 put in the same array in your example since they got the same year and month? – Kjetil S. Mar 18 '18 at 01:46
  • What you say you want to create is not an array of hashes, it is _hash_ of hashes. The members of what you are calling the outer "list" are key-value pairs, so the outer structure is a hasn, not a list. – Jim Garrison Mar 18 '18 at 02:46
  • You say you want `$VAR1 = [$k,$v,$k,$v...], [...], [...], ...;`, which makes no sense. The accepted answer assumed you meant you want `$VAR1 = [ [$k,$v,$k,$v,...], [...], [...], ... ];`, but wouldn't `$VAR1 = [ { $k=>$v,$k=>$v,...}, {...}, {...}, ... ];` make more sense? – ikegami Mar 18 '18 at 03:27
  • @KjetilS. I fixed it, my bad. – rnarcos Mar 18 '18 at 04:53

3 Answers3

0

I'm getting a slightly different results - key_3 and key_4 should belong to the same group.

my %by_year_and_month;
undef $by_year_and_month{ $hash{$_}{year} }{ $hash{$_}{month} }{$_}
    for keys %hash;

my $result;
for my $year (keys %by_year_and_month) {
    for my $month (keys %{ $by_year_and_month{$year} }) {
        push @$result, [ map { $_ => { month => $month, year => $year } }
                         keys %{ $by_year_and_month{$year}{$month} } ];
    }
}
choroba
  • 231,213
  • 25
  • 204
  • 289
  • Yes, you are right, key_3 and key_4 should belong to the same group! Thank you for your answer! – rnarcos Mar 18 '18 at 01:52
0
my %hash = (
    key_1 => {year => 2000, month => 02},
    key_2 => {year => 2000, month => 02},
    key_3 => {year => 2000, month => 03},
    key_4 => {year => 2000, month => 03},
    key_5 => {year => 2000, month => 01}
);
my @v=qw(year month);             # keys compared
my @k=sort keys %hash;
my(@a,%i);
push@{$a[sub{$i{join$;,map$_[0]{$_},@v}//=@a}->($hash{$_})]},($_=>$hash{$_}) for@k;
use Data::Dumper; print Dumper(\@a);
Kjetil S.
  • 3,468
  • 20
  • 22
0

You say you want the following, but it makes no sense.

 $VAR1 = [$k,$v,$k,$v...], [...], [...], ...;

The accepted answer assumed you meant you want the following:

$VAR1 = [ [$k,$v,$k,$v,...], [...], [...], ... ];

That can be obtained as follows:

sub group_id { sprintf "%04d-%02d", @{ $_[0] }{qw( year month )} }

my %grouped;
for my $k (keys(%hash)) {
   my $v = $hash{$k};
   push @{ $grouped{ group_id($v) } }, $k, $v;
}

my $VAR1 = [ map { $grouped{$_} } sort keys %grouped ];

But that's a very weird format. The following would make far more sense:

$VAR1 = [ { $k=>$v,$k=>$v,...}, {...}, {...}, ... ];

That can be obtained as follows:

sub group_id { sprintf "%04d-%02d", @{ $_[0] }{qw( year month )} }

my %grouped;
for my $k (keys(%hash)) {
   my $v = $hash{$k};
   $grouped{ group_id($v) }{$k} = $v;
}

my $VAR1 = [ map { $grouped{$_} } sort keys %grouped ];

In both cases, the key is to use a hash to group similar things.

In both cases, the resulting groups are ordered chronologically.

ikegami
  • 367,544
  • 15
  • 269
  • 518