Jump to content

Variable variables - a variation?


Pedsdude

Recommended Posts

I currently have a piece of code which is performed 5 times, each time with only a few items being changed:

 

if(strpos($mapsRatingEvaluate, ",1,") == 0){
    $oneCounter++; }
while($oneoffset = strpos($mapsRatingsEvaluate, ",1,", $oneoffset + 1)){
    $oneCounter++;
}
     
if(strpos($mapsRatingEvaluate, ",2,") == 0){
    $twoCounter++; }
while($twooffset = strpos($mapsRatingsEvaluate, ",2,", $twooffset + 1)){
    $twoCounter++;
}

if(strpos($mapsRatingEvaluate, ",3,") == 0){
    $threeCounter++; }
while($threeoffset = strpos($mapsRatingsEvaluate, ",3,", $threeoffset + 1)){
    $threeCounter++;
}
        
if(strpos($mapsRatingEvaluate, ",4,") == 0){
    $fourCounter++; }
while($fouroffset = strpos($mapsRatingsEvaluate, ",4,", $fouroffset + 1)){
    $fourCounter++;
}
        
if(strpos($mapsRatingEvaluate, ",5,") == 0){
    $fiveCounter++; }
while($fiveoffset = strpos($mapsRatingsEvaluate, ",5,", $fiveoffset + 1)){
    $fiveCounter++;
}

 

 

How would I merge this into one concise statement?  I'm thinking of something of the following format, although the troublesome part is getting $oneCounter, $twoCounter etc. through the use of $($thenumbertext)Counter, which doesn't want to work:

 

$ratingnumbers = array(',1,'=>'one', ',2,'=>'two', ',3,'=>'three', ',4,'=>'four', ',5,'=>'five');

foreach ($ratingnumbers as $thenumber=>$thenumbertext) {
    
if(strpos($mapsRatingEvaluate, $thenumber) == 0){
    ${$thenumbertext}Counter++; }
while(${$thenumbertext}offset = strpos($mapsRatingsEvaluate, $thenumber, ${$thenumbertext}offset + 1)){
    ${$thenumbertext}Counter++;
}    
        
}

 

 

Any help on the matter would be appreciated!

Link to comment
Share on other sites

Will this work"

 

<?php 
$ratingnumbers = array(',1,'=>'one', ',2,'=>'two', ',3,'=>'three', ',4,'=>'four', ',5,'=>'five');

foreach ($ratingnumbers as $thenumber=>$thenumbertext) {
    
if(strpos($mapsRatingEvaluate, $thenumber) == 0){
    $v = $thenumbertext.'Counter';
	${$v}++;
}

$offset = $thenumbertext.'offset';
while($oneoffset = strpos($mapsRatingsEvaluate, $thenumber, ${$offset} + 1)){
    	$y = $thenumbertext.'Counter';
	${$y}++;
}    
}
?>

 

Link to comment
Share on other sites

Ok, a bit late, but try this:

 

<?php
$ratingnumbers = array(',1,'=>'one', ',2,'=>'two', ',3,'=>'three', ',4,'=>'four', ',5,'=>'five');

foreach ($ratingnumbers as $thenumber=>$thenumbertext) {
   
if(strpos($mapsRatingEvaluate, $thenumber) == 0){
    ${$thenumbertext . 'Counter'}++; }
while(${$thenumbertext . 'offset'} = strpos($mapsRatingsEvaluate, $thenumber, ${$thenumbertext . 'offset'} + 1)){
    ${$thenumbertext . 'Counter'}++;
}   
       
}
?>

But I'm sure there's a better solution, e.g. storing/increasing the values in an array.

Link to comment
Share on other sites

The first response gave the following error:

Fatal error: Maximum execution time of 30 seconds exceeded in /home3/codjumpe/public_html/maps/rate2.php on line 318

 

The second response works perfectly, thank you very much! :)

 

I can explain what it was doing in detail if you're interested, although I would imagine you're not seeing as thebadbad's solution works.

Link to comment
Share on other sites

I'm interested :-)

 

I'm sure there will be a nicer solution :-)

 

Okey dokey.  $mapsRatingEvaluate is taken from a database, and contains a string of usernames and their ratings, for example:

",Alex,5,Bob,2,Charles,3,"

Where Alex's rating is 5, Bob's rating is 2, Charles' rating is 3.

 

I then use the above code to calculate the number of 1-5s and then (using code that wasn't included in this query because it wasn't required) calculate an average rating.

Link to comment
Share on other sites

Actually, are you sure the code works as expected? strpos() returns 0 if the needle is found as the first part of the haystack, so you should instead check strpos() against === false. Not sure it would be a problem with your string though, but still. And you're using both $mapsRatingEvaluate and $mapsRatingsEvaluate - shoundn't they be identical?

Link to comment
Share on other sites

Well, I am glad I asked :-P

 

<?php
$mapsRatingEvaluate = ",Alex,5,Bob,2,Charles,3,Mike,3,";

//This will fill an array with $array[rating] = count;
$pieces = split(',', $mapsRatingEvaluate);
$counters = array();
foreach ($pieces as $piece)
{
  if (is_numeric($piece))
  {
    $counters[$piece]++;
  }
}
?>

 

