Pedsdude Posted June 2, 2009 Share Posted June 2, 2009 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! Quote Link to comment Share on other sites More sharing options...
anupamsaha Posted June 2, 2009 Share Posted June 2, 2009 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}++; } } ?> Quote Link to comment Share on other sites More sharing options...
thebadbad Posted June 2, 2009 Share Posted June 2, 2009 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. Quote Link to comment Share on other sites More sharing options...
TomNomNom Posted June 2, 2009 Share Posted June 2, 2009 Hi there, Could you maybe explain what it is you're trying to do, provide an example value for $mapsRatingEvaluate and an expected output? :-) Quote Link to comment Share on other sites More sharing options...
Pedsdude Posted June 2, 2009 Author Share Posted June 2, 2009 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. Quote Link to comment Share on other sites More sharing options...
TomNomNom Posted June 2, 2009 Share Posted June 2, 2009 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. I'm interested :-) I'm sure there will be a nicer solution :-) Quote Link to comment Share on other sites More sharing options...
Pedsdude Posted June 2, 2009 Author Share Posted June 2, 2009 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. Quote Link to comment Share on other sites More sharing options...
thebadbad Posted June 2, 2009 Share Posted June 2, 2009 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? Quote Link to comment Share on other sites More sharing options...
TomNomNom Posted June 2, 2009 Share Posted June 2, 2009 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; Quote Link to comment Share on other sites More sharing options...
Pedsdude Posted June 2, 2009 Author Share Posted June 2, 2009 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! Quote Link to comment Share on other sites More sharing options...
TomNomNom Posted June 2, 2009 Share Posted June 2, 2009 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! Quote Link to comment Share on other sites More sharing options...
Pedsdude Posted June 2, 2009 Author Share Posted June 2, 2009 What method would you recommend in terms of storing it in the database and separating users from ratings and from each other? Quote Link to comment Share on other sites More sharing options...
TomNomNom Posted June 3, 2009 Share Posted June 3, 2009 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. Quote Link to comment Share on other sites More sharing options...
Pedsdude Posted June 3, 2009 Author Share Posted June 3, 2009 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! ). Quote Link to comment Share on other sites More sharing options...
TomNomNom Posted June 3, 2009 Share Posted June 3, 2009 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! ). 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 :-) Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.