Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > Confused by hashes/data structures

Reply
Thread Tools

Confused by hashes/data structures

 
 
Ian Petts
Guest
Posts: n/a
 
      02-16-2004
I have a series of Squid logs I need extract last-used dates from for
a set of users.

I want to read in a list of usercodes and user names from a file, then
go through the squid logs and for each user found, add a 'last used'
date to the user list.

I am struggling to get my head around Perl's data structures. I am not
sure whether I need a hash of arrays, array of hashes or a hash of
hashes.

I have been fumbling around trying to work out how to [best] create
and maintain the proper data structure for this situation, but my head
is spinning. The following seems to work to a point, but I can't work
out how to change the date value after it is initially created. Here
is what I've come up so far:

#!/usr/bin/perl -w

# Temporarily disabled 'strict' as it increased my confusion while
# coming to terms with this.
#use strict;
use diagnostics;

# Create some base data. (Not required, but all part of the
# learning process).

@userdata = (
{
user => "jsmith",
desc => "John Smith",
date => 0,
},

{
user => "gjones",
desc => "Greg Jones",
date => 0,
},
);


# I can add users to the list, like this:
push @userdata, { user => "suser", desc => "Steve User", date => 0 };
push @userdata, { user => "ipetts", desc => "Ian Petts", date => 0 };
push @userdata, { user => "pcitizen", desc => "Pete Citizen", date =>
0 };

# Let's have a look and see if I have what I think I do.
# This should print the lot:
for $href (@userdata) {
print "{ ";
for $thing ( keys %$href ) {
print "$thing=$href->{$thing} ";
}
print "}\n";
}

print "\n\n";

# And this should print out whatever field I want.
# But how do I get a specific user?

for $href (@userdata) {
print $href->{"user"} . " ";
print $href->{"desc"} . " ";
print $href->{"date"} . "\n";
}

print "\n\n";

# Now let's assume I've found this user in the log and want to update
# their last-used date in the list.
# This is wrong
$userdata->{"user"}->{"ipetts"}->{"date"} = 12345 ;

# And print it out for a look.
for $href (@userdata) {
print "{ ";
for $thing ( keys %$href ) {
print "$thing=$href->{$thing} ";
}
print "}\n";
}


Could someone please point me in the right direction?

Thanks,
Ian.
 
Reply With Quote
 
 
 
 
Bob Walton
Guest
Posts: n/a
 
      02-16-2004
Ian Petts wrote:

....


> I want to read in a list of usercodes and user names from a file, then
> go through the squid logs and for each user found, add a 'last used'
> date to the user list.
>
> I am struggling to get my head around Perl's data structures. I am not
> sure whether I need a hash of arrays, array of hashes or a hash of
> hashes.
>

....


> @userdata = (
> {
> user => "jsmith",
> desc => "John Smith",
> date => 0,
> },
>
> {
> user => "gjones",
> desc => "Greg Jones",
> date => 0,
> },
> );
>

....


> # Now let's assume I've found this user in the log and want to update
> # their last-used date in the list.
> # This is wrong
> $userdata->{"user"}->{"ipetts"}->{"date"} = 12345 ;



Well, @userdata is an array, right? So it must be accessed using a
subscript, as in something like:

$userdata[3]

for the fourth user, for example. That value can continue being accessed:

$userdata[3]->{date}=12345;

Note also that there is no need for quotes around barewords used in hash
keys.

Now, of course, this brings up the question of how do you know what
subscript to use for a given user. If you want to look up stuff by
userID, you have the wrong data structure. You could build yourself a
data structure that would provide the subscript for a given userID as
follows:

my $i=0;my %subs;for(@userdata){$subs{$$_{user}}=$i++;}

Then you can do:

$userdata[$subs{gjones}]->{data}=12345;

for example. But note that in the event of multiple occurrences of the
same user, only the last one will be stored in %subs, so only the last
record for a given user will show up.


>
> # And print it out for a look.
> for $href (@userdata) {
> print "{ ";
> for $thing ( keys %$href ) {
> print "$thing=$href->{$thing} ";
> }
> print "}\n";
> }



You can save yourself a lot of grief when looking at data structures for
debugging purposes by using Data:umper:

use Data:umper;
print Dumper(\@userdata);

for example.


>
>
> Could someone please point me in the right direction?

....


> Ian.


--
Bob Walton
Email: http://bwalton.com/cgi-bin/emailbob.pl

 
Reply With Quote
 
 
 
 
Ben Morrow
Guest
Posts: n/a
 
      02-16-2004

