-1

I am trying to write a Perl subroutine to process any given hash (passed by reference) but I would like to make it a generic one so that I can use it anywhere.

Assuming the hash has simple key/value pairs and is not an elaborated record (containing arrays of arrays or arrays of hashes), is there any way to find how deep the hash of hashes runs?

For example

my %stat = (
    1 => { one => "One is  one.",    two => "two is two"},
    2 => { one => "second val wone", two => "Seconv v"}
);

The hash above has first level key 1 which has two keys one and two. So it is a hash with two levels.

My question is whether is there any way to test and find this information that the hash has two levels?



Update from comment

About the "problem that has led me to believe that I need to know the depth that a Perl hash is nested". Here is my situation.

  1. The program is creating a data structure of three levels, and also publishing it in a text file for other scripts which are processing this published data and doing something else.

  2. The program is also reading and hashing other data structures of five levels, which has data related to the hash in the first point.

  3. The program is also processing a continuously growing log file and collecting data.

Community
  • 1
  • 1
User9102d82
  • 1,172
  • 9
  • 19
  • 3
    how would knowing how deep it runs help you make a generic subroutine? – ysth Dec 11 '16 at 18:08
  • 1
    I don't understand what you're trying to ask. What do you mean by the "depth" of a hash of hashes? – melpomene Dec 11 '16 at 18:14
  • 1
    You mean you want to find the depth of an arbitrary HoH? First decide how you're going to count the depth of cycles. Then write a DFS and compute depth labels in the usual way. If you give it a try, it doesn't work, and you post code, you might get some useful help. – Gene Dec 11 '16 at 18:26
  • Hi Gene, that is exactly i am stuck at. – User9102d82 Dec 11 '16 at 18:34
  • @melpomene: Hi, by depth, i mean levels. I have added an example to highlight my point. Please see if that helps you to visualize. – User9102d82 Dec 11 '16 at 18:35
  • What is the depth of this hash: `my %foo = (a => 123, b => [{b0 => "hi"}, "b1"], c => { c0 => { c01 => "yo" }, c1 => [4,5,6] });`? – melpomene Dec 11 '16 at 18:37
  • I am talking about only simple hash structures. Yes in your example, it would be a bit challenging to find out as it is a type of elaborated record. – User9102d82 Dec 11 '16 at 18:40
  • You can display the entire structure with Data::Dumper. `use Data::Dumper; print Dumper \%stat;` – shawnhcorey Dec 11 '16 at 19:22
  • *"Assuming the hash has simple key/value pairs and is not an elaborated record (containing arrays of arrays or arrays of hashes)"* Then the answer is simple: your hash has a depth of one. – Borodin Dec 11 '16 at 20:10
  • 3
    This feels like a possible XY problem. What are you trying to accomplish this for? – Sobrique Dec 11 '16 at 20:54
  • @Sobrique - I have a need to iterate over hashes multiple times, and hashes are of different levels as in 3,4 or 6 or 8.. etc.. so its a chore to write foreach loop for each of them. I want to write a subroutine to find the level of HoH and then automate the foreach looping operation just by sending the hash reference. – User9102d82 Dec 11 '16 at 21:13
  • I can't imagine a situation where knowing the depth of nesting could help you. Suppose I told you it was *always 42*, what use would that be to you and how would it simplify your code? – Borodin Dec 11 '16 at 21:14
  • 2
    @UselessPerson: *"so its a chore to write foreach loop for each of them"* Then you need to write some code that, instead of counting the levels of indirection, actually performs the operation that you need. I agree with `@Sobrique`: if you want proper help then you must describe your end goal. You have picked the wrong way to proceed: 42 is of no use to anyone. – Borodin Dec 11 '16 at 21:17
  • Sorry for not being able to help you visualize my problem. In my case, it is varying. This way, maybe I can include it in a custom package perl module (for simple hashes (just key value pairs and not elaborate records) for code-reuse. this is a very specific requirement pertaining to my work environment. – User9102d82 Dec 11 '16 at 21:17
  • Borodin - I don't have a problem writing code. But I do have a problem writing lengthy foreach loops and want to find a way which could help me build a one for all foreach loop, which will iterate through the passed hash and generate desired output. I don't know what you mean by "42 is of no use to anyone". – User9102d82 Dec 11 '16 at 21:21
  • *"Sorry for not being able to help you visualize my problem. In my case, it is varying"* So you can't tell us *anything* about your problem because it is so variant? In that case it cannot be programmed, and you should go back to your senior and say so. – Borodin Dec 11 '16 at 21:22
  • @UselessPerson: *"I don't know what you mean by "42 is of no use to anyone""* I was referring to my preceding comment. Suppose I told you that *every* data structure will always have 42 levels. I am certain that that would be of no use to you. Here's the subroutine: `sub num_levels { return 42 }`. I don't believe you need to know, as your question says, *"How to find the depth of the perl HoH"*. – Borodin Dec 11 '16 at 21:23
  • Again, *please tell us the problem that has led you to believe that you need to know the depth that a Perl hash is nested*. I am certain that you will get much more useful answers. – Borodin Dec 11 '16 at 21:29
  • @Borodin: You are right. If the hash has fixed levels, then there is no need to find out how many levels it has. But I regularly deal with situations where in the same program, operation is being performed over hashes of different levels and I feel it could help simplify things here. – User9102d82 Dec 11 '16 at 21:33
  • @Borodin: About the "problem that has led me to believe that i need to know the depth that a perl hash is nested". Here is my situation. My apologies for not explaining the whole shebang before. 1. program is creating data structure of 3 levels and also publishing it in a text file for other scripts which are processing this published data and doing something else. 2. program is also reading and hashing other data structures of 5 levels, which has data related to the hash in point no. 1 3. Program is also processing a log file (continuously growing) and collecting data. – User9102d82 Dec 11 '16 at 21:40
  • Thank you for providing more information. I have edited your question to copy it there. Please check that I have represented you properly. – Borodin Dec 11 '16 at 23:28
  • There is still a problem in that you haven't told us what you want to do with the *depth* information. Are you saying that some data is three levels deep while other data has five levels, and you want to use the depth to tell which is which? That is poor design, but perhaps you should explain further? You must believe us that a number expressing the depth that a data structure is nested can ***never*** be useful within a program. There is a chance that you may want to display it as a statistic, but otherwise it is useless. – Borodin Dec 11 '16 at 23:35
  • You don't need to know the depth beforehand. `Data::Dumper` works on any level of depth and it does not probe for depth beforehand. Just write a sub to walk thru the tree. – shawnhcorey Dec 12 '16 at 00:54
  • @Brodin: Thank you for updating the question with my explanation. Yes you have represented it properly. I sincerely want to understand your point and motive behind my question is so that I could write a subroutine which will process the hash/hashref based on how many levels it has and this will help me get rid of writing multiple foreach loops in the same program. And i was thinking of also adding this to my custom package.pm. If that purpose seems useless, please suggest another strategy or idea which I can try out. Albeit, it will help anyways to make program look concise and easy to read. – User9102d82 Dec 12 '16 at 01:23
  • 1
    I'm still not following what you're trying to accomplish I'm afraid. It sounds like you might be trying to parse something else, and if you're doing _that_ then the answer is probably to do it 'upstream'. Would you be able to give an illustrative example of the things you're trying to process and the code you're currently using? – Sobrique Dec 12 '16 at 09:34

2 Answers2

3

Assuming a uniform hash structure:

use strict;
use warnings;

sub depth {
    my ($h) = @_;
    my $d = 0;
    while () {
        return $d if ref($h) ne 'HASH';
        ($h) = values %$h;
        $d++;
    }
}

my %stat = (
    1 => { one => "One is  one.", two => "two is two"},
    2 => { one => "second val wone", two => "Seconv v"}
);

print depth(\%stat), "\n";

Output:

2
melpomene
  • 84,125
  • 8
  • 85
  • 148
  • Hi Melpomene, thank you for your answer. This works. I just tried it. This perfectly covers the points described by me. Cheers, Gaurav – User9102d82 Dec 11 '16 at 19:01
2

Traversing hash values and tracking levels could give answer,

use strict;
use warnings;
use v5.16;

{ my $max;
sub hlevel {
    my ($h, $n) = @_;

    $max = 0 if !$n;
    $max = $n if $max < $n;
    __SUB__->($_, $n +1) for grep {ref eq "HASH"} values %$h;

    return $max;
}}

my %h;
$h{a}{b}{c}{d}{e} =1;
$h{a}{b}{c}{d}{e1}{f} =1;
$h{a}{b}{c}{d}{e1}{f1}{g} =1;
print hlevel(\%h, 0);

output

6
mpapec
  • 50,217
  • 8
  • 67
  • 127
  • Hi Sukhoi, thanks for your code. I tried it and it seems to work but I have one question. The last entry "$h{a}{b}{c}{d}{e1}{f1}{g} =1;" it seems to be 7 levels, but still the program prints "6". Can you help me understand? Thanks, Gaurav – User9102d82 Dec 11 '16 at 18:50
  • First hash is at `0` level, but you can change it to start from `1` . – mpapec Dec 11 '16 at 18:54
  • Yes. got it! Missed the zero before. So it is exact. so basically it gives the maximum level in the hash. Great ! This is what I kinda looking for. – User9102d82 Dec 11 '16 at 18:58