values of hash of hash

N

ngoc

I have
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;
I want to get 7 and 6 without using three for loop and keys function
How can I do it?
 
N

ngoc

My nummeric key is example only. My real application has keys that mix
of text and number.
My problem is, I have to compare data from two projects and find out
duplicated data. Two projects presented as two hash. If first level key
is different (using exist function), put the whole data attached to it
in an array. Currently, I have to access level 2, level 3 keys before
getting it's real value. It is not advanced computing. So I want to
access all values without using keys. Using "values %hash" just gives me
next level hash.

Matija said:
I have
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;
I want to get 7 and 6 without using three for loop and keys function
How can I do it?


use Data::Dumper;
my $s = Dumper \%hash;
my @numbers = $s =~ /([76])/g;

:)

btw, is there a reason for using a hash? perhaps you could be better
with arrays (all you keys are numeric).
 
M

Matija Papec

I have
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;
I want to get 7 and 6 without using three for loop and keys function
How can I do it?

use Data::Dumper;
my $s = Dumper \%hash;
my @numbers = $s =~ /([76])/g;

:)

btw, is there a reason for using a hash? perhaps you could be better
with arrays (all you keys are numeric).
 
A

Anno Siegel

[Top posting rearranged. Please don't do that]
[...]
btw, is there a reason for using a hash? perhaps you could be better
with arrays (all you keys are numeric).

My nummeric key is example only. My real application has keys that mix
of text and number.
My problem is, I have to compare data from two projects and find out
duplicated data. Two projects presented as two hash. If first level key
is different (using exist function), put the whole data attached to it
in an array. Currently, I have to access level 2, level 3 keys before
getting it's real value. It is not advanced computing. So I want to
access all values without using keys. Using "values %hash" just gives me
next level hash.

So iterate "values":

my %hash;
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;

for ( values % hash ) {
for ( values %$_ ) {
for ( values %$_ ) {
print "$_\n";
}
}
}

That prints 7 and 6 in some order. No key access involved.

Anno
 
N

ngoc

Yes. I have considered it. My data is really complex. Each project have
many employees, each employee have many tasks, each task have time,
budget etc... This can be model as 3 level hash as I did.

For another reason, 3 nested for loop is not beautiful programming. I
try to avoid.

So I am looking for simple solution without using 3 for loop.

An analogue to it, 'map' and 'grep' can do 5 lines of code to 1 line.
 
X

xhoster

ngoc said:
I have
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;
I want to get 7 and 6 without using three for loop and keys function
How can I do it?

Have you considered not creating the uselessly nested hashes in the first
place, but rather sticking the data into a more appropriate structure?

Since you, for completely unknown reasons, don't want to use foreach loops
or the keys function, it would help tremendously if you would list all the
other features you irrationally wish to avoid.

Xho
 
J

John W. Krahn

ngoc said:
I have
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;
I want to get 7 and 6 without using three for loop and keys function
How can I do it?

You could always use the old style for multiple keys:

$hash{1,2,3} = 7;
$hash{1,4,5} = 6;

print for values %hash;


John
 
J

J. Gleixner

ngoc wrote:

** If you continue to top-post, a lot of folks will add you to their
kill-file. **
Yes. I have considered it. My data is really complex. Each project have
many employees, each employee have many tasks, each task have time,
budget etc... This can be model as 3 level hash as I did.

Not terribly complex. Sounds like you'd be better off using a database.
(select distinct(x) from table). Possibly SQLite:
http://www.hwaci.com/sw/sqlite/ might be a "beautiful" solution.
For another reason, 3 nested for loop is not beautiful programming. I
try to avoid.

Then don't build a HoHoH data structure.
So I am looking for simple solution without using 3 for loop.

Then use a different data structure. Possibly, when building
the HoHoH, build another one to hold the values you're after.
An analogue to it, 'map' and 'grep' can do 5 lines of code to 1 line.

Put them all on one line, or put the 3 for's in a sub or method, then
you can make a "beautiful" subroutine call.
 
N

ngoc

J. Gleixner said:
ngoc wrote:

** If you continue to top-post, a lot of folks will add you to their
kill-file. **
What is top-post? I do not know what it means.
 
P

Paul Lalli

ngoc said:
What is top-post? I do not know what it means.

Really? Odd, because the first hit for Google "top-post" is:
http://catb.org/~esr/jargon/html/T/top-post.html

Put simply, it means writing your post *above* the material that you
are quoting, rather than quoting a relevant amount of material, and
then starting your reply *below* this quoted material.

Please read the Posting Guidelines that are posted here twice every
week for more good tips on how to avoid the kill-file.

Paul Lalli
 
M

Matija Papec

X-Ftn-To: ngoc

ngoc said:
My problem is, I have to compare data from two projects and find out
duplicated data. Two projects presented as two hash. If first level key
is different (using exist function), put the whole data attached to it
in an array. Currently, I have to access level 2, level 3 keys before
getting it's real value. It is not advanced computing. So I want to
access all values without using keys. Using "values %hash" just gives me
next level hash.

This is basically the same what Anno wrote,

my @arr = map values %$_, map values %$_, values %hash;

but what could you practically do only with values? Perhaps you're
approaching the problem from the wrong angle so what seems like solution
only produces new problems?
 
N

ngoc

Matija said:
This is basically the same what Anno wrote,

my @arr = map values %$_, map values %$_, values %hash; Yes it looks better.

but what could you practically do only with values? Perhaps you're
approaching the problem from the wrong angle so what seems like solution
only produces new problems?
I wrote a perl application for 2 years ago, when I was right out from
school. It is 8000 lines of code. After 2 years, I get more experience
and knowledge. So I try to reduce amount of code and refine it. What
application do, is comparing data from two databases. Data is hierarchic:

Each project have many employees, each employee have many tasks, each
task have time, budget etc... So I did

$data_1{$proj_id}{$employee}{$task} = [$id, $employee, $task,
$start_time, $budget,.........];

$data_2{$proj_id}{$employee}{$task} = [$id, $employee, $task,
$start_time, $budget,.........];

I first compare $proj_id, using exist function. If $proj_id from data_1
not found in data_2, I store it into another hash $unique_hash{$proj_id}
= $data_1{$proj_id}. Later, I want to display all data, I have to use 3
for loop.

foreach (keys %unique_hash) {
foreach (keys %{$unique_hash{$_}}) {
foreach (keys %{$unique_hash{$_}{$_}}) {
$values = $unique_hash{$_}{$_}{$_};
}
}
}

Reading from many computer science books, a good programming style is
not many nested for loop.
Looking back to my app. I use 3 for loop, in many places in my app. So I
have to change it, IF I WANT TO KEEP my job :). So I posted to forum
in hope some smart guys out there can help me to be better programmer.
(And they can also learn new cases too).
 
