I'm coding a project that generates two arrays containing data. One array contains data for a specific country and the other contains data for all countries.
For example, if a user from the US makes a request, we will generate two arrays with data. One with data only for the US and the other with data for worldwide, including the US. I want to give the US array a 60% chance of being selected if the visitor is from the US. That means the other array will have a 40% chance of being selected.
How does one code this??
if(rand(1, 100) <= $probability_for_first_array)
{
use_the($first_array);
}
else
{
use_the($second_array);
}
I find this a straightforward, easy to read solution
<?php
$us_data = "us";
$worldwide_data = "worldwide";
$probabilities = array($us_data => 0.60, $worldwide_data => 0.40);
/* Code courtesy of Jesse Farmer
* For more details see http://goo.gl/fzq5
*/
function get_data($prob)
{
$random = mt_rand(0, 1000);
$offset = 0;
foreach ($prob as $key => $probability)
{
$offset += $probability * 1000;
if ($random <= $offset)
{
return $key;
}
}
}
?>
Gabi's example is fine for two sets, but if you have more data sets to pick from, the if-else structure is not appropriate.
Related
I am newish to PHP and web development. I am struggling a bit on how to best program this multi-dimensional array in PHP using for loops.
Essentially, it is a review website. I am using Bootstrap to render the data in different tabs. The data is being called via a jquery ajax request and the PHP page is returning JSON. Handlebars is being used to render the data in a template.
In the end, I'd like to send the data in roughly this format:
{
review: {
reviewstotal: INT,
count: INT,
summary: {
//iterated
tab: INT,
[{
Question:,
Value:,
Color: ,
Percent:,
},
{
Question:,
Value:,
Color: ,
Percent:,
}]
}
}
}
Where I'm struggling is at the "summary" section. The "Questions" are in an array, and I have the "Values" as another array. The color value would come from an if statement checking the "Value" and set a css value in the template. The percent is the "Value" multiplied by 10. Here is what I have so far to generate the array.
$array = array();
$color ="";
for ($x = 0; $x <= 5; $x++) {
$summary= [];
if ( ${"q{$x}"} <= 40){
$color = "danger";
} else if ( ${"q{$x}"} >= 70){
$color = "success";
} else {
$color = "warning";
}
$summary = array_push ($array, $aquest[$x], ${"q{$x}"}, $color, ${"q{$x}"}*10);
}
What I get as output is:
"summary": ["The facilities of the school adequately provide for a positive learning/working experience.",null,"danger",0,
"School provides adequate access to teaching materials without any extra expense by the teacher (books, lab supplies, art supplies, materials, copying, etc.)","9.50","danger",95,
"School is well funded to provide students and faculty with adaquate materials to support their course offering.","9.50","danger",95,
"Parent community is actively involved in supporting the school's mission and their child's education endeavors.","9.00","danger",90,
"Classroom student to teacher ratios are kept low.","8.75","danger",87.5,null,"7.63","danger",76.3]
What I am trying to achieve though is that each "Question" section is wrapped in it's own array. Like:
"summary": [["The facilities of the school adequately provide for a positive learning/working experience.",null,"danger",0],
["School provides adequate access to teaching materials without any extra expense by the teacher (books, lab supplies, art supplies, materials, copying, etc.)","9.50","danger",95],
["School is well funded to provide students and faculty with adequate materials to support their course offering.","9.50","danger",95],
["Parent community is actively involved in supporting the school's mission and their child's education endeavors.","9.00","danger",90],
["Classroom student to teacher ratios are kept low.","8.75","danger",87.5,null,"7.63","danger",76.3]]
And then I can add a consistent key to each of them.
You shall try the following -
$array = array();
$color ="";
//taking this out from the scope of loop
$summary= [];
for ($x = 0; $x <= 5; $x++) {
if ( ${"q{$x}"} <= 40){
$color = "danger";
} else if ( ${"q{$x}"} >= 70){
$color = "success";
} else {
$color = "warning";
}
//this is where the diference lies. $summary is a multi-dimension array.
//which means, each element of that array is an array itself.
//So this is how we represent such stuff in php.
//Element at index $x, is also defined as an array with keys containing the
//required values.
$summary[$x] = array(
"text" => $aquest[$x],
"somekey1" => ${"q{$x}"},
"somekey2" => $color,
"somekey3" => ${"q{$x}"}*10
);
}
//here you can print the summary.
print_r( $summary)
You're wanting to add arrays to an array, but currently you're adding variables to an array. So, you need to wrap $aquest[$x], ${"q{$x}"}, $color, ${"q{$x}"}*10 in an array.
$array = array();
$color ="";
for ($x = 0; $x <= 5; $x++) {
$summary= [];
if ( ${"q{$x}"} <= 40){
$color = "danger";
} else if ( ${"q{$x}"} >= 70){
$color = "success";
} else {
$color = "warning";
}
$summary = array_push ($array, array($aquest[$x], ${"q{$x}"}, $color, ${"q{$x}"}*10));
}
The code below is written mainly using PHP, but I am hoping to speed up the process, and parsing strings in PHP is slow.
Assume the following where I get a string from the database, and converted it into an array.
$data['options_list'] = array(
"Colours" => array('red','blue','green','purple'),
"Length" => array('3','4','5','6'),
"Voltage" => array('6v','12v','15v'),
);
These subarrays will each be a dropdown Select list, an the end user can select exactly 1 from each of the select lists.
When the user hits submit, I will want to match the submitted values against a "price table" pre-defined by the admins. Potentially "red" and "6v" would cost $5, but "red" and "5"(length) and "6v" would cost $6.
The question is, how to do so?
Currently the approach I have taken is such:
Upon submission of the form (of the 3 select lists), I get the relevant price rules set by the admin from the database. I've made an example of results.
$data['price_table'] =
array(
'red;4'=>'2',
'red;5'=>'3',
'red;6'=>'4',
'blue;3'=>'5',
'blue;4'=>'6',
'blue;5'=>'7',
'blue;6'=>'8',
'green;3'=>'9',
'green;4'=>'10',
'green;5'=>'11',
'green;6'=>'12',
'purple;3'=>'13',
'purple;4'=>'14',
'purple;5'=>'15',
'purple;6'=>'16',
'red;3'=>'1',
'red;3;12v'=>'17',
'blue;6;15v'=>'18',
);
Note : The order of the above example can be of any order, and the algorithm should work.
I then explode each of the above elements into an array, and gets the result that matches the best score.
$option_choices = $this->input->post('select');
$score = 0;
foreach($data['price_table'] as $key=>$value)
{
$temp = 0;
$keys = explode(';',$key);
foreach($keys as $k)
{
if(in_array($k, $option_choices))
{
$temp++;
}else{
$temp--;
}
}
if($temp > $score)
{
$score = $temp;
$result = $value;
}
}
echo "Result : ".$result;
Examples of expected results:
Selected options: "red","5"
Result: 3
Selected Options: "3", "red"
Result: 1
Selected Options: "red", "3", "12v"
Result: 17
The current method works as expected. However, handling these using PHP is slow. I've thought of using JSON, but that would mean that I would be giving the users my whole price table, which isn't really what I am looking for. I have also thought of using another language, (e.g python) but it wouldn't particularly be practical considering the costs. That leaves me with MySQL.
If someone can suggest a cheap and cost-efficient way to do this, please provide and example. Better still if you could provide an even better PHP solution to this which works fast.
Thank you!
It looks like you did work to make the results read faster but you're still parsing and testing every array part against the full list? This would probably run faster moving the search to MySQL and having extra columns there.
Since you can control the array (or test string) perhaps try fixed length strings:
$results = explode("\n", "
1 Red v1
22 Blue v2
333 Green v3");
$i = 0;
while($i < count($results)) {
$a = substr($results[$i], 0, 10);
$b = substr($results[$i], 10, 20);
$c = substr($results[$i], strpos(' ', strrev($results[$i]))-1);
if(stripos($userInput, $a . $b . $c) !== false) {
// parse more...
Supposedly JavaScript is good at this with memoizaion:
http://addyosmani.com/blog/faster-javascript-memoization/
I've got a class: Game which has values.
I've got two arrays with Game instances. Now I need to compare those two arrays for identical values in the game instance.
The game class has the attributes:
homeId
visitingId
Now I need to check for identical values in both arrays (they are large, 100+ game instances)
What I do is:
foreach ($games1 as $game1) {
foreach ($games2 as $game2) {
if ( ($game1->getHomeId() == $game2->getHomeId()) && ($game1->getVisitingId() == $game2->getVisitingId())) {
//Games are the same
}
}
}
This takes ages, is there a way to do it quicker?
Your current solution has complexity of O(n*n). it is possible to get it down to O(nlogn). For this you'll have to sort both arrays and then compare them. I would do something like:
$t1=array();
foreach ($games1 as $key=>$game1) {
$t1[$key]=$game1->getHomeId;
}
asort($t1);
$t2=array();
foreach ($games2 as $key=>$game2) {
$t2[$key]=$game2->getHomeId();
}
asort($t2);
$el1=each($t1);
$el2=each($t2);
do{
if ($el1['value']<$el2['value'])
$el1=each($t1);
elseif ($el1['value']>$el2['value'])
$el2=each($t2);
elseif($games1[$el1['key']]->getVisitingId == $games2[$el2['key']]->getVisitingId())
//game are the same
}while($el1 !== false && $el2 !== false)
this produces considerable overhead, so on small amount of data it will work slower. However the more data are in the arrays, the more efficient this algorithm will be.
How about using the array_diff function in some way which compares two arrays.
http://php.net/manual/en/function.array-diff.php
You're doing a lot of redundant calculations. Use a for loop instead of a foreach loop and start at where you left off, instead of at the beginning:
$games1_count = count($games1);
$games2_count = count($games2);
for($i=0; $i < $games1_count; $i++) {
$game1 = $games1[$i];
for($j=$i; $j < $games2_count; $j++) {
$game2 = $games2[$j];
if (($game1->getHomeId == $game2->getHomeId()) && $game1->getVisitingId == $game2->getVisitingId()) {
//Games are the same
}
}
}
This should provide a pretty significant speed boost. It won't decrease the order of the problem, but it will cut your calculations in half.
EDIT
You should also look into some sort of indexing. When you populate $game1, for instance, create an array that stores the games by value:
$game_index = array(
"home_id"=array(
"id1"=>$reference_to_game_with_id1,
"id2"=>$reference_to_game_with_id2
),
"visiting_id"=array(
"id1"=>$reference_to_game_with_visiting_id1,
"id2"=>$reference_to_game_with_visiting_id2
)
);
I've got it running but it's dirty I think.
At first I store the instances in a hashtable, the hash is made out of the visitorId and the homeId.
Then I create an hash of the visitorId and homeId of the other games array.
Then I retrieve the instance by using $table[$hash].
The arrays I used to have aren't the same in lenght, so this works. I don't know if it's too dirty to post here but it works :P
foreach($pGames as $pGame) {
$hash = $pGame->getHomeId() . '-' . $pGame->getVisitingId();
$table[$hash] = $pGame;
}
foreach($games as $game) {
$hash = $game->getHomeId() . '-' . $game->getVisitingId();
$pGame = $table[$hash];
if($pGame instanceof Game) {
//use the instance
}
}
I'm writing a small algorithm in PHP that goes through n number of movies with ratings, and will store the top 5. I'm not reading from a datafile, but from a stream so I cannot simply order the movies by rating.
My question is what is the most efficent way to keep track of the top 5 rated movies as I read the stream? Currently I do the following:
Read in 5 movies (into an array called movies[]), with two keys movies[][name] and movies[][rating]
Order the array by movies[rating] using array_multisort() (highest rating now sits at movies[4])
Read in the next movie
If this new movie rating > movies[0][rating] then replace movies[0] with this new movie
Re-order the list
Repeat 3-5 until finished
My method works, but requires a sort on the list after every read. I believe this to be an expensive method mostly due to the fact that every time I use array_multisort() I must do a for loop on 5 movies just to build the index to sort on. Can anyone suggest a better way to approach this?
Linked lists would work here.
Build a linked list that chains the first 5 movies in the correct order. For each new movie, just start at the the end of the chain and walk it until your movie is between one with a higher rating and one with a lower rating. Then insert your link into the list here. If the movie was better than the worst (and thus your list is now 6 long), just remove the last link in the chain, and you are back to 5.
No sorting, no indexing.
Your algorithm looks fine. I am not sure how the arrays are implemented in PHP. From an algorithm point of view: use a heap instead of an array.
No point in re-sorting after every read since you really only need to insert a new entry. Use the following algorithm, it's likely to get you the best speed. It's basically an unrolled loop, not the most beautiful code.
set movies[0..4].rating to -1.
while more movies in stream:
read in next movie.
if movie.rating < movies[0].rating:
next while
if movie.rating < movies[1].rating:
movies[0] = movie
next while
if movie.rating < movies[2].rating:
movies[0] = movies[1]
movies[1] = movie
next while
if movie.rating < movies[3].rating:
movies[0] = movies[1]
movies[1] = movies[2]
movies[2] = movie
next while
if movie.rating < movies[4].rating:
movies[0] = movies[1]
movies[1] = movies[2]
movies[2] = movies[3]
movies[3] = movie
next while
movies[0] = movies[1]
movies[1] = movies[2]
movies[2] = movies[3]
movies[3] = movies[4]
movies[4] = movie
At the end, you have your sorted list of movies. If there's less than 5, those others will have a rating of -1 so you'll know they're invalid. This is assuming that the rating on a real movie is zero or greater but you can adjust the values if they're not.
If you need to adjust it for more than 5 movies, you can. The best bet would be to roll up the loop again. At some point, however, it's going to become more efficient to sort it than use this method. This method's only really good for a small data set.
My method works, but requires a sort on the list after every read.
No it doesn't, it only requires a sort after you find a new movie whos rating is > movies[0][rating].
This method seems efficient to me. You only sort occasionally when there's a new entry for the top 5, which will happen less the more movies you process.
How big is the list? I'm guessing it's not an option to keep the entire list in memory, and sort it at the end?
there is no need for two keys in array. array with name as key, and rating as value will do. Sort it with arsort();
the algorithm is not perfect, you can do it optimally with linked list. Although I think linked list implemented in PHP will be actually slower that function call to asort() for 6 elements. For big O estimation, you can assume that sorting 6 elements has constant time.
You'll only sort when you encounter movie rated higher then the actual, so in average case you'll do it less an less often, while progressing. You'll sort on every movie only in worst case scenario of having initial list sorted from lowest rated.
Here’s what I would do:
// let’s say get_next_movie () returns array with 'rating' and 'name' keys
while ($m = get_next_movie ()) {
$ratings[$m['rating']][] = $m['movie'];
$temp_ratings = $ratings;
$top5 = array ();
$rating = 5;
while (1) {
if (count ($temp_ratings[$rating])) {
$top5[] = array_shift ($temp_ratings[$rating]);
} elseif ($rating > 0) {
--$rating;
} else {
break;
}
}
// $top5 has current top 5 :-)
}
$ratings array looks like this, each rating has array of movies inside:
Array
(
[5] => Array
(
[0] => Five!
)
[3] => Array
(
[0] => Three
[1] => Threeeeee
[2] => Thr-eee-eee
)
[4] => Array
(
[0] => FOR
)
)
Maybe this can be of help.
class TopList {
private $items = array();
private $indexes = array();
private $count = 0;
private $total = 5;
private $lowest;
private $sorted = false;
public function __construct($total = null) {
if (is_int($total))
$this->total = $total;
$this->lowest = -1 * (PHP_INT_MAX - 1);
}
public function addItem($index, $item) {
if ($index <= $this->lowest)
return;
$setLowest = $this->count === $this->total;
if ($setLowest) {
/* //remove first added
$lowestIndex = array_search($this->lowest, $this->indexes);
/*/ //remove last added
$lowestIndex = end(array_keys($this->indexes, $this->lowest));
//*/
unset($this->indexes[$lowestIndex], $this->items[$lowestIndex]);
} else {
++$this->count;
$setLowest = $this->count === $this->total;
}
$this->indexes[] = $index;
$this->items[] = $item;
$this->sorted = false;
if ($setLowest)
$this->lowest = min($this->indexes);
}
public function getItems() {
if (!$this->sorted) {
array_multisort($this->indexes, SORT_DESC, $this->items);
$this->sorted = true;
}
return $this->items;
}
}
$top5 = new TopList(5);
foreach ($movies as $movie) {
$top5->addItem($movie['rating'], $movie);
}
var_dump($top5->getItems());
I have a PHP script which reads a large CSV and performs certain actions, but only if the "username" field is unique. The CSV is used in more than one script, so changing the input from the CSV to only contain unique usernames is not an option.
The very basic program flow (which I'm wondering about) goes like this:
$allUsernames = array();
while($row = fgetcsv($fp)) {
$username = $row[0];
if (in_array($username, $allUsernames)) continue;
$allUsernames[] = $username;
// process this row
}
Since this CSV could actually be quite large, it's that in_array bit which has got me thinking. The most ideal situation when searching through an array for a member is if it is already sorted, so how would you build up an array from scratch, keeping it in order? Once it is in order, would there be a more efficient way to search it than using in_array(), considering that it probably doesn't know the array is sorted?
Not keeping the array in order, but how about this kind of optimization? I'm guessing isset() for an array key should be faster than in_array() search.
$allUsernames = array();
while($row = fgetcsv($fp)) {
$username = $row[0];
if (isset($allUsernames[$username])) {
continue;
} else {
$allUsernames[$username] = true;
// do stuff
}
}
The way to build up an array from scratch in sorted order is an insertion sort. In PHP-ish pseudocode:
$list = []
for ($element in $elems_to_insert) {
$index = binary_search($element, $list);
insert_into_list($element, $list, $index);
}
Although, it might actually turn out to be faster to just create the array in unsorted order and then use quicksort (PHP's builtin sort functions use quicksort)
And to find an element in a sorted list:
function binary_search($list, $element) {
$start = 0;
$end = count($list);
while ($end - $start > 1) {
$mid = ($start + $end) / 2;
if ($list[$mid] < $element){
$start = $mid;
}
else{
$end = $mid;
}
}
return $end;
}
With this implementation you'd have to test $list[$end] to see if it is the element you want, since if the element isn't in the array, this will find the point where it should be inserted. I did it that way so it'd be consistent with the previous code sample. If you want, you could check $list[$end] === $element in the function itself.
The array type in php is an ordered map (php array type). If you pass in either ints or strings as keys, you will have an ordered map...
Please review item #6 in the above link.
in_array() does not benefit from having a sorted array. PHP just walks along the whole array as if it were a linked list.