2 foreach loops with arrays - php

What I am trying to do is get my "$ages" array to display a value once per player in order of the array..
However every time I add the extra for loop with that array, it duplicates each player 20 times, with each array value once. I want 20 players, with the 20 different values of the array one each.
Here's the current output, that is duplicating each player 20 times.
http://freerdarts.com/test.php
Here's the code
<link rel="stylesheet" href="https://freerdarts.com/assets/css/main.css" />
<?php
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML(file_get_contents('https://www.leagueleader.net/sharedreport.php?operatorid=98&code=1166aea7-6e0e-4864-8074-8ebd93311041'));
$doc->strictErrorChecking = false;
$pre = [];
$keys = ['name', 'team', 'ppd', 'games', 'wins', 'hats', '3bd', 'ton80','hton','lton','6do','7do','8do','9do'];
$keys2 = ['name', 'mpr', 'games', 'wins','assists', 'hats','whorse','5mr','6mr','7mr','8mr','9mr'];
foreach ($doc->getElementsByTagName('table') as $k => $table) {
if (strpos($table->getAttribute('class'), 'report') === false) {
continue;
}
foreach ($table->getElementsByTagName('tr') as $i => $tr) {
if ($tr->parentNode->nodeName === 'thead') continue; // skip headers
$row_values = [];
foreach ($tr->childNodes as $td) {
$text = trim($td->nodeValue);
if ($text === '') continue;
$row_values[] = $text;
}
if($k == 1 ){
$row_values = array_combine($keys, $row_values);
}else if($k == 2 ){
unset($row_values[1]);
$row_values = array_combine($keys2, $row_values);
}
$pre[$row_values['name']][] = $row_values;
}
}
$combined = [];
foreach($pre as $name => $row){
$combined[$name] = [
"name"=> $name,
"team"=> $row[0]['team'],
"ppd_01" => $row[0]['ppd'],
"games_01" => $row[0]['games'],
"wins_01" => $row[0]['wins'],
"hats_01" => $row[0]['hats'],
"3bd" => $row[0]['3bd'],
"ton80" => $row[0]['ton80'],
"hton" => $row[0]['hton'],
"lton" => $row[0]['lton'],
"6do" => $row[0]['6do'],
"7do" => $row[0]['7do'],
"8do" => $row[0]['8do'],
"9do" => $row[0]['9do'],
"mpr_crk" => $row[1]['mpr'],
"games_crk" => $row[1]['games'],
"wins_crk" => $row[1]['wins'],
"assists_crk" => $row[1]['assists'],
"hats_crk" => $row[1]['hats'],
"whorse" => $row[1]['whorse'],
"5mr" => $row[1]['5mr'],
"6mr" => $row[1]['6mr'],
"7mr" => $row[1]['7mr'],
"8mr" => $row[1]['8mr'],
"9mr" => $row[1]['9mr']
];
}
//echo '<pre>'.json_encode($combined, JSON_PRETTY_PRINT).'</pre>';
//echo $combined['Ronnie Otto']['ppd_01'];
?>
<?php
// Players Ages
$ages = array(one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen,twenty);
sort($combined);
foreach($combined as $row) {
foreach($ages as $age) {
// Name Corrections
if($row['name'] == 'Scott Sandburg'){ $row['name'] = 'Scott Sandberg'; }
// Profile Images
$profile_image = $row['name'];
$profile_image = str_replace(' ', '_', $profile_image);
?>
<div class="profile">
<span class="profile_ext"> <img src="../images/profiles/<?=$profile_image?>.jpg" />
<span class="name"><?=$row['name'];?></span>
<span class="age"><?=$age;?></span>
<span class="gamesplayed"><?=$row['games_01']+$row['games_crk'];?></span>
<span class="blank"></span>
<!-- Cricket Stats -->
<span class="cricketmpr"><?=$row['mpr_crk'];?></span>
<span class="cricket_5mr"><?=$row['5mr'];?></span>
<span class="cricket_6mr"><?=$row['6mr'];?></span>
<span class="cricket_7mr"><?=$row['7mr'];?></span>
<span class="cricket_8mr"><?=$row['8mr'];?></span>
<span class="cricket_9mr"><?=$row['9mr'];?></span>
<span class="cricket_whorse"><?=$row['whorse'];?></span>
<span class="blank"></span>
<!-- 01 Stats -->
<span class="o1ppd"><?=$row['ppd_01'];?></span>
<span class="o1_lton"><?=$row['lton'];?></span>
<span class="o1_hats"><?=$row['hats_01'];?></span>
<span class="o1_hton"><?=$row['hton'];?></span>
</span>
</div>
<?php } } ?>

