I have an events loop set up; and I also have an ads loop set up.
I want to inject each 'ad' into the events loop at random points. These loops/arrays have different set ups so can't push into the loop/array.
I had the below set up, which tended to work, but within ad.inc it was getting a random ad... whereas it should be getting the total count of ads and injecting them randomly into the events until that count is reached.
$count = 1;
$total = count($events);
$random = rand(3, $total);
foreach ($events as $event) {
include('./inc/events-item.inc');
if ($count == $random) {
include("./inc/ad.inc");
$random = rand($count, $total);
}
$count++;
}
For example, if my total events count is 30, and my total ads count is 4 then I should see the four ads randomly injected into the 30 events.
Any help?
Create array of all positions for ads. If you have 30 ads - there're 30 positions, from 0 to 29:
$positions = range(0, 29);
// now get 4 random elements from this array:
$rand_positions = array_rand($positions, 4);
// though `array_rand` returns array of keys
// in this case keys are the same as values
// iterate over your events and if counter equals
// to any value in $rand_positions - show ad
$i = 0;
foreach ($events as $event) {
include('./inc/events-item.inc');
if (in_array($i, $rand_positions, true)) {
include("./inc/ad.inc");
}
$i++;
}
You need to randomly select 4 (or however many ads you have) points between 30 (or however many entries you have) other points. Here is a possible solution.
// Set up counts
$entry_count = count($events);
$ad_count = 4;
// Create an array of entry indices, and an array of ad indices
$entry_indices = range(0, $entry_count - 1);
$ad_indices = array();
// Fill the ad indices with random elements from the entry indices
for ($i = 0; $i < $ad_count; $i++) {
$entry = rand(0, count($entry_indices));
array_push($ad_indices, $entry_indices[$entry]);
array_splice($entry_indices, $entry, 1);
}
// Sort it so we only need to look at the first element
sort($ad_indices);
// Iterate through the events
$count = 0;
foreach ($events as $event) {
include('./inc/events-item.inc');
// If we have any ad indices left, see if this entry is one of them
if (count($ad_indices) > 0 && $count == $ad_indices[0]) {
include("./inc/ad.inc");
array_shift($ad_indices);
}
$count++;
}
Related
I want to print an array in spiral order. For arrays with sizes 3x3, 4x4, ...etc. my code works correctly, but for 3x5, 4x6 or 5x8 sizes the output is wrong, returning only the first iteration.
This is my simple code:
private function _spiral($rows, $cols, array $array) {
$offset = 0;
while($offset < ($rows - 1)){
for($col = $offset; $col <= $cols - 1; $col++){
print($array[$offset][$col] . ' ');
}
$offset++;
$cols--;
for($row = $offset; $row < $rows; $row++){
print($array[$row][$cols] . ' ');
}
$rows--;
for($col = $cols - 1; $col >= $offset; $col--){
print($array[$rows][$col] . ' ');
}
for($row = $rows; $row >= $offset; $row--){
print($array[$row][$offset - 1] . ' ');
}
}
}
Example with 3 rows and 4 columns:
$array = array(
array(00,01,02,03),
array(10,11,12,13),
array(20,21,22,23)
)
Expected result for this array is 0 1 2 3 13 23 22 21 20 10 11 12, but the output of my function stops after 10.
For 4 rows and 4 columns:
$array = array(
array(00,01,02,03),
array(10,11,12,13),
array(20,21,22,23),
array(30,31,32,33)
)
...it should return 0 1 2 3 13 23 33 32 31 30 20 10 11 12 22 21, and that is what my code returns.
But I want both cases to work with my code. How can I correct the code to also produce the correct output for the first, and other cases?
There are a few problems with your code:
it does not treat the four directions of traversal in the same way. You have four loops for these four directions, but in some you have <= as loop-end condition in others <, in some the condition is on something minus 1, in others not.
it has no provision for when all elements have been printed by the first or second inner loop, and thus the remaining loops will in some cases print already printed elements.
the outer loop condition does not check whether there are still columns that need traversal. It is not enough to test for such rows only.
Although you could try to fix your code, I think it is better to start from scratch, taking into account that the solution should be symmetric for all four directions. This is an important intuitive reaction to develop: spot symmetries. This will lead to less code and fewer bugs.
You want to traverse a dimension (row or column) in your array until you reach the border of the array or an element you already printed. Then you want to turn 90° to the right and repeat exactly the same logic over and over again. So if your code looks different for these different directions, something is not right.
I will share two implementations. Both will use the concept of the "current" cell, and let it move around in spiral motion.
The first solution treats going back or forward along a row with the same code, and similarly it has one piece of code for traversing a column forward or backward. So this solution has two inner loops, one for traversing along a row, and another for traversing along a column. The direction in which a row or column is traversed is kept in the $direction variable, which flips between 1 and -1 at each execution of the outer loop:
function _spiral(array $array) {
// No need to have the number of rows and columns passed as arguments:
// We can get that information from the array:
$rows = count($array);
$cols = count($array[0]);
// Set "current" cell to be outside array: it moves into it in first inner loop
$row = 0;
$col = -1;
$direction = 1; // Can be 1 for forward and -1 for backward
while ($rows > 0 and $cols > 0) {
// Print cells along one row
for ($step = 0; $step < $cols; $step++) {
$col += $direction;
print $array[$row][$col] . ' ';
}
// As we have printed a row, we have fewer rows left to print from:
$rows--;
// Print cells along one column
for ($step = 0; $step < $rows; $step++) {
$row += $direction;
print $array[$row][$col] . ' ';
}
// As we have printed a column, we have fewer columns left to print from:
$cols--;
// Now flip the direction between forward and backward
$direction = -$direction;
}
}
Note the perfect symmetry between the first inner loop and the second inner loop.
In a second solution, this use of symmetry is taken one step further, in order to replace the two inner loops with only one. For that to happen we must abandon the use of separate variables for rows and columns, and use the concept of a size related to a dimension:
function _spiral(array $array) {
// This version of the function aims to treat rows and columns in the same way,
// They are just another dimension, but all the logic is exactly the same:
// $size[] has the number of rows in $size[0] and number of columns in $size[1]
$size = Array(count($array), count($array[0]));
// $current[] has the current row in $current[0] and current column in $current[1]
$current = Array(0, -1);
// $direction[] has the current row-traversal direction in $direction[0]
// and column-traveral direction in $direction[1]
$direction = Array(1, 1);
$dimension = 0; // Which dimension to traverse along, can be 0 for row, 1 for column
while ($size[$dimension] > 0) {
// Switch dimension (row to column, column to row), to traverse along
$dimension = 1 - $dimension;
// Print one line along that dimension, in its current direction
for ($step = 0; $step < $size[$dimension]; $step++) {
$current[$dimension] += $direction[$dimension];
print $array[$current[0]][$current[1]] . ' ';
}
// As we have printed a line, we have fewer left to print from:
$size[1 - $dimension]--;
// Now flip the direction between forward and backward for this dimension:
$direction[$dimension] = -$direction[$dimension];
}
}
An extended version
Upon request more than one year later: here is a version that allows one to choose the corner to start from, and whether to do it counter-clockwise instead of clockwise. This function will not print the result, but return a 1D array, with the spiral sequence. This way you can decide yourself what to do with the result: print it, or ... whatever.
function spiral(array $array, $startRight = false, $startBottom = false,
$counterClockWise = false) {
// This version allows to select which corner to start from, and in which direction.
// $size[] has the number of rows in $size[0] and number of columns in $size[1]
$size = [count($array), count($array[0])];
// $direction[] has the current row-traversal direction in $direction[0]
// and column-traversal direction in $direction[1]
$direction = [$startBottom ? -1 : 1, $startRight ? -1 : 1];
// Which dimension to traverse along: false means row, true means column.
// Every one of the optional arguments will flip the first dimension to use:
$dimension = ($startBottom xor $startRight xor $counterClockWise);
// $current[] has the current row in $current[0] and current column in $current[1]
$current = [$startBottom * (count($array)-1), $startRight * (count($array[0])-1)];
// Go back one step, outside of the grid
$current[!$dimension] -= $direction[!$dimension];
while ($size[$dimension] > 0) {
// Switch dimension (row to column, column to row), to traverse along
$dimension = !$dimension;
// Print one line along that dimension, in its current direction
for ($step = 0; $step < $size[$dimension]; $step++) {
$current[$dimension] += $direction[$dimension];
$result[] = $array[$current[0]][$current[1]]; // store in new array
}
// As we have printed a line, we have fewer left to print from:
$size[!$dimension]--;
// Now flip the direction between forward and backward for this dimension:
$direction[$dimension] = -$direction[$dimension];
}
return $result; // Return the resulting spiral as a 1D array
}
See it run on eval.in
First of all, I apologize for my lack of English. I hope you do understand what I'm trying to explain here.
So basically I need to build a function that would limit the number of duplicate values inside an array.
The reason I need to do this is that I'm building a system that would divide numbers into groups and every group has to have the same amount of numbers.
EDIT: Random number represents the group number.
I've written a function do this but for some reason, it is not working properly.
function jagaTiimid($max, $liiget, $tArvLength, $tArv){
$tiimid = []; //Starting array
for($z=0;$z<$liiget;$z++){
$numbers = [];
$rn = randomNumber($tArvLength, $tArv, $numbers); //Generate a random number for a group, etc group 1, group 2, group 3
$mitu = countInArray($tiimid, $rn); //Check how many times that number has occured in array
if($mitu == $max){ //If it equals to maximum number of times then...
$rnUus = randomNumber($tArvLength, $tArv, $numbers); //generate a new random number
while($rnUus == $rn){
$numbers = [];
$rnUus = randomNumber($tArvLength, $tArv, $numbers);
} //loop until the new generated number doesn't equal to old rn.
$tiimid[] = $rnUus; //if it doesn't equal to $rn then push into array
}else{
$tiimid[] = $rn;
}
}
return $tiimid;
}
For some reason the number still occures more than it is suppose to.
Basically how it shouldn't end up is.
As you can see, one group(group 2) occurs more times than other group but it should be equal for both groups.
EDIT: CountInArray();
function countInArray($array, $what) {
$count = 0;
for ($i = 0; $i < count($array); $i++) {
if ($array[$i] === $what) {
$count++;
}
}
return $count;
}
When the first random pick hits a number that is already used $liiget times, the inner loop kicks in, but it does not check whether the newly generated random number already occurs $liiget times.
For efficiency I would keep track of the number of times a number has been used. Also, you could benefit from a safety net, in case there really is no number any more that would not exceed the maximum recurrence.
It is not necessary to have a nested loop. The code would look like this:
function jagaTiimid($max, $liiget, $tArvLength, $tArv){
$tiimid = []; //Starting array
$counts = []; // Helper for quick count
$tries = 0; // Counter to avoid infinite looping
while (count($tiimid) < $liiget && $tries++ < 100) {
$numbers = [];
$rn = randomNumber($tArvLength, $tArv, $numbers); //Generate a random number for a group, etc group 1, group 2, group 3
if (!isset($counts[$rn])) $counts[$rn] = 0; // initialise on first occurence
if ($counts[$rn] < $max) {
$tiimid[] = $rn; // add it to the result
$counts[$rn]++; // ... and adjust the count
$tries = 0; // reset the safety
}
}
return $tiimid;
}
replace while($rnUus == $rn) with while(countInArray($tiimid, $rnUus) >= $max)
– Ilya Bursov
So, I want to distribute evenly lists across 3 columns. The lists cannot be broken up or reordered.
At the moment, I have 5 lists each containing respectively 4, 4, 6, 3 and 3 items.
My initial approach was:
$lists = [4,4,6,3,3];
$columns = 3;
$total_links = 20;
$items_per_column = ceil($total_links/$columns);
$current_column = 1;
$counter = 0;
$lists_by_column = [];
foreach ($lists as $total_items) {
$counter += $total_items;
$lists_by_column[$current_column][] = $total_items;
if ($counter > $current_column*$links_per_column) {
$current_column++;
}
}
Results in:
[
[4],
[4,6],
[3,3]
]
But, I want it to look like this:
[
[4,4],
[6],
[3,3]
]
I want to always have the least possible variation in length between the columns.
Other examples of expected results:
[6,4,4,6] => [[6], [4,4], [6]]
[4,4,4,4,6] => [[4,4], [4,4], [6]]
[10,4,4,3,5] => [[10], [4,4], [3,5]]
[2,2,4,6,4,3,3,3] => [[2,2,4], [6,4], [3,3,3]]
Roughly what you need to do is loop over the number of columns within your foreach(). That will distribute them for you.
$numrows = ceil(count($lists) / $columns);
$thisrow = 1;
foreach ($lists as $total_items) {
if($thisrow < $numrows){
for($i = 1; $i <= $columns; $i++){
$lists_by_column[$i][] = $total_items;
}
}else{
//this is the last row
//find out how many columns need to fit.
//1 column is easy, it goes in the first column
//2 columns is when you'll need to skip the middle one
//3 columns is easy because it's full
}
$thisrow++;
}
This will be an even distribution, from left to right. But you actually want a modified even distribution that will look symmetrical to the eye. So within the foreach loop, you'll need to keep track of 1.) if you're on the last row of three, and 2.) if there are 2 remainders, to have it skip col2 and push to col3 instead. You'll need to set that up to be able to play around with it,...but you're just a couple of logic gates away from the land of milk and honey.
So, I ended up using this code:
$lists = [4,4,6,3,3];
$columns = 3;
$total_links = 20;
$items_per_column = ceil($total_links/$columns);
$current_column = 1;
$lists_by_column = [];
for ($i = 0; $i < count($lists); $i++) {
$total = $lists[$i];
$lists_by_column[$current_column][] = $lists[$i];
//Loop until reaching the end of the column
while ($total < $items_per_column && $i+1 < count($lists)) {
if ($total + $lists[$i+1] > $items_per_column) {
break;
}
$i++;
$total += $lists[$i];
$lists_by_column[$current_column][] = $lists[$i];
}
//When exiting the loop the last time we need another break
if (!isset($lists[$i+1])) {break;}
//If the last item goes onto the next column
if (abs($total - $items_per_column) < abs($total + $lists[$i+1] - $items_per_column)) {
$current_column++;
//If the last item goes onto the current column
} else if ($total + $lists[$i+1] > $items_per_column) {
$i++;
$lists_by_column[$current_column][] = $lists[$i];
$current_column++;
}
}
I've got a $identifier, $start_number and $end_number.
The start number is the number where the for loop should start
counting from
The end number is where the loop should stop counting
The identifier determinates how much is getting added to the start number
This for loops looks something like this:
$start_number = 102;
$end_number = 1051;
$identifier = 24;
for($i = $start_number; $i <= $end_number; $i += $identifier) {
//The first two times, add 1 to the identifier
//The second two times (we're at 4 now) add 5 to the identifier
//The third two times (were at 6 now) add 10 to the identifier
//The fourth two times (we're at 8 now) add 20 to the identifier
//etc...
}
I want it to add a dynamic number (which changes) to the $identifier each 2 times it loops, how do i do this?
Just keep track of where you are in your loop by using a counter. Then you can use the modulus operator to determine if it an even number iteration. You can add the appropriate value by using an array to store the values to add to $identifier with the count being the key to get your correct value.
$start_number = 102;
$end_number = 1051;
$identifier = 24;
$add = array(
2 => 1,
4 => 5,
6 => 10,
8 => 20
);
$count = 1;
for($i = $start_number; $i <= $end_number; $i += $identifier) {
if ($count % 2 === 0) {
$identifier += $add[$count];
}
$count++;
}
for($i = $identifier; $i <= $end_number; $i += $identifier) {
if($i%2 == 0){
//do your work
}
}
Let's say I have a mysql table with an id, some measurements and a DATE column.
Example: id, measurements, date_entered
This table stores some measurements of a patient so as to keep a record for him.
I want to make a graph which according to the count of rows that exist in the database will change dynamically the X-axis.
For example, if there are only 7 rows in the table I need to represent 7 days to the graph with the measurement for every day. If there are more than 14 days, I want it to change to respresent 2 weeks on X-axis and the average measurements(average for 1 week and average for the other too) on Y-axis and so on from weeks to months.
Can anyone help me on this? I cannot think of something that will do in my case..
I use JPGraph to make the line graph but i don't have a problem there. My problem is on how to handle the results.
I hope you will understand what I need! Thanks.
Something like this?
// Get the results from the database
$query = "SELECT `data_col` FROM `table` WHERE `condition_col` = 'some value'";
$result = mysql_query($query);
// Get all results into array and count them
$results = array();
for ($i = 0; $row = mysql_fetch_assoc($result); $i++) {
$results[] = $row;
}
// Re-format the data depending on number of results
$data = array();
if ($i < 14) { // Less than 14 days, show per day
foreach ($results as $row) {
$data[] = $row['data_col'];
}
} else if ($i < 56) { // Less than 8 weeks, show per-week
$thisweek = array();
for ($j = 0; isset($results[$j]); $j++) { // Loop the results
$thisweek[] = $results[$j]['data_col']; // Add result to this week total
if ($j % 7 == 0 && $j > 0) { // Every 7 days...
$data[] = array_sum($thisweek) / 7; // ...calculate the week average...
$thisweek = array(); // ...and reset the total
}
}
// If there is an incomplete week, add it to the data
$data[] = array_sum($thisweek) / count($thisweek);
} else { // 8 weeks or more, show per-month
$thismonth = array();
for ($j = 0; isset($results[$j]); $j++) { // Loop the results
$thismonth[] = $results[$j]['data_col']; // Add result to this month total
if ($j % 28 == 0 && $j > 0) { // Every 28 days...
$data[] = array_sum($thismonth) / 28; // ...calculate the month average...
$thismonth = array(); // ...and reset the total
}
}
// If there is an incomplete month, add it to the data
$data[] = array_sum($thismonth) / count($thismonth);
}
// $data now contains an array from which you should be able to draw your
// graph, where array keys are (sort of) x values and array values are y
// values.
Obviously, this solution assumes a 28-day month - it does not use the calendar, simply the number of days. You could do something horrible involving working out the stats based on some values returned by date() or similar, but this would likely drastically increase the calculation overhead and slow the process down.
Hopefully this will give you a place to start.