Suppose I have some serially numbered items that are 1-n units wide, that need to be displayed in rows. Each row is m units wide. I need some pseudo-code that will output the rows, for me, so that the m-width limit is kept. This is not a knapsack problem, as the items must remain in serial number order - empty spaces at the end of rows are fine.
I've been chasing my tail over this, partly because I need it in both PHP and jQuery/javascript, hence the request for pseudo-code....
while (!items.isEmpty()) {
rowRemain = m;
rowContents = [];
while (!items.isEmpty() && rowRemain > items[0].width) {
i = items.shift();
rowRemain -= i.width
rowContents.push(i);
}
rows.push(rowContents);
}
Running time is Θ(number of items)
Modulus is your friend. I would do something like:
$items = array(/* Your list of stuff */);
$count = 0;
$maxUnitsPerRow = 4; // Your "m" above
while ($item = $items[$count]) {
if ($count % $maxUnitsPerRow == 0) {
$row = new row();
}
$row->addItemToRow($item);
$count++;
}
Here is an alternative php code ...
function arrayMaxWidthString($items, $maxWidth) {
$out = array();
if (empty($items)) {
return $out;
}
$row = $maxWidth;
$i = 0;
$item = array_shift($items);
$row -= strlen($item);
$out[0] = $item;
foreach ($items as $item) {
$l = strlen($item);
$tmp = ($l + 1);
if ($row >= $tmp) {
$row -= $tmp;
$out[$i] = (($row !== $maxWidth) ? $out[$i] . ' ' : '') . $item;
} elseif ($row === $maxWidth) {
$out[$i] = $item;
++$i;
} else {
++$i;
$row = $maxWidth - $l;
$out[$i] = $item;
}
}
return $out;
}
For what it's worth, I think I have what I was looking for, for PHP - but not sure if there is a simpler way...
<?php
// working with just a simple array of widths...
$items = array(1,1,1,2,1,1,2,1);
$row_width = 0;
$max_width = 2;
echo "Begin\n"; // begin first row
foreach($items as $item=>$item_width) {
// can we add item_width to row without going over?
$row_width += $item_width;
if($row_width < $max_width) {
echo "$item_width ";
} else if($row_width == $max_width) {
echo "$item_width";
echo "\nEnd\nBegin\n"; // end last row, begin new row
$row_width = 0;
} else if($row_width == 2* $max_width) {
echo "\nEnd\nBegin\n"; // end last row, begin new row
echo "$item_width";
echo "\nEnd\n"; // end new row
$row_width = 0;
if($item < count($items)) echo "Begin\n"; // new row
} else if($row_width > $max_width) {
echo "\nEnd\nBegin\n"; // end last row, begin new row
echo "$item_width";
$row_width = $item_width;
}
}
echo "\nEnd\n"; // end last row
?>
Related
The below code works and does output exactly what i want. I made a foreach loop getting the values of a specific field ($CustomFields...) which is part of a framework variable. Then is only counts that field when the condition is "group".
After that i want to het the average price of all fields / count.
// ########### Get average hourly rate for group classes
$itemsperhour = array();
$countperhour = 0;
foreach($listings as $listing) {
if ($CustomFields->fieldValue('jr_typeoflesson',$listing,false,false) == 'group') {
$itemsperhour[] = $CustomFields->field('jr_hourlyrateus',$listing,false,false);
$countperhour = $countperhour + 1;
}
}
//print_r($items);
if ($countperhour > 0) {
$totalperhour = array_sum($itemsperhour);
$averageperhour =($totalperhour / $countperhour);
echo round($averageperhour,2);
} else {
echo "No data";
}
unset ($averageperhour);
As said, the snippet works. But may I ask how other people would write such a script related to optimise such a piece of code (for speed and readability?
PHP 5.6+
Jasper
Below is one way of optimizing:
$totalperhour = 0;
$countperhour = 0;
foreach($listings as $listing) {
if ($CustomFields->fieldValue('jr_typeoflesson',$listing,false,false) == 'group') {
$totalperhour += $CustomFields->field('jr_hourlyrateus',$listing,false,false);
$countperhour = $countperhour + 1;
}
}
if($countperhour > 0) {
$averageperhour =($totalperhour / $countperhour);
echo round($averageperhour,2);
$averageperhour = '';
} else {
echo "No data";
}
I would suppose to use array_reduce function for getting the average:
$averageperhour = array_reduce($listings, function($average, $listing) use (&$CustomFields)
{
static $sum = 0;
static $counter = 0;
if ($CustomFields->fieldValue('jr_typeoflesson', $listing, false, false) == 'group') {
$sum += $CustomFields->field('jr_hourlyrateus', $listing, false, false);
$counter ++;
$average = round(($sum / $counter), 2);
}
return $average;
}, 'No data');
echo $averageperhour;
Not sure about speed improvement (needs testing), but this variant seems to me like more readable.
How about this?
$itemsPerHour = [];
foreach($listings as $listing) {
if ($CustomFields->fieldValue('jr_typeoflesson', $listing, false, false) !== 'group') {
continue;
}
$itemsPerHour[] = $CustomFields->field('jr_hourlyrateus', $listing, false, false);
}
$countPerHour = count($itemsPerHour);
if ($countPerHour > 0) {
$averagePerHour = array_sum($itemsPerHour) / $countPerHour;
echo round($averagePerHour,2);
} else {
echo "No data";
}
I have 38 days and 20 clubs (EPL).
How can I generate not repeated matches for this clubs in this days (schedule)?
For example:
Day 1:
club1 - club2
club3 - club4
...
club19 - club 20
Day 2:
club1 - club3
club2 - club4
...
club20 - club18
Each club plays with other two games (home and away). Respectively do not play with himself.
My thinks:
$clubs1 = array();
$clubs2 = array();
$days = range(1, 38);
$calendar = array();
$pars = array();
$rows = Yii::app()->db->createCommand()
->select('id')
->from('clubs')
->queryAll();
foreach ($rows as $item) {
$clubs1[] = $item['id'];
$clubs2[] = $item['id'];
}
shuffle($clubs1);
shuffle($clubs2);
$total = (count($clubs1) * 2) - 2;
for ($j = 1; $j <= $total; $j ++) {
$day = $days[$j];
for ($i = 0; $i < count($clubs1); $i++) {
WHAT I SHOULD DO IN THIS BODY?
}
}
You need only one clubs array
1) remove $clubs2
2) rename $clubs1 to $clubs
3) remove whole for structure
//for testing: $clubs=array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
$countofteams=count($clubs);
$c=1;
for($j=0;$j<2;$j++) //home/away
for($i=1;$i<$countofteams;$i++){ //move teams
echo '----DAY '.$c++.'----<br>';
for($a=0;$a<$countofteams;$a++) //all teams are playing
echo 'Match '.$clubs[$a].' vs '.$clubs[($a+$i)%$countofteams].'<br>';
}
$team = array();
$pars = array();
$rows = Yii::app()->db->createCommand()
->select('id')
->from('clubs')
->queryAll();
foreach ($rows as $k => $item) {
$team[$k+1] = $item['id'];
}
$all_team = count($team);
$k = $all_team/2;
$days = range(7, 100, 2); // first halh of season
$days2 = range(55, 100, 2); // second half
// 1 tour
for ($i=1; $i <= $k; $i++) {
$pars[] = $days[0].'|'.$team[$i].'|'.$team[($all_team-$i+1)];
$pars[] = $days2[0].'|'.$team[($all_team-$i+1)].'|'.$team[$i];
}
// Next tours
for($i=2; $i<$all_team; $i++)
{
$team2 = $team[2];
for($y=2;$y<$all_team;$y++)
{
$team[$y] = $team[$y+1];
}
$team[$all_team] = $team2;
for($j=1;$j<=$k;$j++)
{
$pars[] = $days[$i - 1].'|'.$team[$j].'|'.$team[($all_team-$j+1)];
$pars[] = $days2[$i - 1].'|'.$team[($all_team-$j+1)].'|'.$team[$j];
}
}
Here's my solution, replace the $clubs array with your result set of club IDs from database. The only slight inaccuracy with real-world EPL is that the second half of the season will mirror the first half exactly.
See if this adaption of http://board.phpbuilder.com/showthread.php?10300945-Round-Robin-Generator is any better :) - just for first half of the season.
$clubs = array(
'che', 'swa', 'ast', 'manc', 'liv', 'tot', 'ars', 'sot', 'hul', 'stok', 'wham', 'qpr', 'sun', 'mutd', 'lei', 'new', 'eve', 'wba', 'cry', 'bur',
);
shuffle($clubs);
$num_players = count($clubs) - 1;
// Set the return value
$ret = '';
// Generate the pairings for each round.
for ($round = 0; $round < $num_players; $round++) {
$ret .= '<h3>' . ($round + 1) . '</h3>';
$players_done = array();
// Pair each player except the last.
for ($player = 1; $player < $num_players; $player++) {
if (!in_array($player, $players_done)) {
// Select opponent.
$opponent = $round - $player;
$opponent += ($opponent < 0) ? $num_players : 1;
$playerName = $clubs[$player];
$opponentName = $clubs[$opponent];
// Ensure opponent is not the current player.
if ($opponent != $player) {
// Choose colours.
if (($player + $opponent) % 2 == 0 xor $player < $opponent) {
// Player plays white.
$ret .= "$playerName - $opponentName $br";
} else {
// Player plays black.
$ret .= "$opponentName - $playerName $br";
}
// This pair of players are done for this round.
$players_done[] = $player;
$players_done[] = $opponent;
}
}
}
// Pair the last player.
if ($round % 2 == 0) {
$playerName = $clubs[$num_players];
$opponent = ($round + $num_players) / 2;
$opponentName = $clubs[$opponent];
// Last player plays white.
$ret .= "$playerName - $opponentName $br";
} else {
$opponent = ($round + 1) / 2;
// Last player plays black.
$ret .= "$opponentName - $playerName $br";
}
}
echo $ret;
Basically this is related to a squash application where we have 2 scores. One is from winner point of view and another from loser point of view.
eg.
Score1: 11-5,11-5,11-5 (Winner point of view)
Score2: 5-11, 5-11,5-11 (Loser point of view)
Now in my logic i want to find which is the winner score and which is the loser score.
I have written my logic in the below way and it does work. But i want to know if their is any other better/optimized way of writing this.
$high1 = 0;
$high2 = 0;
$score1 = "2-11,5-11,4-11,4-4";
$score2 = "11-2,11-5,11-4,4-4";
$score1Array = explode(",",$score1);
$size = sizeof($score1Array);
for($i = 0; $i < $size; $i++) {
$checkscore1 = explode("-",$score1Array[$i]);
if($checkscore1[0] < $checkscore1[1]) {
$high1++;
}else if($checkscore1[0] > $checkscore1[1]) {
$high2++;
}
}
if($high1 > $high2) {
$winningScore = $score2;
$losingScore = $score1;
}else{
$winningScore = $score1;
$losingScore = $score2;
}
echo $winningscore;
echo $losingscore;
What about something like this:
function is_winning($score) {
$split_scores = preg_split('/(-|,)/', $score);
$wins = $losses = 0;
for($i = 0; $i < count($split_scores) / 2; $i += 2) {
if($split_scores[$i] > $split_scores[$i + 1])
$wins++;
if($split_scores[$i] < $split_scores[$i + 1])
$losses++;
}
return $wins > $losses;
}
Assuming $score is formatted as in your question. You can then use it like this:
$score1 = "2-11,5-11,4-11,4-4";
$score2 = "11-2,11-5,11-4,4-4";
if(is_winning($score1)) {
$winning_score = $score1;
$losing_score = $score2;
} else {
$winning_score = $score2;
$losing_score = $score1;
}
echo $winning_score;
echo $losing_score;
The idea is to split the score into an array where the even numbered indexes have the left score and the odd numbered indexes the right score. We then count the number of wins and the number of losses. If there's more wins then losses then we return true since the score was a winning score. If there's not more wins then losses we simply return false.
This should work
$score1 = "2-11,5-11,4-11,4-4";
$score2 = "11-2,11-5,11-4,4-4";
$l = $r = 0;
$score1_sets_arr = explode(',', $score1);
foreach ($score1_sets_arr as $set_score) {
$set_score_arr = explode('-', $set_score);
if ($set_score_arr[0] > $set_score_arr[1]) {
$l++;
} else {
$r++;
}
}
if ($l > $r) {
$winning_score = $score1;
$losing_score = $score2;
} else {
$winning_score = $score2;
$losing_score = $score1;
}
you can use this :
<?php
$high1 = 0;
$high2 = 0;
$score1 = "2-11,5-11,4-11,4-4";
$score2 = "11-2,11-5,11-4,4-4";
$explode = explode(",",$score1);
for($i=0;$i< sizeof($explode);$i++){
$explode2= explode("-", $explode[$i]);
if($explode2[0] <= $explode2[1]){
echo $explode2[0]."-";
echo $explode2[1]." ";
}
}
echo "<br />";
for($i=0;$i< sizeof($explode);$i++){
$explode2= explode("-", $explode[$i]);
if($explode2[1] >= $explode2[0]){
echo $explode2[1]."-";
echo $explode2[0]." ";
}
}
?>
for Winner point of view, all big score in left,otherwise in right. so u can just detect the first score.
$score1Array = explode(",",$score1);
$checkscore1 = explode("-",$score1Array[$i]);
if($checkscore1[0] < $checkscore1[1]) {
echo $score2;
echo $score1;
}else{
echo $score1;
echo $score2;
}
Fix: above code is wrong,try this:
$score1value = eval(str_replace(",","+",$score1));
$score2value = eval(str_replace(",","+",$score2));
if($score1value < $score2value) {
echo $score2;
echo $score1;
}else{
echo $score1;
echo $score2;
}
I have a simple foreach loop which enters data in a 2D array:
foreach ($query as $row){
if (!isset($sites[$row->site])){ $sites[$row->site] = array(); }
if (!isset($sites2[$row->site])){ $sites2[$row->site] = array(); }
if ($row->type == 1){
$sites[$row->site][] = array($row->data1, $row->data2);
} else {
$sites2[$row->site][] = array($row->data1, $row->data2);
}
}
I need a minimum of 20 entries in the array's ($sites and $sites2).
So if the query has lets say 5 rows, I want to reiterate the loop, repeating the 5 existent rows (inserting them inside the arrays) until the arrays reach 20 rows.
Any ideas?
try this:
$rows=0;
$haveDuplicated=false;
while($rows<20){
foreach ($query as $row){
if (!isset($sites[$row->site])){ $sites[$row->site] = array(); }
if (!isset($sites2[$row->site])){ $sites2[$row->site] = array(); }
if ($row->type == 1){
$sites[$row->site][] = array($row->data1, $row->data2);
} else {
$sites2[$row->site][] = array($row->data1, $row->data2);
}
$rows++;
if ($rows>=20 && $haveDuplicated) break;
}
$haveDuplicated=true;
}
I suppose you didn't see array_pad() yet?
Edit: Re-reading your question, I don't think this function is helpful, at least not by itself.
I'm quite tired today, and I can't concentrate much, so can you please do me the favour of showing us the expected results as opposed to your faulty(no offence) code?
$index = 0;
for ($i=0; $i < 20; $i++) {
if($index >= count($query))
$index = 0;
if (!isset($sites[$query[$index]->site])){ $sites[$query[$index]->site] = array(); }
if (!isset($sites2[$query[$index]->site])){ $sites2[$query[$index]->site] = array(); }
if ($query[$index]->type == 1){
$sites[$query[$index]->site][] = array($query[$index]->data1, $query[$index]->data2);
} else {
$sites2[$query[$index]->site][] = array($query[$index]->data1, $query[$index]->data2);
}
$index++;
}
This will work at all:
$row = 1;
while($row == 20){
foreach ($query as $row){
if (!isset($sites[$row->site])){ $sites[$row->site] = array(); }
if (!isset($sites2[$row->site])){ $sites2[$row->site] = array(); }
if ($row->type == 1){
$sites[$row->site][] = array($row->data1, $row->data2);
} else {
$sites2[$row->site][] = array($row->data1, $row->data2);
$row++;
}
}
When I launch my web page, increment doesn't work correctly!
It should go like this: $i = from 1 to x (0,1,2,3,4,5,6 etc..).
But instead it jumps over every step giving result of (1,3,5,7 etc..).
Why is this code doing this?
<ul class="about">
<?php
$result = mysql_query("SELECT * FROM info WHERE id = 1");
while ($row = mysql_fetch_assoc($result))
{
$bioText = $row['bio'];
}
$endBioTxt = explode("\n", $bioText);
for ($i=0; $i < count($endBioTxt);)
{
if (checkNum($i) == true)
{
echo "<li class='left'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
else
{
echo "<li class='right'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
$i++;
}
// Function to check if number is prime
function checkNum($num){
return ($num % 2) ? TRUE : FALSE;
}
?>
</ul>
Output:
Sometext!(right side)
0
1
Sometext2!(right side)
2
3
...
Please DONT do this as other suggested:
for ($i=0; $i < count($endBioTxt); $i++)
do this:
$count = count($endBioTxt);
for ($i=0; $i < $count; $i++) {
}
No need to calculate the count every iteration.
Nacereddine was correct though about the fact that you don't need to do:
$i++;
inside your loop since the preferred (correct?) syntax is doing it in your loop call.
EDIT
You code just looks 'strange' to me.
Why are you doing:
while ($row = mysql_fetch_assoc($result))
{
$bioText = $row['bio'];
}
???
That would just set $bioText with the last record (bio value) in the recordset.
EDIT 2
Also I don't think you really need a function to calculate the modulo of a number.
EDIT 3
If I understand your answer correctly you want 0 to be in the left li and 1 in the right li 2 in the left again and so on.
This should do it:
$endBioTxt = explode("\n", $bioText);
$i = 0;
foreach ($endBioTxt as $txt)
{
$class = 'left';
if ($i%2 == 1) {
$class = 'right';
}
echo '<li class="'.$class.'"><div>'.$txt.'</div></li>';
echo $i; // no idea why you want to do this since it would be invalid html
$i++;
}
Your for statement should be:
for ($i=0; $i < count($endBioTxt); $i++)
see http://us.php.net/manual/en/control-structures.for.php
$i++; You don't need this line inside a for loop, it's withing the for loop declaration that you should put it.
for ($i=0; $i < count($endBioTxt);$i++)
{
if (checkNum($i) == true)
{
echo "<li class='left'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
else
{
echo "<li class='right'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
//$i++; You don't need this line inside a for loop otherwise $i will be incremented twice
}
Edit: Unrelated but this isn't how you check whether a number is prime or not
// Function to check if number is prime
function checkNum($num){
return ($num % 2) ? TRUE : FALSE;
}
This code works, please test it in your environment and then uncomment/comment what you need.
<?php
// This is how query should look like, not big fan of PHP but as far as I remember...
/*
$result = mysql_query("SELECT * FROM info WHERE id = 1");
$row = mysql_fetch_assoc($result);
$bioText = $row['bio'];
$endBioTxt = explode("\n", $bioText);
*/
$endBioTxt[0] = "one";
$endBioTxt[1] = "two";
$endBioTxt[2] = "three";
$endBioTxt[3] = "four";
$endBioTxt[4] = "five";
$totalElements = count($endBioTxt);
for ($i = 0; $i < $totalElements; $i++)
{
if ($i % 2)
{
echo "<li class='left'><div>".$endBioTxt[$i]."</div></li>";
}
else
{
echo "<li class='right'><div>".$endBioTxt[$i]."</div></li>";
}
/*
// This is how you should test if all your array elements are set
if (isset($endBioTxt[$i]) == false)
{
echo "Array has some values that are not set...";
}
*/
}
Edit 1
Try using $endBioTxt = preg_split('/$\R?^/m', $bioTxt); instead of explode.