p2p.wrox.com Forums

Need to download code?

View our list of code downloads.


  Return to Index  

beginning_php thread: This should be SO SIMPLE! Arrghh! Nik! Someone! Help!


Message #1 by "Dan Ostrowski" <dan@t...> on Wed, 3 Apr 2002 17:53:52
I am doing a simple poll script.  Everytime someone votes on an option 
(out of a potential 5) the script puts a tally for that option in a 
database text field.

At present this tally is this ---    :i:   --- where i is the number 
selected.  So, for instance lets say 3 people have voted on the issue, and 
the results entry in the databse looks like thus:

:2::3::4:


Please don't ask WHY i have it like this, I just do.  Anyhow, so now i 
want to be able to go through and count up the tallys for use in the 
results section of the poll.

Simple stuff right?  Well, I thought... "I'll just pull out my buddy the 
regular expression!".

However, he has failed me miserably. However, he keeps telling me it's my 
fault.

First, I misinterpreted the way the 3rd parameter in the eregi() function 
works. I thought it stored ALL matches found against a string. Well, it 
only finds one match and then stores in the rest of the array any ()'ed 
out strings.

Bleh.  Well, then I have to loop through all the options (at this point 5) 
and somehow increment a count for them.  Here is my current, and futile, 
code(at least, this section of it):

$total_num_of_options = 5;         // sets the number of choises
$the_results = $data["results"];   // converts the database data into a var

// we are inside a loop right now reading database data in the array $data

for($i=1; $i <= $total_num_of_options; $i++) {

	$var = "true";
	$counter[$i] = 0;

	while($var == "true") {
						
	$the_string = ":$i:";
	$var = eregi($the_string, $the_results);
		if($var == "true") {
			$counter[$i]++;
		}
						

	}

	echo "Counter " . $i . " = " . $counter[$i] . "<br>";

}



Nice and useless.  The echo there at the end was to see what turns up and 
SUPRISE! It prints out the counter = 0 everytime.

I KNOW I could just make a results field in the database for every option, 
and increment 5 different counters in those fields.  However, I wanted to 
save database space, and (hahah!), figure out how to do this part of the 
code.

Anyhow, is there any function or way to go through a string and get the 
number of particular items?


Any help would be wonderful.. and keep me from pulling hair out.

dan
Message #2 by Jacob Cohen <cohen@r...> on Wed, 3 Apr 2002 09:40:25 -0800
Hello Dan,

dan@t... wrote:
> I am doing a simple poll script.  Everytime someone votes on an option
> (out of a potential 5) the script puts a tally for that option in a 
> database text field.
> At present this tally is this ---    :i:   --- where i is the number
> selected.  So, for instance lets say 3 people have voted on the issue, and 
> the results entry in the databse looks like thus:
> :2::3::4:
> Bleh.  Well, then I have to loop through all the options (at this point 5)
> and somehow increment a count for them.  Here is my current, and futile, 
> code(at least, this section of it):
> Anyhow, is there any function or way to go through a string and get the
> number of particular items?

I'm missing exactly how your database scheme is supposed to work..

You originally suggest that the database keeps a tally for a
particular option, but then your code suggests that you just store the
raw votes and have to synthesize the tally yourself. Which is it?

My understanding of your :2::3::4: syntax is that someone voted for
option 2, someone voted for option 3, and someone voted for option 4,
right?

Get rid of the leading : and trailing :, and you can use split or
preg_split to get an array of the votes. Then you can let PHP do the
tallying for you with array_count_values.

http://www.php.net/manual/en/function.preg-split.php
http://www.php.net/manual/en/function.array-count-values.php

$dbstring = ":1::3::4::3::3::5::4::2::1::1:";
$dbstring = substr($dbstring, 1, strlen($dbstring) - 1);
$votes = preg_split("/::/", $dbstring);
$totals = array_count_values($votes);

for ($i=1; $i <= 5; ++$i)
{
    echo "$i: $totals[$i]<br>\n";
}

Nik probably knows an easier way to do this, but this will work.

-- 
Regards,
Jake                          mailto:cohen@r...

Message #3 by "Nikolai Devereaux" <yomama@u...> on Wed, 3 Apr 2002 10:52:52 -0800
As long as you only have less than 10 items you're polling, you could play
with the math to help you out.

since PHP doesn't have a charAt() function, lemme write one real quick:

function charAt(& $string, $index)
{
   // simple validity check
   if( ($index < 0) || ($index > strlen($string)))
   {
      return false;
   }
   return substr($string, $index, 1);
}


The numbers we're interested in occur in the middle of every block of three.
We can  start at the first character, and then skip every three characters
in the string to get the digits.

for($i = 1; false !== ($char = charAt($string, $i)); $i += 3)
{
    $counter[$char]++;
}

The "false !== ($char = charAt(..))" part is important because of how PHP
determines true/false.  If you didn't use the equivalence operator, the
string "0" would probably evaluate to false and break out of the loop.