A

Arne Ruhnau

ngoc said:
I have
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;
I want to get 7 and 6 without using three for loop and keys function
How can I do it?

You could use the following code, which "just hides the loops". The code is
taken out of some more general version I am developing and thus contains
more features than necessary (and it works, as it is, only if you specify
an empty pattern to look for...). Hm.
Graphsearch::matcherFor takes a nested hash as argument and returns an
object/factory/coderef capable of understanding a "Pattern" and returning
an object/Iterator/coderef that extracts every path in the hash matching
the pattern.

BTW: Note the evil goto ;)

<code>
use strict;
use warnings;

my %hash;
$hash{1}{2}{3} = 7;
$hash{1}{4}{5} = 6;

my $matcher = Graphsearch::matcherFor(\%hash);
my $iter = $matcher->([qw//]); # [qw//] is the empty pattern
while(my $path = $iter->()) {
print $path->[-1]; # $path contains [1,2,3,7] and [1,4,5,6]
}

package Graphsearch;

use strict;
use warnings;

sub matcherFor {
my $data = shift;
my $possiblePath = sub { return 0 unless defined ($_[1]);
exists $_[0]->{$_[1]}
};
my $isTerminal = sub { !ref($_[0]->{$_[1]}) };
my $walkpath = sub { $_[0]->{$_[1]} };
my $defaultAction = sub { [keys %{+shift}] };
return sub {
my ($pattern, $requireLength) = @_;
$requireLength ||=0;
my $plength = scalar(@$pattern)-1;

my ($stacks, $olddata, $path, $patternpos, $backtrackfirst)
= ([], [], [], 0, 0);
my ($try, $lengthOk, $nextTry, $consumePattern, $backtrack, $get,
$nextMove);
$lengthOk = sub {
return 1 unless $requireLength; $plength < $patternpos
};
$nextTry = sub {
$try = pop @{$stacks->[-1]}
};
$consumePattern = sub {
my $action = $pattern->[$patternpos] || $defaultAction;
push @$stacks, $action->($data);
++$patternpos;
goto $nextTry;
};
$nextMove = sub {
goto defined($nextTry->())?$get:$backtrack
};
$backtrack = sub {
return undef unless(scalar(@$olddata));
pop @$stacks;
pop @$path;
$data = pop(@$olddata);
--$patternpos;
goto $nextMove;
};
$get = sub {
if ($backtrackfirst or !$possiblePath->($data, $try)) {
$backtrackfirst = 0;
goto $nextMove;
}
else {
if ($isTerminal->($data, $try)) {
if($lengthOk->()) {
$backtrackfirst = 1;
return [@$path, $try, $data->{$try}];
}
else { goto $nextMove }
}
else {
push @$olddata, $data;
push @$path, $try;
$data = $walkpath->($data, $try);
$consumePattern->();
goto $get;
}
}
};
$consumePattern->();
return bless $get, __PACKAGE__;
};
}

__END__
 
X

xhoster

ngoc said:
Reading from many computer science books, a good programming style is
not many nested for loop.

While many nested map statements *is* good style?
Looking back to my app. I use 3 for loop, in many places in my app. So I
have to change it, IF I WANT TO KEEP my job :). So I posted to forum
in hope some smart guys out there can help me to be better programmer.
(And they can also learn new cases too).

You do not become a good programmer by blindly following style guides.

If you wish to do something with each item individually, use nested foreach
loops (upon values rather than keys). If you want to do soemthing with the
items as a collection, use the nested maps. If you want pretty code, write
subroutines.


Xho
 
A

Anno Siegel

[Top-posting rearranged]
Yes. I have considered it. My data is really complex. Each project have
many employees, each employee have many tasks, each task have time,
budget etc... This can be model as 3 level hash as I did.

For another reason, 3 nested for loop is not beautiful programming. I
try to avoid.

Where did you get that from? If your data structure requires three
nested loops, write three nested loops. If it requires five, use five.

Programming style is no set of hard and fast rules, like nesting
two deep is okay, three deep isn't. The main criterion is impact
on readability. A straight nest of loops of the same type, with
no intervening peculiarities on some levels, is perfectly readable
and should be used where appropriate as long as line length allows.

Anno
 
N

ngoc

Matija said:
I guess this isn't your actual code as $_ can't be three different things at
the time?

yes, I have changed a little bit. The original is "foreach my $proj_id
......" form.

and the name of 'unique_hash' is not used in my actual code. Because of
posting things in a forum reading by many people, I have to use easy
understanding words. And using publicly examples that are similar to my
actual problems.

#...

for my $k (keys %unique_hash) {
my $values = $unique_hash{$k};
}

Suddenly, even five nested loops look better then this. :)




