This question already has answers here:
PHP Foreach Pass by Reference: Last Element Duplicating? (Bug?) [duplicate]
(6 answers)
Closed 8 years ago.
I am new to PHP and I am experimenting
right now with some foreach loops in PHP.
I know that it is recommended to unset
the foreach loop variables after the loop.
I noticed that if I include the lines // 1 // and
// 2 //, this script prints the right thing.
...
Name: Rachel, Age: 56
Name: Grace, Age: 44
( It also prints the right thing if in the last loop, I use other
variable names like $n and $a instead of $name and $age.)
But if I comment lines // 1 // and // 2 // out, it prints:
Name: Lisa, Age: 28
Name: Jack, Age: 16
Name: Ryan, Age: 35
Name: Rachel, Age: 46
Name: Grace, Age: 34
Name: Lisa, Age: 38
Name: Jack, Age: 26
Name: Ryan, Age: 45
Name: Rachel, Age: 56
Name: Grace, Age: 56
Notice that 56 is printed two times.
Why does it behave that way?
I mean: what happens under the hood?
<?php
$employee_age = array();
$employee_age["Lisa"] = "28";
$employee_age["Jack"] = "16";
$employee_age["Ryan"] = "35";
$employee_age["Rachel"] = "46";
$employee_age["Grace"] = "34";
foreach( $employee_age as $name => $age){
echo "Name: $name, Age: $age <br />";
}
echo "<br>";
unset($age);
unset($name);
foreach( $employee_age as $name => &$age){
$age += 10;
}
// echo "<br>";
// unset($age); // 1 //
// unset($name); // 2 //
foreach( $employee_age as $name => $age){
echo "Name: $name, Age: $age <br />";
}
echo "<br>";
unset($age);
unset($name);
?>
If you var_dump($employee_age) before the second loop you'll notice that the last entry say &int instead of int. It is still a reference. It points to the last element (ignoring itself). And that is Rachel with 56.
Look into the manual's warning: http://pl.php.net/manual/en/control-structures.foreach.php
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
It's PHP's behavior of a referenced item.
Reason : I think this guy can explain better than me.
Just a little memo.
Alternatively, to fix this, you can add & to $age at your third foreach:
foreach( $employee_age as $name => &$age){
echo "Name: $name, Age: $age <br />";
}
Hm.. not sure why you unset,
simpler:
$employee_age = array(
"Lisa" => 28,
"Jack" => 16,
"Ryan" => 35,
"Rachel" => 46,
"Grace" => 34);
foreach( $employee_age as $name => $age){
echo "Name: $name, Age: $age <br />";
}
echo "<br>";
foreach( $employee_age as $name => $age){
$age2 = $age + 10; // (Don't think this is required)
echo "Name: $name, Age: $age2 <br />"; // Try with just $age+10
}
?>
Grace = 56 because your code is;
foreach( $employee_age as $name => &$age){
$age += 10;
}
Change it to
foreach( $employee_age as $name => $age){
$age += 10;
}
And Grace will 34 again.
Related
I have a form on my site for the users to post a results report into,
the report looks like this:
Nojoks's Tourney Bracket Tool Version 1.2.1.84
Tournament: 3/5 Backgammon 1:00pm
Date: 01/22/2017
Day: Sunday
Scheduled Start: 1.00pm PST
Actual Start: 20:00:30
Closed: 20:11:00
Host: Waiter ()
Number of Players: 15
1st place: poppop
1st place email: bobmitch1170#gmail.com
2nd place: Sarge
2nd place email: rgarvey5#hotmail.com
3rd place: Litigolfer
3rd place email: dostrow2008#gmail.com
3rd place: PhantomMask
3rd place email:
START POINTS
burnieboy 5
EU_BNL_Chris1 5
EU_IT_VIANG 5
GennaLee 5
happybear 5
MC_Vicky 5
merceaviles 5
MRC_cadet 5
poeticfool 5
UBG_Angel_D_8 5
UBG_sara1smoon 5
Litigolfer 60
PhantomMask 60
Sarge 90
poppop 120
STOP POINTS
this report is going to be identical everytime with some minor changes
I have already split this into an array with explode
$records = explode( PHP_EOL, $_POST['points'] );
$records = array_map('htmlspecialchars', $records );
$records = array_map ('trim', $records);
Then i have work on collecting the information from the top of the report like so:
// Get Date
$date = substr($records[2], 7, 10);
echo "<b>Tournament Date: </b>" . $date . "<br />";
// Get star time
$start_time = substr($records[4], 18, 7);
echo "<b>Tournament Start Time: </b>" . $start_time . "<br />";
now i need to work on everything from $records[20] down
what i need to do is simple enough i just do not know how to get to the correct part of my array first
I used to ask my users to post only the information from the START POINTS line down to STOP POINTS so to get my information out and split was simple i used:
foreach( $records as $record ) {
$lastSpace = strrpos( $record, ' ' );
$player = trim( substr( $record, 0, $lastSpace ) );
$points = trim( substr( $record, $lastSpace+1 ) );
this code will still work in this case if i can drop the index's 0 - 19 or just split the array into a new array $records1
P.S its this section in the report that is ever changing so to speak this report is from a online tournament hosting tool and each tournament has no set amount of players it can range from 8 upwards
I'm not sure I understand what you need. Perhaps something like this?
The idea is that you search for the indexes in the array which contain the start and end of the points and do a for loop to iterate only over those points.
$start = array_search("START POINTS", $records);
$end = array_search("END POINTS", $records);
$playerArray = [];
for ($i = $start+1;$i < $end;$i++) {
$parts = explode(" ",$records[$i]);
$player = $parts[0];
$points = $parts[1];
$playerArray = [ "player" => $player, "points" => $points ];
}
I managed to work this problem out for myself with thanks to #apokryfos your code still did not do quiet what i wanted so i used your codes and added them into what i have now,
Here is what i came up with:
// New try to split a full report in one go.
$points = $date = $day = $start_time = $host = $number_of_players = $fp_name = $fp_email = $sp_name = $sp_email = "";
// Explode the string at the end of each line,
// array map and remove any special chars then trim white space.
$records = explode( PHP_EOL, $_POST['points'] );
$records = array_map('htmlspecialchars', $records );
$records = array_map ('trim', $records);
// now each line of the NJ's report is in an array, call the array $records[index number]
// use substr( , , ) to find the needed part of the array indexs,
// I.E in the array $records on line 2 is the Date the actual needed information is the date dd/mm/yyyy,
// so we use $date = substr($records[2], 7, 10);
// from this string : Date: 01/18/2017 which is line 2 in records we get the 01/18/2017
// Get Date
$date = substr($records[2], 7, 10);
echo "<b>Tournament Date: </b>" . $date . "<br />";
// Get star time
$start_time = substr($records[4], 18, 7);
echo "<b>Tournament Start Time: </b>" . $start_time . "<br />";
// get Host name
$host = substr($records[7], 7);
echo "<b>Tournament Host: </b>" . $host . "<br />";
// get number of players
$number_of_players = substr($records[8], 20);
echo "<b> Number Of Players: </b>" . $number_of_players . "<br />";
echo "<br />";
// get the first place name and email address
$fplaceName = substr($records[10], 12);
echo "<b>1ST place: </b>" . $fplaceName . "<br />";
$fplaceEmail = substr($records[11], 18);
echo "<b>1ST place Email: </b>" . $fplaceEmail . "<br />";
// Get second place name and email
$splaceName = substr($records[12], 12);
echo "<b>2ND place Email: </b>" . $splaceName . "<br />";
$splaceEmail = substr($records[13], 18);
echo "<b>2ND place Email: </b>" . $splaceEmail . "<br />";
// get third place name and email
$tplaceName = substr($records[14], 12);
echo "<b>3RD place Email: </b>" . $tplaceName . "<br />";
$tplaceEmail = substr($records[15], 18);
$t1placeEmail = "fake#fake.com";
if($tplaceEmail == "") { // if third place email is empty add a generic fake email else continue as normal
$tplaceEmail = $t1placeEmail;
} ;
echo "<b>3RD place Email: </b>" . $tplaceEmail . "<br />";
echo "<hr /><br /><br />";
// Getting the players and points.
$parts1 = array_slice($records, 20, -1);
$end = array_pop($parts1);
$records = array_map('htmlspecialchars', $records );
$records = array_map ('trim', $records);
foreach( $parts1 as $record ) {
$lastSpace = strrpos( $record, ' ' );
$player = trim( substr( $record, 0, $lastSpace ) );
$points = trim( substr( $record, $lastSpace+1 ) );
echo $player . " " . " " . " " . $points . "<hr /><br />";
}
so what happens is, some user pastes the report as above in my original post,
and hits submit,
the form posts to my processing.php page,
I explode the whole post "string" into an array $records,
array_map the htmlspecialchars and trim the records,
then there is all the codes to extract the Date, time, host, number of players,
Then we get the 1st, 2nd, 3rd names and email address's,
then we get to sorting the playernames and points,
array_slice from the line after STOP POINTS index[20] also -1 for the STOP POINTS index,
pop the last line from the array "STOP POINTS" gone,
re do the array_map's on the new array,
run a foreach loop on the new array using a lastSpace strpos " " and the new array
then split player name from points....
if i run the finished code on the report given in my original post you get the following output:
Music Cafe Tournament Date: 01/22/2017 Tournament Start Time: 1.00pm
Tournament Host: Waiter () Number Of Players: 15
1ST place: poppop 1ST place Email: bobmitch1170#gmail.com 2ND place
Email: Sarge 2ND place Email: rgarvey5#hotmail.com 3RD place Email:
Litigolfer 3RD place Email: dostrow2008#gmail.com
burnieboy 5
EU_BNL_Chris1 5
EU_IT_VIANG 5
GennaLee 5
happybear 5
MC_Vicky 5
merceaviles 5
MRC_cadet 5
poeticfool 5
UBG_Angel_D_8 5
UBG_sara1smoon 5
Litigolfer 60
PhantomMask 60
Sarge 90
poppop 120
of course with the rule separating each name and points,
which is what i needed and now i can add all my sql statements and bingo.
please if anyone can see an easier or better way to achieve this goal i would love to see your edits and test them out
I am still a novice at PHP scripting.
I have an Array
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
From the associative array, the key are the student's exam no and the values are the student's scores. Then I used the 2 inbult PHP functions array_keys and array_values to separate the exam nos from the scores.
$exam_nos=(array_keys($students));
$marks=(array_values($students));
Then I passed the $marks array through the code below:
$i=0;
$occurrences = array_count_values($marks);
$marks = array_unique($marks);
echo '<table border="1">';
foreach($marks as $grade) {
if($grade == end($marks))$i += $occurrences[$grade]-1;
echo str_repeat('<tr><td>'.$grade.': '.($i+1).'</td></tr>',$occurrences[$grade]);
$i += $occurrences[$grade];
}
echo '</table><br />';
output:
94: 1
94: 1
94: 1
91: 4
89: 5
83: 6
65: 7
41: 8
38: 9
37: 11
37: 11
And this is closer to what I want; to rank the scores such that if a tie is encountered, 1 or more positions are skipped, occurs at the end the position the items at the end are assigned a position equivalent toi the total number of ranked items. However, it would be much helpful if this could be done without separating the Array into 2 ...
Questions:
(1) I am pulling my hair how, from the $student array I could have something like:
Exam No Score Position
1201 94 1
1210 94 1
1203 94 1
1200 91 4
1205 89 5
1209 83 6
1206 65 7
1202 41 8
1207 38 9
1204 37 11
1208 37 11
(2) I would like to be able to pick any student by exam no and be able to echo or print out her position e.g
the student 1207 is number 9.
I think I need to capture the postions in a variable, but how do I capture them? Well I don't know!
Could the experts help me here with a better way to achieve my 2 goals (please see questions 1 and 2)? I will try any suggestion that will help me disolve the 'metal blockage' I have hit.
If you're pulling out the students from a database (mentioned in the comments), you could retrieve them with the desired format directly using SQL.
However, I'm going to assume that that's not an option. You could do as follows:
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
arsort($students);// It orders high to low by value. You could avoid this with a simple ORDER BY clause in SQL.
$result = array();
$pos = $real_pos = 0;
$prev_score = -1;
foreach ($students as $exam_n => $score) {
$real_pos += 1;// Natural position.
$pos = ($prev_score != $score) ? $real_pos : $pos;// If I have same score, I have same position in ranking, otherwise, natural position.
$result[$exam_n] = array(
"score" => $score,
"position" => $pos,
"exam_no" => $exam_n
);
$prev_score = $score;// update last score.
}
$desired = 1207;
print_r($result);
echo "Student " . $result[$desired]["exam_no"] . ", position: " . $result[$desired]["position"] . " and score: ". $result[$desired]["score"];
Hope it helps you.
I would use a custom object to process the students individually and store them in an array.
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
arsort($students); // Sort the array so the higher scores are on top.
$newStudents = array();
$pos = 0;
$count = 0;
$holder = -1; // Assuming no negative scores.
foreach($students as $k=>$v){
$count++; // increment real counter
if($v < $holder || $holder == -1){
$holder = $v;
$pos = $count;
}
$newStudents[] = makeStudent($pos, $v, $k);
// If you want the exam # as the array key.
// $newStudents[$k] = $student;
}
$newStudents = fixLast($newStudents);
// outputs
print_r($newStudents);
foreach($newStudents as $v){
echo "position : " . $v->position . "<br>";
echo "score : " . $v->score . "<br>";
echo "exam : " . $v->exam . "<br>";
}
function makeStudent($pos, $score,$examNo){
$student = new stdClass(); // You could make a custom, but keeping it simple
$student->position = $pos;
$student->score = $score;
$student->exam = $examNo;
return $student;
}
function fixLast($students){
$length = count($students) -1;
$count = 0;
$i = $length;
while($students[$i]->position == $students[--$i]->position){
$count++;
}
for($i = 0; $i <= $count; $i++){
$students[$length - $i]->position = $students[$length - $i]->position + $count;
}
return $students;
}
I am pulling college classes and details out of a MySQL database and sorting them onto a webpage with PHP.
Currently my script is simple. It pulls several fields and organizes them by class title:
Class name: Programming 101
Credit hours: 4
Time: 11:00am - 12:50pm
Days: M T W
Room #: 361
Sometimes we have 3 or 4 of the same class going on, so this can create a rather long page.
I'm trying to simplify the way the page looks.
So instead of having repeated class names:
Class name: Programming 101
Credit hours: 4
Time: 11:00am - 12:50pm
Days: M T W
Room #: 361
Class name: Programming 101
Credit hours: 4
Time: 11:30am - 2:50pm
Days: Th F
Room #: 123
You could see this:
Class name: Programming 101
Credit hours: 4
Time: 11:00am - 12:50pm
Days: M T W
Room #: 361
Time: 11:30am - 2:30pm
Days: Th F
Room #: 123
Class name: Programming 102
Credit hours: 4
Time: 1:00am - 2:30pm
Days: M W
Room #: 231
Time: 2:30am - 4:30pm
Days: T F
Room #: 222
Here is my current script:
$sql = "SELECT crs_title, trm_cde, last_name, first_name, begin_dte, end_dte, crs_cde, begin_tim, end_tim, monday_cde, tuesday_cde, wednesday_cde, thursday_cde, friday_cde, saturday_cde, sunday_cde, bldg_cde, room_cde, udef_5_2_1, crs_capacity, crs_enrollment, section_sts FROM BTC_Web_Schedule_view WHERE yr_cde = 2014 AND trm_cde = 'fa' ORDER BY crs_title, trm_cde";
$rs = odbc_exec($conn,$sql);
while (odbc_fetch_row($rs))
{
$crs_title = odbc_result($rs,"crs_title");
$trm_cde = odbc_result($rs,"trm_cde");
$crs_cde = odbc_result($rs,"crs_cde");
$begin_tim = odbc_result($rs,"begin_tim");
$end_tim = odbc_result($rs,"end_tim");
$begin_time = substr($begin_tim, 11, -7);
$end_time = substr($end_tim, 11, -7);
$begin_dte = odbc_result($rs,"begin_dte");
$end_dte = odbc_result($rs,"end_dte");
$monday_cde = odbc_result($rs,"monday_cde");
$tuesday_cde = odbc_result($rs,"tuesday_cde");
$wednesday_cde = odbc_result($rs,"wednesday_cde");
$thursday_cde = odbc_result($rs,"thursday_cde");
$friday_cde = odbc_result($rs,"friday_cde");
$saturday_cde = odbc_result($rs,"saturday_cde");
$sunday_cde = odbc_result($rs,"sunday_cde");
$first_name = odbc_result($rs,"first_name");
$last_name = odbc_result($rs,"last_name");
$fullname = $first_name.$last_name;
$bldg_cde = odbc_result($rs,"bldg_cde");
$room_cde = odbc_result($rs,"room_cde");
$udef_5_2_1 = odbc_result($rs,"udef_5_2_1");
$crs_capacity = odbc_result($rs,"crs_capacity");
$crs_enrollment = odbc_result($rs,"crs_enrollment");
$seats_left = $crs_capacity - $crs_enrollment;
$section_sts = odbc_result($rs,"section_sts");
echo "<div class='container'><p><h2>$crs_title</h2> | $crs_cde</p></div>";
echo "<div class='panel'><p><strong>Time</strong>: ".date('g:ia', strtotime($begin_time))." - ".date('g:ia', strtotime($end_time))."<br>";
echo "<strong>Start date</strong>: ".date("F jS, Y",strtotime($begin_dte))."<br>";
echo "<strong>End date</strong>: ".date("F jS, Y",strtotime($end_dte))."<br>";
echo "<strong>Days</strong>: $monday_cde $tuesday_cde $wednesday_cde $thursday_cde $friday_cde $saturday_cde $sunday_cde <br>";
echo "<strong>Instructor</strong>: $fullname <br>";
echo "<strong>Building</strong>: $bldg_cde <br>";
echo "<strong>Room</strong>: $room_cde <br>";
echo "<strong>Fee</strong>: $$udef_5_2_1 <br>";
echo "<strong>Seats remaining</strong>: $seats_left <br>";
echo "<strong>Enrollment status</strong>: $section_sts</p></div>";
}
odbc_close($conn);
I appreciate any advice. Thank you.
Normalize your database?
Other then that:
while (odbc_fetch_row($rs))
{
$crs_title = odbc_result($rs,"crs_title");
$first_name => odbc_result($rs,"first_name");
$last_name => odbc_result($rs,"last_name");
$fullname = $first_name.$last_name;
$begin_tim = odbc_result($rs,"begin_tim");
$end_tim = odbc_result($rs,"end_tim");
$crs_capacity = odbc_result($rs,"crs_capacity");
$crs_enrollment = odbc_result($rs,"crs_enrollment");
$courses[$crs_title][] = array(
$trm_cde = odbc_result($rs,"trm_cde")
'crs_cde' => odbc_result($rs,"crs_cde"),
'begin_tim' => $begin_tim,
'begin_time' => substr($begin_tim, 11, -7),
'end_tim' => $end_tim,
'end_time' = substr($end_tim, 11, -7),
'begin_dte' => odbc_result($rs,"begin_dte"),
'end_dte' => odbc_result($rs,"end_dte"),
'monday_cde' => odbc_result($rs,"monday_cde"),
'tuesday_cde' => odbc_result($rs,"tuesday_cde"),
'wednesday_cde' => odbc_result($rs,"wednesday_cde"),
'thursday_cde' => odbc_result($rs,"thursday_cde"),
'friday_cde' => odbc_result($rs,"friday_cde"),
'saturday_cde' => odbc_result($rs,"saturday_cde"),
'sunday_cde' => odbc_result($rs,"sunday_cde"),
'first_name' => $first_name,
'last_name' => $last_name,
'fullname' => $first_name.$last_name,
'bldg_cde' => odbc_result($rs,"bldg_cde"),
'room_cde' => odbc_result($rs,"room_cde"),
'udef_5_2_1' => odbc_result($rs,"udef_5_2_1"),
'crs_capacity' => $crs_capacity,
'crs_enrollment' => $crs_enrollment,
'seats_left' = $crs_capacity - $crs_enrollment,
'section_sts' => odbc_result($rs,"section_sts"),
);
foreach ($courses as $crs_title => $data) {
/* html output */
$count = count($data);
foreach ($data as $subdata) {
//multiple times
}
}
}
A few things to consider:
Change your query to do as much of the sorting as possible before even retrieving the data. For example, you could ORDER BY course title first (then secondarily by other fields like start date).
Build an array from the data such that you can easily output in teh way you want: For example:
$array = array();
while (odbc_fetch_row($rs)) {
// ... get your data (probably use something besides odbc_result so you can get ther whole array at once .. but for this example just assume I am using your same variables.
$array[$crs_title] = array(
// place your other course data here in array
);
}
foreach($array as $course_title => $course_data) {
// present your data
}
I'm pretty much a PHP beginner, although I can copy and paste like no one's business and generally I'm able to pick it apart and figure out what's going on :) This is my first post here, but I've gotten lots of help from other answers, so thanks for all the past and hopefully future help!
Basically what I'm trying to replicate here is an equestrian show jumping event, in which each knockdown of a pole equals four faults, and time faults are given for going over the time allowed. All horses who jump clear, that is, with zero faults, go on to a jump-off round, where they may or may not incur faults.
My current issue is using for and foreach loops to advance two different arrays. The problem I'm having is that if I get one array working, the other stops. One of the arrays needs to be shuffled, the other needs to be sorted in numerical order. Basically it's a randomizer which will take a series of horses and give them placings and then assign a weight random number of faults (it's for a horse game, nerdy, I know). Here is the code I'm working with:
index.php
<form action="classes.php" method="post">
How many classes do you wish to randomize?<br />
<input type="text" name="number"><br />
<input type="submit" /><br />
</form>
classes.php
<form action="action.php" method="post">
<?php
$number = $_POST['number']; //This is the desired value of Looping
echo '<input type="hidden" value="' . $number . '" name="number">';
$i = 1; //First we set the count to be zero
while ($i<=$number) {
echo '
Class: <input type="text" name="class' . $i . '"><br />
<textarea cols="100" rows="20" name="entries' . $i . '"></textarea><br />
<br />';
$i++; //Increase the value of the count by 1
};
?>
<input type="submit" /><br />
</form>
action.php
$number = $_POST['number']; //This is the desired value of Looping
function array_rand_weighted($values) {
$r = mt_rand(1, array_sum($values));
foreach ($values as $item => $weight) {
if ($r <= $weight) return $item;
$r -= $weight;
}
}
for ($i=1; $i<=$number; $i++) {
$class[$i] = $_POST['class'.$i];
//trim off excess whitespace off the whole
$text[$i] = trim($_POST['entries'.$i]);
//explode all separate lines into an array
$textAr[$i] = explode("\n", $text[$i]);
//trim all lines contained in the array.
$textAr[$i] = array_filter($textAr[$i], 'trim');
//shuffle the results
shuffle($textAr[$i]);
//add faults
//loop through the lines
echo '<div id="output">
[b]' . $class[$i] . '[/b]<br />';
foreach($textAr[$i] as $key[$i]=>$line[$i]){
$knockdowns = array( 0 => 20, 1 => 25, 2 => 30, 3 => 10, 4 => 8, 5 => 7); // knockdowns
$knockdowns = array_rand_weighted($knockdowns)*4;
$timefaults = array( 0 => 75, 1 => 10, 2 => 10, 3 => 5); // time faults
$timefaults = array_rand_weighted($timefaults);
$faultsadded = $knockdowns + $timefaults;
$faults[$i] = $faultsadded;
}
asort($faults);
foreach($textAr[$i] as $key[$i]=>$line[$i]){
echo $key + 1,". " . $line . " (" . $faults[$i] . " Faults)<br />";
}
echo '</div>';
}
At present, this code is producing the randomized results I need, but not the faults. When I am able to get it to produce the full set of faults, the list of random horses stops functioning. I have never been able to get the faults to sort in order of value (0-20+). I found another answer using array_combine, and thought maybe to use this, but I need the key (I think- maybe I don't really?).
If you want to see it in action, here is the link: http://www.eecreates.com/randomizer/dhjc%20randomizer/ it's a multi-class randomizer, so on the first page you put the number of classes you want to randomize, then the next page you input the class name and horses entered.
The final product I'm going for would look something like this:
Show Jumping Class
penny (0 Faults | 0 Faults)
sheldon (0 Faults | 4 Faults)
raj (4 Faults)
leonard (5 Faults)
howard (8 Faults)
amy farrah fowler (8 Faults)
bernadette (9 Faults)
I'd also like for it to show a second set of faults for those horses who have zero to begin with, but of course one thing at a time. :) Thank you!
Totally new answer. index.php remains unchanged. I've reworked the code in classes.php to use named arrays. This simplifies the processing in action.php. See below for notes on that. The code here will need to be wrapped in a suitable HTML page layout.
Note that although the code changes are extensive I have minimised the changes to the data structures. This is not necessarily the way I'd handle the data structures if I was starting from scratch.
classes.php
<form action="action.php" method="post">
<?php
$number = $_POST['number']; //This is the desired value of Looping
echo '<input type="hidden" value="' . $number . '" name="number">';
$i = 1; //First we set the count to be zero
while ($i<=$number) {
echo '
Class: <input type="text" name="class[]"><br />
<textarea cols="100" rows="20" name="entries[]"></textarea><br />
<br />';
$i++; //Increase the value of the count by 1
};
?>
<input type="submit" /><br />
</form>
action.php
I've moved the weighted random faults code into its own function. Since the data coming from classes.php is now already in array form I've simplified the processing, and separated it from the output. Once the processing is done and the number of faults generated, a pair of nested loops generates the output by class and entrant.
<?php
function array_rand_weighted($values) {
$r = mt_rand(1, array_sum($values));
foreach ($values as $item => $weight) {
if ($r <= $weight) return $item;
$r -= $weight;
}
}
// generate a random value for faults and return in sorted order, ascending.
function generateFaults($numEntries) {
$faults = array();
while ($numEntries) {
$knockdowns = array( 0 => 20, 1 => 25, 2 => 30, 3 => 10, 4 => 8, 5 => 7); // knockdowns
$knockdowns = array_rand_weighted($knockdowns)*4;
$timefaults = array( 0 => 75, 1 => 10, 2 => 10, 3 => 5); // time faults
$timefaults = array_rand_weighted($timefaults);
$faults[] = $knockdowns + $timefaults;
$numEntries--;
}
// echo nl2br(print_r($faults, true));
sort($faults);
return $faults;
}
$textAr = array();
$faults = array();
// Iterate over the entries array, extracting the names of our entrants.
foreach ( $_POST['entries'] as $entries) {
//explode all separate lines into an array
$tempEntries = explode("\n", $entries);
//shuffle the results
shuffle($tempEntries);
//trim all lines contained in the array and assign to next entry in $textAr
$textAr[] = array_filter($tempEntries, 'trim');
//add faults
$faults[] = generateFaults(count($tempEntries));
}
//loop through the lines
// echo nl2br(print_r($faults, true));
for ($i=0; $i<count($_POST['class']); $i++) {
echo '<div id="output">
<b>' . $_POST['class'][$i] . '</b><br />';
for($j = 0; $j<count($textAr[$i]); $j++) {
echo $j + 1,". " . $textAr[$i][$j] . " (" . $faults[$i][$j] . " Faults)<br />";
}
echo '</div>';
}
?>
Footnote:
Here is the code:
$TopFive = array_slice($counts,0,5);
{
foreach($TopFive as $key => $tops)
{
for($i=0; $i<$tops; $i++)
{
echo "*";
}
$b=0;
for($a=0; $a<5; $a++)
{
$b++;
}
echo "{$b}";
echo "#:{$key} - {$tops} <br/>";
}
}
currently, the output looks this:
*********5#:THE - 9
*****5#:OF - 5
*****5#:TO - 5
***5#:AND - 3
***5#:THEM - 3
but what I really want to have is this:
********* #1: THE - 9
***** #2: OF - 5
***** #3: TO - 5
*** #4: AND - 3
*** #5: THEM - 3
I can't seem to figure out how to arrange the looping. Any ideas? I am very sorry this simple question, I ran out of ideas. I just want the numbers to be from 1-5.
I just want some advice as to how to arrange the looping for the $b so that the counting will be from 1-5, not just 5
If:
$TopFive = array('THE' => 9, 'OF' => 5, 'TO' => 5, 'AND' => 3, 'THEM' => 3);
then:
$number = 1;
foreach ($TopFive as $word => $count)
{
echo str_repeat('*', $count); // Outputs '*' characters
echo " #{$number}: {$word} - {$count}\n";
$number++; // increment your number
}
Here's an example.
Your this line has problem
for($a=0; $a<5; $a++)
$b always increments to 5
To solve this, just initialize $x (or anything) outside foreach loop with 1. Do away with $b and simply echo $x; $x++; at appropriate place.
You can change your code to:
$b=1;
foreach($TopFive as $key => $tops)
{
for($i=0; $i<$tops; $i++)
{
echo "*";
}
echo "#$b:{$key} - {$tops} <br/>";
$b++;
}
but all the inner loops are redundant.
In your code, please change this part:
echo "{$b}";
echo "#:{$key} - {$tops} <br/>";
To:
// echo "{$b}";
echo "#$i:{$key} - {$tops} <br/>";
And use str_repeat('*', $count) instead of a for loop! :)