Take care,

Nik

Message #4 by Jacob Cohen <cohen@r...> on Wed, 3 Apr 2002 11:21:12 -0800
cohen@r... wrote:
> $dbstring = ":1::3::4::3::3::5::4::2::1::1:";
> $dbstring = substr($dbstring, 1, strlen($dbstring) - 1);
> $votes = preg_split("/::/", $dbstring);
> $totals = array_count_values($votes);
> for ($i=1; $i <= 5; ++$i)
> {
>     echo "$i: $totals[$i]<br>\n";
> }

Was just discussing this with Nik, and we came up with a few
improvements:

<?

$dbstring = ":1::3::4::3::3::5::4::2::1::1:";

// Actual useful code begins here
$len = strlen($dbstring) - 1;

if ($len > 0)
    $votes = preg_split("/::/", substr($dbstring, 1, $len - 1));
else
    $votes = array();

$total = array_count_values($votes);

// Sample result output
ksort($total);
foreach(array_keys($total) as $option)
{
    echo "Option $option got $total[$option] votes<br>\n";
}

?>

-- 
Regards,
Jake                          mailto:cohen@r...

Message #5 by "Nikolai Devereaux" <yomama@u...> on Wed, 3 Apr 2002 11:45:37 -0800
I had originally thought that Jake's way would take longer to execute
becuase he's creating two different arrays and executing a [simple] regex
function, but I was totally wrong.

I ran a few tests using varying sizes of source strings, and his version
runs significantly faster than mine. ~>:(


Also, since his version is robust enough to handle ANY* value separated by
::, whereas mine just handles single character values, I'd use the updated
version that he posted.


Take care,

Nik


* ANY value here means "any value that doesn't contain '::' in it".

Message #6 by "Dan Ostrowski" <dan@t...> on Wed, 3 Apr 2002 21:09:01
are you two really just disguised as people and really... gods?

=)

thanks so much.


dan
Message #7 by Jacob Cohen <cohen@r...> on Wed, 3 Apr 2002 11:56:59 -0800
yomama@u... wrote:
> Also, since his version is robust enough to handle ANY* value separated by
> ::, whereas mine just handles single character values, I'd use the updated
> version that he posted.
> * ANY value here means "any value that doesn't contain '::' in it".

This having been said, your approach of creating a user defined
function is attractive in the sense that if the database format ever
changes, you only have to modify one place in the code, as exemplified
in the usual "Nik on PHP" rants.

Change my

$votes = preg_split("/::/", substr($dbstring, 1, $len - 1));

to

$votes = get_votes($dbstring);

and define a get_votes() function that does the splitting or whatever
processing is necessary to extract an array of votes.

Since the user-defined function is only called once, it shouldn't be a big
performance hit.

-- 
Regards,
Jake                          mailto:cohen@r...

Message #8 by "Dan Ostrowski" <dan@t...> on Wed, 3 Apr 2002 21:17:29
> yomama@u... wrote:
> Also, since his version is robust enough to handle ANY* value separated 
by
> ::, whereas mine just handles single character values, I'd use the 
updated
> version that he posted.
> * ANY value here means "any value that doesn't contain '::' in it".

This having been said, your approach of creating a user defined
function is attractive in the sense that if the database format ever
changes, you only have to modify one place in the code, as exemplified
in the usual "Nik on PHP" rants.

Change my

$votes = preg_split("/::/", substr($dbstring, 1, $len - 1));

to

$votes = get_votes($dbstring);

and define a get_votes() function that does the splitting or whatever
processing is necessary to extract an array of votes.

Since the user-defined function is only called once, it shouldn't be a big
performance hit.

-- 
Regards,
Jake                          mailto:cohen@r...

Message #9 by "Dan Ostrowski" <dan@t...> on Wed, 3 Apr 2002 21:20:02
SORRY about the above post... please disregard!



What I meant to say is thanks for showing me that GREAT array_count_values 
function!  That is AWESOME!

dan
Message #10 by "Lawrence" <lkrubner@g...> on Thu, 4 Apr 2002 14:50:51 -0500
From: "Dan Ostrowski" <dan@t...>
> I am doing a simple poll script.  Everytime someone votes on an option
> (out of a potential 5) the script puts a tally for that option in a
> database text field.
>
> At present this tally is this ---    :i:   --- where i is the number
> selected.  So, for instance lets say 3 people have voted on the issue, and
> the results entry in the databse looks like thus:
>
> :2::3::4:

Don't know where to start. You know, of course, that it would be faster to use numbers instead of
text, right? As to your "save space in the database" I don't see what space you're saving. 5
interger fields don't take up more space than one big text field, and they go faster. And if you
have to use a text field, why the colon on both sides of the number? If you had it on just one side
then you could take this string and explode() it on the colon and then loop through the array,
testing each entry.







  Return to Index