Then if you want the number of times the rating was '5', just check $counters[5].

 

If you just want the average, this would be a easier to handle:

 

<?php
$mapsRatingEvaluate = ",Alex,5,Bob,2,Charles,3,Mike,3,";

//If you want the average, this is a better approach
$pieces = split(',', $mapsRatingEvaluate);
$ratings = array();
foreach ($pieces as $piece)
{
  if (is_numeric($piece))
  {
    $ratings[] = $piece;
  }
}

$average = array_sum($ratings) / sizeof($ratings);
?>

 

No drama. Of course, you can manipulate the array however you want afterwards. (I.E. if you want mode rather than mean, just do an rsort on $counters from the first example and take the first element).

 

**EDIT**

 

Come to think of it, it would be even better if you could change the format that the data is stored so that it's more like "Mike:3,Jim:4,Mark:2". Then you could split by comma, and each part a colon. That way you don't have to worry too much about checking for numbers and guessing which bit is the user and which bit is the rating.

 

Even better still, would be a table consisting of an id, user id and rating (and whatever else you need). Then you could just form a query to do all the work for you ;-)

 

For example:

 

id | user  | rating
---------------------
1  | Bob   | 3
2  | Jim   | 2
3  | Tom   | 1
4  | Mick  | 1

select ave(rating) from ratings;

Link to comment
Share on other sites

Wow, that's very nice :)

 

I have one concern, and that is the splitting at each ,.  In this case, some of the names might contain commas themselves, so would that still work effectively?  It's for this reason that I was specifically searching for ,#, so that it didn't mess up with people who have commas in their username.

 

Actually, are you sure the code works as expected? strpos() returns 0 if the needle is found as the first part of the haystack, so you should instead check strpos() against === false. Not sure it would be a problem with your string though, but still. And you're using both $mapsRatingEvaluate and $mapsRatingsEvaluate - shoundn't they be identical?

It's funny, the $mapsRatingEvaluate must have been a mistake, and when I change it to what it's meant to be I get errors!

Link to comment
Share on other sites

Wow, that's very nice :)

 

I have one concern, and that is the splitting at each ,.  In this case, some of the names might contain commas themselves, so would that still work effectively?  It's for this reason that I was specifically searching for ,#, so that it didn't mess up with people who have commas in their username.

 

 

You're welcome :-)

 

Splitting the string has no problems with a name with a comma in it, because of the is_numeric() check in the foreach loop.

 

For example, if your input was "Mike, 3, Thompson, Jim, 4, Karl, 2", the resultant array from the split would be array('Mike', 3, 'Thompson', 'Jim', 4, 'Karl', 2). When they are iterated over, only the numeric values are taken to be ratings.

 

You could extend the is_numeric() check to include a check for the rating being between 1 and 5 (or whatever your range is) to exclude non-valid votes (I.E. 99999)

 

On the other hand, if some of your names have commas in: you should really change your choice of delimiter to a pipe, colon, semi-colon or something else not very common in names :-)

 

Good luck!

Link to comment
Share on other sites

What method would you recommend in terms of storing it in the database and separating users from ratings and from each other?

 

Personally, I would suggest something like this:

 

users table:
user_id | username | password | etc...
---------------------------------------
1       | Jim      | fnwoeng4 | ...
2       | Mike     | wl35fm4  | ...
3       | James    | mr3j34os | ...
4       | Bob      | nrqn3    | ...

things_to_rate table:
thing_id | name
--------------------------
1        | some game
2        | a tv
3        | sega megadrive

ratings table:
rating_id | thing_id | user_id | rating
----------------------------------------
1         | 1        | 3       | 2
2         | 1        | 2       | 5
3         | 3        | 3       | 1
4         | 3        | 2       | 3
5         | 1        | 4       | 5

 

Where users contains your user details, things_to_rate contains the things you want to rate, and ratings contains the actual ratings.

 

Having a table structure like this is very useful, because it means there's a whole lot you can do with it with just SQL. If you wanted the average ratings for a thing, you would just execute something like:

 

select avg(rating) from ratings where thing_id = 2;

 

If you also wanted the name of that thing, you could just join it, like so:

 

select 
  things_to_rate.name, 
  avg(ratings.rating) 
from 
  ratings 
join 
  things_to_rate on
    (things_to_rate.thing_id = ratings.thing_id)
where 
  ratings.thing_id = 2
group by
  things_to_rate.name
;

 

This approach has lots of other advantages too. It means that if someone ever changes their username, you don't have to go through and update the ratings table.

Link to comment
Share on other sites

Wow, thank you for that very informative post :)

 

I think I might have to consider a significant change in the structure of my website database, as the way it is currently organised isn't particularly efficient.

 

I completely overlooked the case of people changing usernames (currently only Admins can change usernames, but despite this it will still effectively wipe that user's history!).

 

I'll have a long think about it and will no doubt get back to you asking for help (if you're willing / able to persist with me! :D).

Link to comment
Share on other sites

Wow, thank you for that very informative post :)

 

...

 

I'll have a long think about it and will no doubt get back to you asking for help (if you're willing / able to persist with me! :D).

 

Thank you, and no problem :-)

 

I intend to be around here quite regularly. Feel free to ask, and I will do my best to answer :-)

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.