I recently asked a question about how to parse this JSON feed. An answer was given but it also presented another problem. The echo is spitting out duplicate records for each player in the feed. I'm not sure why this is happening and I hope someone can help me out.
Here's my code:
$url = file_get_contents("http://www.nfl.com/liveupdate/game-center/2015091700/2015091700_gtd.json");
$json = json_decode($url, true); // 'true' makes data an array
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($json));
$player = array();
foreach($iterator as $key=>$value) {
$player[$key] = $value;
echo $player['name'] . ' ' . $player['att'] . ' ' . $player['cmp'] . ' ' . $player['yds'] . ' ' . $player['tds'] . ' ' . $player['fgm'] . ' ' . $player['fga'] . '<br>';
}
This is the JSON:
{
"2015091700":{
"home":{
"abbr":"KC",
"to":0,
"stats":{
"passing":{
"00-0023436":{
"name":"A.Smith",
"att":25,
"cmp":16,
"yds":191,
"tds":0,
"ints":2
}
},
"rushing":{
"00-0026213":{
"name":"J.Charles",
"att":21,
"yds":125,
"tds":1
}
}
}
}
}
}
It is giving me duplicates. See below.
A.Smith
A.Smith 25
A.Smith 25 16
A.Smith 25 16 191
A.Smith 25 16 191 0
A.Smith 25 16 191 0
A.Smith 25 16 191 0
A.Smith 25 16 191 0
J.Charles 25 16 191 0
J.Charles 21 16 191 0
J.Charles 21 16 125 0
J.Charles 21 16 125 1
J.Charles 21 16 125 1
J.Charles 21 16 125 1
J.Charles 21 16 125 1
J.Charles 21 16 125 1
I would like unique results for each player.
A.Smith should be A.Smith 25 16 191 0 2 and J.Charles should be J.Charles 21 125 1 instead of what you see above.
The code is not actually creating duplicates, you just print out every intermittent result. In fact, the loop is overwriting data for existing keys in every iteration. This would work if you only have data for one player, but can lead to unexpected (wrong) results in this case with multiple players.
A quick and somewhat dirty solution would be to save and reset when a new player is started. Here we assume the 'name' key is always present and always the first entry.
$url = file_get_contents("http://www.nfl.com/liveupdate/game-center/2015091700/2015091700_gtd.json");
$json = json_decode($url, true); // 'true' makes data an array
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($json));
$players = array();
$player = false;
foreach ( $iterator as $key => $value ) {
// The key 'name' marks the start of a new player
if ( $key == 'name' ) {
// If we were already processing a player, save it
if ( is_array($player) ) {
$players[] = $player;
}
// Start a new player
$player = array();
}
// If we are processing a player, save the values
if ( is_array($player) ) {
$player[$key] = $value;
}
}
// Don't forget the last player
$players[] = $player;
// Output the resulting players
print_r($players);
// Or, in your desired output format
// Will give a ton of E_NOTICES on dev setups!
foreach( $players as $player ) {
echo $player['name'] . ' ' . $player['att'] . ' ' . $player['cmp'] . ' ' . $player['yds'] . ' ' . $player['tds'] . ' ' . $player['fgm'] . ' ' . $player['fga'] . '<br>';
}
It would be cleaner to actually get the data from the parsed array directly, but this requires a well defined and known format for the json.
Related
Consider I have a following array of total scores, with each value being a score of a player in a tournament.
$total_scores = array(350,200,150,150,75,75,75,0);
I need to create a table, which lists the players with correct positions, if they have the same score, the listing should reflect this, ie.:
1. Player 1 350
2. Player 2 200
3.-4. Player 3 150
3.-4. Player 4 150
5.-7. Player 5 75
5.-7. Player 6 75
5.-7. Player 7 75
8. Player 8 0
I tried to do something with
foreach ($total_scores as $total_score) {
$no_of_occurrences = array_count_values($total_scores)[$total_score];
}
But cannot figure out how to build the correct positions numbering.
<?php
$scores = array(350,200,150,150,75,75,75,0); //assuming you have sorted data otherwise you need to sort it first
$count = array();
$startIndex = array();
$endIndex = array();
$len = count($scores);
for($i = 0; $i < $len; $i++){
if(!isset($count[$scores[$i]])){
$count[$scores[$i]] = 1;
$startIndex[$scores[$i]] = $endIndex[$scores[$i]] = $i+1;
}else{
$count[$scores[$i]]++;
$endIndex[$scores[$i]] = $i+1;
}
}
$i = 1;
foreach($scores as $s){
echo $startIndex[$s].'.';
if($startIndex[$s] != $endIndex[$s]){
echo '-'.$endIndex[$s].'.';
}
echo ' Player '.$i.' '.$s."\n"; //if newline not works try echoing <br>
$i++;
}
Working Demo
For this Array needs to be sorted in descending order
$total_scores = array(350, 200, 150, 150, 75, 75, 75, 0);
rsort($total_scores);
$no_of_occurrence = array_count_values($total_scores);
array_unshift($total_scores, ""); // For starting count from 1
unset($total_scores[0]); // For starting count from 1
$i = 1;
foreach ($total_scores as $key => $value)
{
$position = array_keys($total_scores,$value);
if($no_of_occurrence[$value] == 1)
{
echo "Position " . $i . " ";
echo "Player " . $i . " " . $value . " ";
}
else
{
echo "Position " . $position[0] . " - " . end($position) . " ";
echo "Player " . $i . " " . $value . " ";
}
$i++;
echo "<br>";
}
Output of above code :
Position 1 Player 1 350
Position 2 Player 2 200
Position 3 - 4 Player 3 150
Position 3 - 4 Player 4 150
Position 5 - 7 Player 5 75
Position 5 - 7 Player 6 75
Position 5 - 7 Player 7 75
Position 8 Player 8 0
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;
}
<?php
function apache($b) {
return $b;
}
$a = array(1, 2, 3, 4, 5, 6);
$num = "";
foreach ($a as $b) {
$num = apache($b) . $num ;
}
echo $num;
?>
When you write it like this the output is 654321, but if you write it like this:
$num = $num . apache($b);
the output would be 123456. I don't understand why the results are like that. Can someone explain this?
This isn't really hard to understand.
This line:
$num = apache($b) . $num;
add the currently selected number and appends the current value of $num to it. The result will be written to $num.
So this will happen:
$b. $num = $num
1 . "" = 1
2 . 1 = 21
3 . 21 = 321
4 . 321 = 4321
5 . 4321 = 54321
6 . 54321 = 654321
If you write
$num = $num . apache($b);
instead, you're adding the currently selected number behind $num:
$num .$b = $num
"" . 1 = 1
1 . 2 = 12
12 . 3 = 123
123 . 4 = 1234
1234 . 5 = 12345
12345 . 6 = 123456
one way you are appending to the string
the other way you are prepending to the string
which gives the effect of reversing it.
first way kind of looks like this
[1]
2[1]
3[21]
4[321]
5[4321]
6[54321]
the other way looks like this
[1]
[1]2
[12]3
[123]4
[1234]5
[12345]6
where the value outside the [] is the value being returned by your function and the value inside the [] is $num
I need to convert an array of numbers and totals into a simple statement.
For example, how can I convert the following, programmatically via PHP, to simple statements like, 1 out of 10, 1 out of 100, and even rounding some (like 2 out of 100 for 9000,400000).
Generate Sample Array:
$arr = array();
for ($i=0; $i < 100; $i++) {
$n = mt_rand(1,1000);
$t = mt_rand(10,100000);
if ($n > $t) continue; // skip!
$arr[] = array($n,$t);
}
/*
// Generates pairs like:
// Array
// (
// [0] => Array ( [0] => 55 [1] => 8774 )
// [1] => Array ( [0] => 814 [1] => 11174 )
// [2] => Array ( [0] => 255 [1] => 32168 )
// ...
// [99] => Array ( [0] => 851 [1] => 24231 )
// )
*/
Run through a function and print simplified results:
foreach ($arr as $a) {
echo $a[0] . '/' . $a[1] . ' ==> ' . simplifyRatio($a[0],$a[1]) . "\r\n";
}
Can you point me in the right direction on how to accomplish this?
Here's the start of a function I'm working on, but the solution is escaping me.
function simplifyRatio($n,$t) {
$p = $n/$t;
if ($p > 0.09) return round($n) . ' out of ' . round($t);
if ($p > 0.009) return round($n) . ' out of ' . round($t);
}
Ideally the denominator should be: 1,2,3...10,20,30...100,200,300...1000,2000,3000...10000,20000,30000...100000 (max)
Assuming always a percentage. You may also want to sprintf $outof before displaying it
function convertPercent($iPercent)
{
// Assume validation on $iPercent
$outof = round(100 / $iPercent);
return "1 out of $outof";
}
For simplicity's sake, I'll assume that you can get your percent in a fraction (ie 25% is 25/100, 0.7% = 7/1000, etc).
You can use Euclid's algorithm to find the GCD of the numerator and the denominator:
http://en.wikipedia.org/wiki/Euclidean_algorithm
In php it'd look something like this:
function gcd ($int1, $int2) {
$tmp = 0;
while ($int1 > 0) {
$tmp = $int1;
$int1 = $int2 % $int1;
$int2 = $tmp;
}
return $int2;
}
This will work as long as $int1 and $int2 are integers greater than 0 (you might want to put in some logic to ensure this). If you need negative numbers, just take the absolute value.
Knowing the GCD, it's easy to figure out the rest:
function reduce($numerator, $denominator) {
$gcd = gcd($numerator, $denominator);
echo ($numerator/$gcd) . " out of " . ($denominator/$gcd);
}
echo reduce(4, 8).'<br>'; // 1 out of 2
echo reduce(38, 897).'<br>'; // 38 out of 897
echo reduce(39, 26).'<br>'; // 3 out of 2
Hope this helps!
I ended up settling for a near match on the pattern 1 out of ___, like so:
function simplifyRatio($n,$t) {
$r = $t/$n;
return '1 out of ' . round($r);
}
// Examples:
$arr = array();
for ($i=0; $i < 100; $i++) {
$n = mt_rand(1,1000);
$t = mt_rand(10,100000);
if ($n > $t) continue; // skip!
$arr[] = array($n,$t);
}
foreach ($arr as $a) {
echo $a[0] . '/' . $a[1] . ' ==> ' . simplifyRatio($a[0],$a[1]) . "\r\n";
}
Example Result:
1000/24819 ==> 1 out of 25
309/50305 ==> 1 out of 163
488/99123 ==> 1 out of 203
322/47610 ==> 1 out of 148
183/54287 ==> 1 out of 297
752/67646 ==> 1 out of 90
240/68854 ==> 1 out of 287
301/81345 ==> 1 out of 270
611/16404 ==> 1 out of 27
522/62992 ==> 1 out of 121
CodePad: http://codepad.org/wu6iOdDq
Initially I had hoped to end up with rounded denominators (10,20...100,200...1000,2000, etc.), but I'm uncertain how to do this well. I'll happily award an answer that cleans up the denominators of the above.
I have a HTML table with more than 1000 rows. Now i want to show these records in parallel manner.
Like 30 rows in left side and 30 in right side
1 xyz 120 00:10:01 31 xyz 120 00:10:01
1 xyz 120 00:10:01 32 mxy 20 00:10:01
2 mxy 20 00:10:01 . . . ........
. . . ........ . . . ........
. . . ........ . . . .........
. . . ........ . . . .........
30 mld 2 00:05:01 60 mld 2 00:05:01
I am going to generate PDF so i want to show 60 records per page. 30 left and 30 right.
It will probably be easiest to display two tables side by side (set each to a width of about half the page and float one to the left or right).
Then your loop can be simple:
$i = -1;
$totalRows = count($rows);
$halfRows = round($numRows / 2);
//construct $headerRow HTML here
foreach ($rows as $row) {
$i++;
if ($i == 0 || $i == $halfRows) {
echo '<table class="'. ($i==0 ? 'floatLeft': '').'">';
echo $headerRow;
}
//Code to output column values here
if ($i == ($halfRows - 1)) {
echo '</table>';
}
}
echo '</table>';