[comp.lang.perl] A question on associative arrays

sid@think.com (Sid Stuart) (05/30/91)

I am writing a Perl application in which I would like to store
into an associative array only if an entry for that key does
not exist. The problem is I may come up with duplicate keys and
I don't want to overwrite the contents of an entry. Is there a
cheap way to find out if a key is in use? By cheap I mean other
than using the functions "keys" or "each" to search through all
the keys of the array.

Thanks for the time,
Sid Stuart
sid@think.com

-- 
Sid Stuart, Thinking Machines Corp.

sid@think.com
{uunet,harvard}!think!sid

ziegast@eng.umd.edu (Eric W. Ziegast) (05/31/91)

In article <1991May30.134223.23225@Think.COM> sid@think.com (Sid Stuart) writes:
>
>I am writing a Perl application in which I would like to store
>into an associative array only if an entry for that key does
>not exist. The problem is I may come up with duplicate keys and
>I don't want to overwrite the contents of an entry. Is there a
>cheap way to find out if a key is in use? By cheap I mean other
>than using the functions "keys" or "each" to search through all
>the keys of the array.

   print defined($assoc{$key}) ? "$assoc{$key}\n" : "Key $key undefined\n";

So long as you don't set $assoc{$key} to a value (even ""), that entry
in the associative should be undefined.

Eric Z.
--
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
| Eric W. Ziegast      Internet: ziegast@eng.umd.edu |
| Univ. of Merryland   Phonenet: Eric@[301.405.3689] |
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+

Tom Christiansen <tchrist@convex.COM> (05/31/91)

From the keyboard of sid@think.com (Sid Stuart):
:
:I am writing a Perl application in which I would like to store
:into an associative array only if an entry for that key does
:not exist. The problem is I may come up with duplicate keys and
:I don't want to overwrite the contents of an entry. Is there a
:cheap way to find out if a key is in use? By cheap I mean other
:than using the functions "keys" or "each" to search through all
:the keys of the array.

Barring pathological cases (keys whose values are undef), you
could do this:

    $ary{$key} = $val unless defined $ary{$key};

or perhaps

    $ary{$key} .= $val . ' ';

--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
		"So much mail, so little time." 

przemek@rrdstrad.nist.gov (Przemek Klosowski) (05/31/91)

Hello!
The solution to a problem with testing if associative array element
was already defined reminded me of a problem I had, and couldn't figure
out. How does one check if the associative array was assigned at all?
My program runs a loop initializing an array, but each individual
initialization is conditioned on something. On a rainy day all conditions
could be false: is there a way to find out? The obvious $#ary doesn't work,
since it refers to @ary. defined(%ary) is no good, since %ary might be
pre-defined in a local(). Ideas, anyone?

			przemek
--
			przemek klosowski (przemek@ndcvx.cc.nd.edu)
			Physics Department
			University of Notre Dame IN 46556

tchrist@convex.COM (Tom Christiansen) (05/31/91)