http://www.velocityreviews.com/forums/(E-Mail Removed) (Ian Petts) wrote:
> I am struggling to get my head around Perl's data structures. I am not
> sure whether I need a hash of arrays, array of hashes or a hash of
> hashes.
>
> #!/usr/bin/perl -w


'use warnings' is better than -w.

> # Temporarily disabled 'strict' as it increased my confusion while
> # coming to terms with this.
> #use strict;


The only thing you need to do is put 'my' before the first use of each
variable. I've put these in below, so you can see.

> use diagnostics;
>
> # Create some base data. (Not required, but all part of the
> # learning process).
>
> @userdata = (


my @userdata = (

> {
> user => "jsmith",
> desc => "John Smith",
> date => 0,


It's probably better to leave the date undefined if you haven't found
one for this user: a date of 0 means 00:00 on 1970-01-01, which isn't
very useful. Also, if you later print out the undefined value, you'll
get a warning, which is handy if someone's date failed to get set for
some reason.

> },
>
> {
> user => "gjones",
> desc => "Greg Jones",
> date => 0,
> },
> );
>
>
> # I can add users to the list, like this:
> push @userdata, { user => "suser", desc => "Steve User", date => 0 };
> push @userdata, { user => "ipetts", desc => "Ian Petts", date => 0 };
> push @userdata, { user => "pcitizen", desc => "Pete Citizen", date =>
> 0 };
>
> # Let's have a look and see if I have what I think I do.
> # This should print the lot:
> for $href (@userdata) {


for my $href (@userdata) {

Or, in fact, you'd be *much* better off using the Data:umper module:

use Data:umper;

print Dumper \@userdata;

> print "{ ";
> for $thing ( keys %$href ) {
> print "$thing=$href->{$thing} ";
> }
> print "}\n";
> }
>
> print "\n\n";
>
> # And this should print out whatever field I want.
> # But how do I get a specific user?


Whenever you ask yourself 'how do I find a specific X', the answer is a
hash, and X is the key to the hash. So in this case, your data structure
should look like:

my %userdata = (
jsmith => {
desc => 'John Smith',
date => undef,
},
gjones => {
desc => 'Greg Jones',
date => undef,
},
);

I just put the 'date' entries in to make things clearer: you can leave
them out, and they'll default to undef. Now you can add a user like
this:

$userdata{suser} = { desc => 'Steve User', date => undef };

and find it like:

print "jsmith is called $userdata{jsmith}{desc}.\n";

To iterate over them all, use:

for my $user (keys $userdata) {
print "$user is called $userdata{$user}{desc}\n";
}

> for $href (@userdata) {
> print $href->{"user"} . " ";
> print $href->{"desc"} . " ";
> print $href->{"date"} . "\n";
> }
>
> print "\n\n";
>
> # Now let's assume I've found this user in the log and want to update
> # their last-used date in the list.
> # This is wrong
> $userdata->{"user"}->{"ipetts"}->{"date"} = 12345 ;


# say you have the user in $user and the date in $date:

$userdata{$user}{date} = $date;

> # And print it out for a look.
> for $href (@userdata) {
> print "{ ";
> for $thing ( keys %$href ) {
> print "$thing=$href->{$thing} ";
> }
> print "}\n";
> }


Again, use Data:umper;

A rule-of-thumb I use when building data structures is 'if I'm using an
array, that keeps things in order: is the order important here?'. If it
isn't, chances are you should be using a hash instead.

Ben

--
$.=1;*g=sub{print@_};sub r($$\$){my($w,$x,$y)=@_;for(keys%$x){/main/&&next;*p=$
$x{$_};/(\w)::$/&&(r($w.$1,$x.$_,$y),next);$y eq\$p&&&g("$w$_")}};sub t{for(@_)
{$f&&($_||&g(" "));$f=1;r"","::",$_;$_&&&g(chr(0012))}};t # (E-Mail Removed)
$J::u::t, $a::n:::t::h::e::r, $P::e::r::l, $h::a::c::k::e::r, $.
 
Reply With Quote
 
Ben Morrow
Guest
Posts: n/a
 
      02-16-2004

Steve May <(E-Mail Removed)> wrote:
> User: $_ Desc: $userdata{$_}{'desc} Date: $userdata{$_}{'date}

^ ^
Been writing Lisp?

Ben

--
Every twenty-four hours about 34k children die from the effects of poverty.
Meanwhile, the latest estimate is that 2800 people died on 9/11, so it's like
that image, that ghastly, grey-billowing, double-barrelled fall, repeated
twelve times every day. Full of children. [Iain Banks] (E-Mail Removed)
 
Reply With Quote
 
Bob Walton
Guest
Posts: n/a
 
      02-16-2004
Bob Walton wrote:

....
> $userdata[$subs{gjones}]->{data}=12345;


e----------------------------------^

Sorry about that.


....

--
Bob Walton
Email: http://bwalton.com/cgi-bin/emailbob.pl

 
Reply With Quote
 
David K. Wall
Guest
Posts: n/a
 
      02-16-2004
Steve May <(E-Mail Removed)> wrote:

> Ian Petts wrote:
>>
>> I want to read in a list of usercodes and user names from a file, then
>> go through the squid logs and for each user found, add a 'last used'
>> date to the user list.


> Since user names are unique (usually) I think I'd use a hash
> of hashes instead, like:
>
> my %userdata = ( jsmith =>{ desc => "John Smith",
> date => 0,
> },
> gjones =>{ desc => "Greg Jones",
> date => 0,
> },
> );


[snip]

[print out data]
> while( sort( keys %userdata ) ){
> # error trapping ignored
> print <<END;
> User: $_ Desc: $userdata{$_}{'desc} Date: $userdata{$_}{'date}
> END
>
> }


Below are a couple of alternate ways to code this, just in case they'll
help. (Besides, I'd already altered this from the original array of hashes
form before I saw Steve May's post


for my $user (keys %userdata) {
print "{ ";
my $href = $userdata{$user};
for my $attribute ( keys %$href ) {
print "$attribute=$href->{$attribute} ";
}
print "}\n";
}
print "\n\n";

# another way to write it... maybe this will make it a bit clearer
for my $user (keys %userdata) {
print "{ ";
for my $attribute ( keys %{$userdata{$user}} ) {
print "$attribute=$userdata{$user}{$attribute} ";
}
print "}\n";
}
print "\n\n";

Data:umper might interest you (Ian) as well, if you get tired of writing
code to do screen dumps of data structures. The Data Structures Cookbook is
highly recommended if you haven't already read it. (perldoc perldsc)

 
Reply With Quote
 
Ian Petts
Guest
Posts: n/a
 
      02-16-2004
Steve May <(E-Mail Removed)> wrote

> Since user names are unique (usually) I think I'd use a hash
> of hashes instead, like:


That's fine. Like I said, I was confused by all the options and I
wasn't sure which angle to tackle it from.

This is really good stuff, Steve. Thank you very much. Easy to read
and I think I actually understand it

The only hitch I have now (I think) is when printing out the hash:

> while( sort( keys %userdata ) ){
> # error trapping ignored
> print <<END;
> User: $_ Desc: $userdata{$_}{'desc} Date: $userdata{$_}{'date}
> END
>
> }


Perl complains with the following:

--- 8< ---
Useless use of sort in scalar context at ./try2.pl line 28 (#1)
(W void) You used sort in scalar context, as in :

my $x = sort @y;

This is not very useful, and perl currently optimizes this away.
--- >8 ---

What's going on here?

Thanks to everyone who has replied to my original post. ALL of the
suggestions and help are very much appreciated.

Regards,
Ian.
 
Reply With Quote
 
Eric Bohlman
Guest
Posts: n/a
 
      02-16-2004
(E-Mail Removed) (Ian Petts) wrote in
news:(E-Mail Removed) om:

> Steve May <(E-Mail Removed)> wrote
> The only hitch I have now (I think) is when printing out the hash:
>
>> while( sort( keys %userdata ) ){


You want for (syn. foreach) rather than while there. Right now the loop
says "keep running (without setting $_ to anything) as long as the return
value of sort(...) in a scalar context is nonzero." I think you got thrown
by the special treatment <HANDLE> gets in while loops.

>> # error trapping ignored
>> print <<END;
>> User: $_ Desc: $userdata{$_}{'desc} Date: $userdata{$_}{'date}


The second-level key here is:

desc} Date: $userdata{$_}{

because the single quotes try to match.

>> END
>>
>> }

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
structures, structures and more structures (questions about nestedstructures) Alfonso Morra C Programming 11 09-24-2005 07:42 PM
unconstrained structures Olaf Petzold VHDL 4 08-30-2005 05:46 PM
confused in sizeof structures rahul8143@gmail.com C Programming 3 04-15-2005 03:20 PM
Type Casting IPv4 and IPv6 structures to Generic Structures tweak C Programming 14 06-11-2004 02:43 PM
Again the synthetize problems, structures Pedro Claro VHDL 2 07-16-2003 04:52 PM



Advertisments