use strict; use warnings; use Data::Dump 'dump'; my $INTERVAL = 500; # Don't know how you get your indices, so: my ( $x, $y, $z ) = ( 1, 2, 3 ); my @indices = ( $x, $y, $z ); my %orig = ( I => { $x => [ 4, 10, 200, 6000, 7000, 7100 ], $y => [ 75, 350, 5900, 6402, 7110 ], $z => [ 0, 95, 5990, 6450 ] }, II => { $x => [ 0, 20, 501, 1000 ], $y => [ 22, 23, 24 ], $z => [ 22, 23, 500 ] }, ); my %outhash; foreach my $key ( keys %orig ) { my ( @merged, @graded, @prev ); foreach my $a_ref ( @indices ) { push @merged, map { $_ . '.' . $a_ref } @{ $orig{$key}{$a_ref} }; } @merged = sort { $a <=> $b } @merged; foreach my $i ( 0 .. $#merged ) { my @cluster; foreach my $j ( $i .. $#merged ) { last if $merged[$j] - $merged[$i] > $INTERVAL; push @cluster, $merged[$j]; } if ( not subset_of( \@prev, \@cluster ) ) { push @graded, [ @cluster ]; } @prev = @cluster; } foreach my $a_ref ( @graded ) { my %HoA; for ( @{ $a_ref } ) { my ( $int, $idx ) = split /\./; push @{ $HoA{$idx} }, $int; } push @{ $outhash{$key} }, [ %HoA ]; } } dump( %outhash ); sub subset_of { my %first = map { $_ => undef } @{ $_[0] }; my @second = @{ $_[1] }; for ( @second ) { return 0 unless exists $first{$_}; } return 1; }