From the keyboard of przemek@rrdstrad.nist.gov (Przemek Klosowski):
:Hello!
:The solution to a problem with testing if associative array element
:was already defined reminded me of a problem I had, and couldn't figure
:out. How does one check if the associative array was assigned at all?
:My program runs a loop initializing an array, but each individual
:initialization is conditioned on something. On a rainy day all conditions
:could be false: is there a way to find out? The obvious $#ary doesn't work,
:since it refers to @ary. defined(%ary) is no good, since %ary might be
:pre-defined in a local(). Ideas, anyone?

    if (%a) {

will actually work, but I don't think that Larry has ever documented
this.  More portably, and more slowly would  be

    if (@my_keys = keys %a) {


--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
		"So much mail, so little time." 

ziegast@eng.umd.edu (Eric W. Ziegast) (05/31/91)

(Tom Christiansen) writes:
> From the keyboard of (Przemek Klosowski):
>       How does one check if the associative array was assigned at all?
> :My program runs a loop initializing an array, but each individual
> :initialization is conditioned on something. On a rainy day all conditions
> :could be false: is there a way to find out? The obvious $#ary doesn't work,
> :since it refers to @ary. defined(%ary) is no good, since %ary might be
> :pre-defined in a local(). Ideas, anyone?
> 
>     if (%a) {
> 
> will actually work, but I don't think that Larry has ever documented
> this.  More portably, and more slowly would  be
> 
>     if (@my_keys = keys %a) {

Perhaps this should be documented.  I gave it a few tests of wort-case
scenarios...

1. Null assignment (an assignment nonetheless)

	$assoc{''}='';
	print %assoc ? "true" : "false";

   Results in true.

2. Defining the assoc as a local

	sub foo {
		local(%assoc);
		print %assoc ? "true" : "false";
	}
	&foo

   Results in false.

3. A new dbm file

	dbmopen(%assoc,"newdbmfile",0600);
	print %assoc ? "true" : "false";

   Results in false.


I performed these tests with Perl 3.0 (41) on a SunOS 4.1 system.
I believe ndbm was compiled into our perl.  Just FYI.

Someone more knowledgable might want to explain why/how this works.

-- 
Eric Ziegast

tchrist@convex.COM (Tom Christiansen) (05/31/91)

From the keyboard of ziegast@eng.umd.edu (Eric W. Ziegast):
:(Tom Christiansen) writes:
:> From the keyboard of (Przemek Klosowski):
:>       How does one check if the associative array was assigned at all?
:> 
:>     if (%a) {
:> 
:> will actually work, but I don't think that Larry has ever documented
:> this.  

:Perhaps this should be documented.  I gave it a few tests of wort-case
:scenarios...
:
:Someone more knowledgable might want to explain why/how this works.

Here's an article from Larry around 3700 articles ago explaining this:

 Date:         30 Jul 90 18:24:01 GMT
 From:         lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall)
 Subject:      Re: #elements in assoc array / array in scalar context
 Reply-to:     lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall)
 Organization: Jet Propulsion Laboratory, Pasadena, CA
 Newsgroups:   comp.lang.perl
 
 
 In article <15429@thorin.cs.unc.edu> tell@oscar.cs.unc.edu () writes:
 : 
 : What is the recommended way to find out how many elements are in an
 : associative array?  I don't see any method that looks particularly
 : efficient.
 
 There isn't any efficient way.  You can find out how many buckets in the
 hash table are in use, which will give you an approximation for non-dbm
 files, since most filled buckets will only have a single entry.  I suppose
 I could keep a count, but I don't.  And such a count wouldn't work on dbm
 files anyway.  Currently, in a scalar context, %array returns the
 filled_buckets/allocated_buckets ratio.  That's 0/4 on an empty associative
 array.  After the next patch, it will return 0 if no buckets are filled,
 so you can at least use %array as a boolean.
 
 Given the problem with dbm files, I don't anticipate changing Perl to
 keep track.  If you want the count, then $count++.
 
 Larry

Sounds like he's documented it to me. :-)

--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
		"So much mail, so little time." 

lawrence@APS.Atex.Kodak.COM (Scott Lawrence) (05/31/91)

In article <1991May31.011755.4305@eng.umd.edu> ziegast@eng.umd.edu (Eric W. Ziegast) writes:

Eric>    I gave it a few tests of wort-case scenarios...

Eric>    1. Null assignment (an assignment nonetheless)
Eric>            $assoc{''}='';
Eric>            print %assoc ? "true" : "false";
Eric>       Results in true.

Eric>    2. Defining the assoc as a local
Eric>            sub foo {
Eric>                    local(%assoc);
Eric>                    print %assoc ? "true" : "false";
Eric>            }
Eric>            &foo
Eric>       Results in false.

Eric>    3. A new dbm file
Eric>            dbmopen(%assoc,"newdbmfile",0600);
Eric>            print %assoc ? "true" : "false";
Eric>       Results in false.

Eric>    I performed these tests with Perl 3.0 (41) on a SunOS 4.1 system.
Eric>    I believe ndbm was compiled into our perl.  Just FYI.
Eric>    Eric Ziegast

I got the same results with 4.03 on SunOS 4.1, and was prompted to check
on the behaviour of `defined' in those same cases.  The interesting one
turned out to be case 2, in which local( %assoc2 ) does not cause
defined( %assoc2 ) to return true.

===========

$assoc1{''}='';
print "Case 1 ", ( defined(  %assoc1 )? "defined" : "undefined" ), "\n";

Case 1 defined <<<<<< makes sense

======
sub case2
{
local( %assoc2 );
print "Case 2 ", (defined(%assoc2)? "defined" : "undefined" ), "\n";
}
&case2;

Case 2 undefined 

======

dbmopen( %assoc3, "/tmp/testdbmfile", 0777);
print "Case 3 ", (defined(%assoc3)? "defined" : "undefined" ), "\n";

Case 3 defined
======
--
Scott Lawrence       <lawrence@APS.Atex.Kodak.COM>   Voice: {US} 508-670-4023
Atex Advanced Publishing Systems                     G3Fax: {US} 508-670-4033
Atex, Inc; 165 Lexington St. MS 400/165L; Billerica MA 01821

allbery@NCoast.ORG (Brandon S. Allbery KF8NH) (06/04/91)

As quoted from <1991May30.205555.14490@convex.com> by Tom Christiansen <tchrist@convex.COM>:
+---------------
|     if (%a) {
| 
| will actually work, but I don't think that Larry has ever documented
| this.  More portably, and more slowly would  be
+---------------

Not in so many words he hasn't, but %a has an array value, and therefore has a
scalar value (the number of elements in the array generated by %a in an array
context).  Note that %a will be *twice* the number of entries in the assoc
array, since it is a list of keys *and* values.

...but it occurs to me that %a in either context will not do what you expect
if %a has been bound to a dbm file.  Ooooops.

++Brandon
-- 
Me: Brandon S. Allbery			 KF8NH: DC to LIGHT!  [44.70.4.88]
Internet: allbery@NCoast.ORG		 Delphi: ALLBERY
uunet!usenet.ins.cwru.edu!ncoast!allbery

tchrist@convex.COM (Tom Christiansen) (06/04/91)

From the keyboard of allbery@ncoast.ORG (Brandon S. Allbery KF8NH):
:As quoted from <1991May30.205555.14490@convex.com> by Tom Christiansen <tchrist@convex.COM>:
:+---------------
:|     if (%a) {
:| 
:| will actually work, but I don't think that Larry has ever documented
:| this.  More portably, and more slowly would  be
:+---------------
:
:Not in so many words he hasn't, but %a has an array value, and therefore has a
:scalar value (the number of elements in the array generated by %a in an array
:context).  Note that %a will be *twice* the number of entries in the assoc
:array, since it is a list of keys *and* values.

Um, no, I don't think that's what's afoot: see Larry's article I reposted
on what you get from a scalar %a reference.

--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
	    "Perl is to sed as C is to assembly language."  -me