Sounds like subroutine could replace your repeating code.
Thanks guys. I get much help from you, guys.
 
M

Matija Papec

X-Ftn-To: ngoc

ngoc said:
Each project have many employees, each employee have many tasks, each
task have time, budget etc... So I did

$data_1{$proj_id}{$employee}{$task} = [$id, $employee, $task,
$start_time, $budget,.........];

How about,
push @{ $data_1{$proj_id} }, [$id, $employee, $task, ..]

as you already have $employee and $task stored on the right side, and you
don't actually need them in the hash?
foreach (keys %unique_hash) {
foreach (keys %{$unique_hash{$_}}) {
foreach (keys %{$unique_hash{$_}{$_}}) {
$values = $unique_hash{$_}{$_}{$_};
}
}
}

I guess this isn't your actual code as $_ can't be three different things at
the time?
Reading from many computer science books, a good programming style is
not many nested for loop.

Your data structure is fine, but if you're really unhappy with it, build
simple hash with $data_1{$k1, $k2, $k3} = $some_value and later,

#untested
my @data1_keys = keys %data_1;

# assuming $proj_id has only \w chars
my @slice_keys = grep /^$proj_id\b/, @data1_keys;
@unique_hash{@slice_keys} = @data_1{@slice_keys};
#...

for my $k (keys %unique_hash) {
my $values = $unique_hash{$k};
}

Suddenly, even five nested loops look better then this. :)
Looking back to my app. I use 3 for loop, in many places in my app. So I
have to change it, IF I WANT TO KEEP my job :).

Sounds like subroutine could replace your repeating code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,173
Messages
2,570,939
Members
47,484
Latest member
JackRichard

Latest Threads

Top