The problem is that you have an inner foreach() in your display part...
foreach($ages as $age) {
this doesn't seem in any way connected to the data you are displaying as there doesn't seem to be an age in the data.
You should also be getting some warnings as
$ages = array(one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen,twenty);
will show
Warning: Use of undefined constant one - assumed 'one' (this will
throw an Error in a future version of PHP)
You should have quotes round the values...
$ages = array('one',...
If you just want the players listed with the corresponding age, you first need to change
foreach($combined as $key => $row) {
// Remove next foreach
// foreach($ages as $age) {
Then to display the age, use the $key from the foreach() to index the $ages array...
<span class="age"><?=$ages[$key];?></span>

Related

Dumping tables into json from external page

I have this partially working. I need to grab the data of each player, and present a variable for each "cricket" and "x01" games. I am able to grab the data from the top table, however the 2nd one is not showing any data in my code. I am probably missing something simple, but I can't figure it out.
I want the output to show like this. The part under the line break is what I am missing.
"Howard Hill": {
"name": "Howard Hill",
"team": "Team 2",
"ppd_01": "34.54",
"games_01": "153",
"wins_01": "999",
"assists_01": "69",
"sspre_01": "7.876",
"mpr_crk": "9.99",
"games_crk": "999",
"wins_crk": "999",
"assists_crk": "99",
"sspre_crk": "9.999"
}
Here is my code
<?php
ini_set('default_socket_timeout', 180); // 900 Seconds = 15 Minutes
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML(file_get_contents('http://freerdarts.com/past_stats/tues-2018-player-standings.html'));
$doc->strictErrorChecking = false;
$pre = [];
foreach ($doc->getElementsByTagName('table') as $table) {
foreach ($table->getElementsByTagName('tr') as $i => $tr) {
$y = 0;
foreach ($tr->childNodes as $td) {
$text = trim($td->nodeValue);
if ($y > 7) {
unset($pre[$i]);
continue;
}
if (empty($text)) {
continue;
}
$pre[$i][] = $text;
$y++;
}
}
}
// normalise
$pstats = [];
foreach ($pre as $row) {
$pstats[$row[0]] = [
'name' => $row[0],
'team' => $row[1],
'ppd_01' => $row[2],
'games_01' => $row[3],
'wins_01' => $row[4],
'sspre_01' => $row[5],
];
}
echo '<pre>'.json_encode($pstats, JSON_PRETTY_PRINT).'</pre>';
//echo $pstats['Scott Sandberg']['01'];
?>
One problem you're facing is that you're not getting the proper table that needs parsing.
Take note there are multiple tables inside that page.
You need to point out inside the loop that you're skipping other tables in the HTML page and only choose to process the score report table, nothing else:
if (strpos($table->getAttribute('class'), 'report') === false) {
continue;
}
So after getting other tables out of the way, you can start processing the data inside the specific table results that you want to store.
Another thing to point out is you need to skip the headers inside the table. You don't need to anyways.
if ($tr->parentNode->nodeName === 'thead') continue; // skip headers
After that, its just a matter of looping on each <td>.
One gotcha on the tables is that one table has six 6 columns. Another one has 7 so first gather all <td> values. After gathering just unset it from the gathered data so that you have a uniform column layout structure. (I assume you're trying to skip out assists)
Here's the full code:
$pre = []; // initialize container
$keys = ['name', 'team', 'ppd', 'games', 'wins', 'sspre']; // keys needed to be used in the json
foreach ($doc->getElementsByTagName('table') as $table) { // loop all found tables
if (strpos($table->getAttribute('class'), 'report') === false) {
continue; // if its not the report table, skip
}
foreach ($table->getElementsByTagName('tr') as $i => $tr) { // loop each row of report table
if ($tr->parentNode->nodeName === 'thead') continue; // skip headers
$row_values = []; // initialize container for each row
foreach ($tr->childNodes as $td) { // loop each cell
$text = trim($td->nodeValue); //
if ($text === '') continue;
$row_values[] = $text;
}
// unset assist if this table has 7 columns
if (count($row_values) === 7) unset($row_values[5]);
$row_values = array_combine($keys, $row_values); // combine the keys and values
$pre[$row_values['name']] = $row_values; // push them inside
}
}
// finally encode in the end
echo json_encode($pre);
Here's the sample output
I have modified #Ghost code. Try below code.
<?php
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML(file_get_contents('http://freerdarts.com/past_stats/tues-2018-player-standings.html'));
$doc->strictErrorChecking = false;
$pre = [];
$keys = ['name', 'team', 'ppd', 'games', 'wins', 'sspre'];
$keys2 = ['name', 'mpr', 'games', 'wins','assists', 'sspre'];
foreach ($doc->getElementsByTagName('table') as $k => $table) {
if (strpos($table->getAttribute('class'), 'report') === false) {
continue;
}
foreach ($table->getElementsByTagName('tr') as $i => $tr) {
if ($tr->parentNode->nodeName === 'thead') continue; // skip headers
$row_values = [];
foreach ($tr->childNodes as $td) {
$text = trim($td->nodeValue);
if ($text === '') continue;
$row_values[] = $text;
}
if($k == 1 ){
$row_values = array_combine($keys, $row_values);
}elseif($k == 2 ){
unset($row_values[1]);
$row_values = array_combine($keys2, $row_values);
}
$pre[$row_values['name']][] = $row_values;
}
}
$new_arr = [];
foreach($pre as $name => $row){
$new_arr[$name] = [
"name"=> $name,
"team"=> $row[0]['team'],
"ppd_01" => $row[0]['ppd'],
"games_01" => $row[0]['games'],
"wins_01" => $row[0]['wins'],
"sspre_01" => $row[0]['sspre'],
"mpr_crk" => $row[1]['mpr'],
"games_crk" => $row[1]['games'],
"wins_crk" => $row[1]['wins'],
"assists_crk" => $row[1]['assists'],
"sspre_crk" => $row[1]['sspre']
];
}
echo '<pre>'.json_encode($new_arr, JSON_PRETTY_PRINT).'</pre>';
Here is sample output
https://www.tehplayground.com/Du5rId3iRx3NH6UL
It seems to me that you want to combine the x01 table values with the crk table values under the same name. Here is the code that I think you are looking for with an example.
$x01 = [];
$crk = [];
$keys_01 = ['name', 'team', 'ppd_01', 'games_01', 'wins_01', 'sspre_01'];
$keys_crk = ['name', 'team', 'mpr_crk', 'games_crk', 'wins_crk', 'assists_crk', 'sspre_crk'];
$table_num = 1;
foreach ($doc->getElementsByTagName('table') as $table) {
if (strpos($table->getAttribute('class'), 'report') === false) {
continue;
}
foreach ($table->getElementsByTagName('tr') as $i => $tr) {
if ($tr->parentNode->nodeName === 'thead') continue; // skip headers
$row_values = [];
foreach ($tr->childNodes as $td) {
$text = trim($td->nodeValue);
if ($text === '') continue;
$row_values[] = $text;
}
// build x01 array
if ($table_num === 1) {
$row_values = array_combine($keys_01, $row_values);
$x01[$row_values['name']] = $row_values;
// build crk array
} else {
$row_values = array_combine($keys_crk, $row_values);
$crk[$row_values['name']] = $row_values;
}
}
$table_num++;
}
$combined = array_merge_recursive($x01, $crk);
// after arrays are merged, remove duplicate values
foreach ($combined as $name => $value) {
if ($value['name']) {
$combined[$name]['name'] = $name;
}
if ($value['team']) {
$combined[$name]['team'] = $value['team'][0];
}
}
echo json_encode($combined, JSON_PRETTY_PRINT);

php : csv import to multiple table is taking too long

i am using csv importer library to load bulk of products into mysql database with codeigniter. csv file has some column names from one table like name, price and some column names from another table like categories. it inserts name and price to first table and categories will be inserted after we got id from first table, so category will go with id of the product. but this is taking too long to process the csv file (1000 entries = 2 minutes). the code i have put is
$csv_file_data = $this->csvimport->get_array($_FILES['csv']['tmp_name']);
foreach ($csv_file_data as $csv_data) {
$required = array(
'name' => $csv_data['name'],
'price' => $csv_data['price']
);
foreach ($required as $key => $value) {
$this->db->set($key, $value);
}
$categories= array();
foreach ($csv_data as $key => $value) {
if(strstr($key, 'categories')) {
$category = explode('|', $value);
for($i = 0; $i < sizeof($category); $i++) {
array_push($categories, $category[$i]);
}
}
}
$this->db->insert('first_table');
$id = $this->db->insert_id();
foreach ($categories as $metatag) {
$this->db->set('productid', $id);
$this->db->set('category', $metatag);
$this->db->insert('second_table');
}
}
but it is taking too long to process only 1000 entries, how to optimize this to process 5000 entries in less than 20-30 seconds ?
I believe this is what you're after ...
$csv_file_data = $this->csvimport->get_array($_FILES['csv']['tmp_name']);
foreach ($csv_file_data as $csv_data) {
$required = array(
'name' => $csv_data['name'],
'price' => $csv_data['price']
);
$this->db->set($required);
$categories = array();
foreach ($csv_data as $key => $value) {
if (strstr($key, 'categories')) {
$category = explode('|', $value);
for ($i = 0; $i < sizeof($category); $i++) {
$categories[] = $category[$i];
}
}
}
$this->db->insert('first_table');
$bulk = array();
$id = $this->db->insert_id();
foreach ($categories as $metatag) {
$bulk[] = array(
'category' => $metatag,
'productid' => $id,
);
}
$this->db->insert_batch('table2', $bulk);
}
Note the use of insert_batch which will dramatically improve your speed and also you can give set() an array.
Can you try a version like this and tell me if this is working and return me the time for 100 rows ?
$bulk = [];
$csv_file_data = $this->csvimport->get_array($_FILES['csv']['tmp_name']);
$required = [];
foreach ($csv_file_data as $csv_data) {
$bulk = [];
$id = $this->db->insert_id();
$required[] = [
'name' => $csv_data['name'],
'price' => $csv_data['price'],
];
foreach ($csv_data as $key => $value) {
if (false !== strpos($key, 'categories')) {
$category = explode('|', $value);
foreach ($category as $metatag) {
$bulk[] = [
'category' => $metatag,
'productid' => $id,
];
}
}
}
}
$this->db->insert_batch('first_table', $required);
$this->db->insert_batch('second_table', $bulk);

Looping into a multidimensional array with PHP

I've got this array:
Array
(
[0] => Array
(
[name] => System
[order] => 1
[icon] => stats.svg
[0] => Array
(
[title] => Multilingual
)
[1] => Array
(
[title] => Coloring
)
[2] => Array
(
[title] => Team work
)
[3] => Array
(
[title] => Tutorials
)
)
)
I want to loop into this to show the section name and after all the features containing in the following array.
So, this is what I made:
foreach ($features as $feature => $info) {
echo '
'.$info['name'].'
<ul class="menu-vertical bullets">
';
foreach (array_values($info) as $i => $key) {
echo '
<li>'.$key['title'].'</li>
';
}
echo '
</ul>
';
}
It works except for the first third <li> where I have the first char of name, order and icon value.
Do you know why ?
Thanks.
array_values return value of array so for info values is name, order, icon, 0, 1, ...
Your values foreach is wrong if you just want print title you can use:
foreach ($features as $feature => $info) {
echo '
'.$info['name'].'
<ul class="menu-vertical bullets">
';
//Remove some keys from info array
$removeKeys = array('name', 'order', 'icon');
$arr = $info;
foreach($removeKeys as $key) {
unset($arr[$key]);
}
foreach (array_values($arr) as $i => $key) {
echo '
<li>'.$key['title'].'</li>
';
}
echo '
</ul>
';
}
In php, array_values means all the values of the array. So array_values($info) is array($info['name'], $info['order'], $info['icon'], $info[0], $info[1], $info[2], $info[3])
in your example, you can skip the non-integer keys of the $info to get your titles:
<?php
$features = array();
$info = array();
$info['name'] = 'System';
$info['order'] = 1;
$info['icon'] = 'stats.svg';
$info[] = array('title'=>'Multilingual');
$info[] = array('title'=>'Coloring');
$features[] = $info;
foreach ($features as $feature => $info) {
echo $info['name'] . PHP_EOL;
echo '<ul class="menu-vertical bullets">' . PHP_EOL;
foreach ($info as $k => $item) {
if(!is_int($k)) continue;
echo '<li>' . $item['title'] . '</li>' . PHP_EOL;
}
echo '</ul>' . PHP_EOL;
}
BUT, your original data structure is not well designed and hard to use. For a better design, you can consider the following code, move your items to a sub array of $info:
<?php
$features = array();
$info = array();
$info['name'] = 'System';
$info['order'] = 1;
$info['icon'] = 'stats.svg';
$info['items'] = array();
$info['items'][] = array('title'=>'Multilingual');
$info['items'][] = array('title'=>'Coloring');
$features[] = $info;
foreach ($features as $feature => $info) {
echo $info['name'] . PHP_EOL;
echo '<ul class="menu-vertical bullets">' . PHP_EOL;
foreach ($info['items'] as $item) {
echo '<li>' . $item['title'] . '</li>' . PHP_EOL;
}
echo '</ul>' . PHP_EOL;
}
Sample output of the two demos:
System
<ul class="menu-vertical bullets">
<li>Multilingual</li>
<li>Coloring</li>
</ul>
It works except for the first third li where I have the first char of name, order and icon value. Do you know why ?
Why you see first chars of the values of 'name', 'order', 'icon'? Let see how PHP works.
Take the first loop as an example: foreach (array_values($info) as $i => $key)
Then $i == 0, $key == 'System'
We know that $key[0] == 'S', $key[1] == 'y', $key[2] == 's', etc.
Then you try to access $key['title'], but the string 'title' is not valid as a string offset, so it is converted to an integer by PHP: intval('title') == 0.
Then $key['title'] == $key[intval('title')] == 'S'
That's what you see.
array_value() returns the values of the array, here you will get the value of the array $info and what I understand is that is not what you need. See details for array_value().
You can check if the key for the $info is an integer. if yes, echo the title. Give this a try.
foreach ($features as $feature => $info) {
echo $info['name'].'<ul class="menu-vertical bullets">';
foreach ($info as $key => $value) {
if (is_int($key)) {
echo '<li>'.$key['title'].'</li>';
}
}
echo '</ul>';
}

PHP How to count same words in foreach loop

I have this result inside my foreach loop:
Tonsilitis
Tonsilitis
Laryngitis
Rhinosinusitis Akut
Rhinosinusitis Akut
Rhinosinusitis Akut
Common Cold
Common Cold
Common Cold
Rhinitis Alergi
This is my script:
foreach ($results as $data) :
$final = $data->nama_diagnosis . '<br>';
echo $final;
endforeach;
My question is, how can i count the same word in my loop or outside the loop. Can i do that? give me the solution please. As a result i want to count them like this:
Tonsilitis = 2
Laryngitis = 1
Rhinosinusitis Akut = 3
Common Cold = 3
Rhinitis Alergi = 1
Or maybe i can filter the same word so i get only the most words, like Rhinosinusitis Akut and Common Cold. Please help me.
Thanks
You can try something like this, iterating through array with foreach loop and using a ternary operator with isset to safely assign and increment each occurrence:
$count = array();
foreach ($results as $result)
isset($count[$data]) ? $count[$data]++ : $count[$data] = 1;
Example
In foreach loop save words and their count into array, then make another loop and write the amounts.
<?php
$results = array(
array('nama_diagnosis' => 'Tonsilitis'),
array('nama_diagnosis' => 'Tonsilitis'),
array('nama_diagnosis' => 'Laryngitis'),
array('nama_diagnosis' => 'Rhinosinusitis Akut'),
array('nama_diagnosis' => 'Rhinosinusitis Akut'),
array('nama_diagnosis' => 'Rhinosinusitis Akut'),
array('nama_diagnosis' => 'Common Cold'),
array('nama_diagnosis' => 'Common Cold'),
array('nama_diagnosis' => 'Common Cold'),
array('nama_diagnosis' => 'Rhinitis Alergi')
);
$res = array();
foreach ($results as $words) { // changed $word to $words
foreach ($words as $word) { // this foreach added
if (isset($res[$word])) {
$res[$word] += 1;
} else {
$res[$word] = 1;
}
} // end of nested foreach which was added
}
foreach ($res as $word => $count) {
echo $word . ' (' . $count . ')<br>';
}
/*
output:
Tonsilitis (2)
Laryngitis (1)
Rhinosinusitis Akut (3)
Common Cold (3)
Rhinitis Alergi (1)
*/
?>
Try with -
$counts = array();
foreach ($results as $data) :
$final = $data->nama_diagnosis . '<br>';
if (array_key_exists($data->nama_diagnosis, $counts)) {
$counts[$data->nama_diagnosis] += 1;
} else {
$count[$data->nama_diagnosis] = 1;
}
endforeach;
foreach ($counts as $key => $val) {
echo $key.' = '.$val;
}
This should work for you:
<?php
$results = array("Tonsilitis", "Tonsilitis", "Laryngitis", "Rhinosinusitis Akut", "Rhinosinusitis Akut", "Rhinosinusitis Akut", "Common Cold", "Common Cold", "Common Cold", "Rhinitis Alergi");
$counter = array();
foreach ($results as $data):
if(in_array($data->nama_diagnosis, array_flip($counter)))
$counter[$data->nama_diagnosis]++;
else
$counter[$data->nama_diagnosis] = 1;
endforeach;
foreach ($counter as $key => $data)
echo $key . " = " . $data . "<br />";
?>
Output:
Tonsilitis = 2
Laryngitis = 1
Rhinosinusitis Akut = 3
Common Cold = 3
Rhinitis Alergi = 1

PHP array_sum multi-dimensional

I need to get the array_sum of $product['stars']; so I can use it to find the average. When I try to use it I am not using it on the array somehow? Also I believe this comes in as a string. Does it need to be converted to INT? Thanks so much for any ideas.
<?php
$categories = array();
while ($row_rsCategories = mysqli_fetch_assoc($res)) {
$product_array = array();
$product_array_query = mysqli_query($mysqli,"SELECT id, user_id, client_id, comments, stars FROM reviews WHERE user_id = '".$row_rsCategories['userId']."'");
while($product_array_fetch = mysqli_fetch_array($product_array_query)) {
$product_array[] = array("id"=>$product_array_fetch['user_id'],"comments"=>$product_array_fetch['comments'],"stars"=>$product_array_fetch['stars']);
}
$categories[] = array(
'id' => $row_rsCategories['userId'],
'name' => $row_rsCategories['usersName'],
'products' => $product_array,
);
}
foreach ($categories as $category) {
?>
<div class="col-md-4">
<div id = "user-square">
<div class="avatar">
<img src="<?php echo $avatarDir.$list['usersAvatar']; ?>" class="publicAvatar" />
</div>
<?php
echo $category['name']; ?> </br>
<?php foreach($category['products'] as $product) {
echo $product['stars']; ?> </br>
<?php }
?>
You could do it by combining array_sum with array_map:
$starsum = array_sum(array_map(function($x) { return $x['stars']; }, $product_array));
But you can also just calculate the sum while you're constructing the array of results:
$starsum = 0;
$rowcount = 0;
while($product_array_fetch = mysqli_fetch_array($product_array_query)) {
$product_array[] = array("id"=>$product_array_fetch['user_id'],"comments"=>$product_array_fetch['comments'],"stars"=>$product_array_fetch['stars']);
$starsum += $product_array_fetch['stars'];
$rowcount++;
}
$categories[] = array(
'id' => $row_rsCategories['userId'],
'name' => $row_rsCategories['usersName'],
'products' => $product_array,
'avgstars' => ($rowcount == 0) ? 0 : $starsum / $rowcount
);
There's no need to convert the values to integers, PHP will do that automatically when you use arithmetic functions on them.

Categories