Count columns in a HTML table with PHP - php

I have a basic string with holds a html table. The table looks like this:
<TR>
<TD>asdf, dfg</TD><TD>0915</TD><TD>0945</TD><TD></TD><TD>15</TD><TD>45</TD><TD></TD><TD>1315</TD>
</TR>
<TR>
<TD>asdf, dfg</TD><TD>0915</TD><TD>0945</TD><TD></TD><TD>15</TD><TD>45</TD><TD></TD><TD>1315</TD>
</TR>
<TR>
<TD>asdf, dfg</TD><TD>0915</TD><TD>0945</TD><TD></TD><TD>15</TD><TD>45</TD><TD></TD><TD>1315</TD>
</TR>
<TR>
<TD>asdf, dfg</TD><TD>0915</TD><TD>0945</TD><TD></TD><TD>15</TD><TD>45</TD><TD></TD><TD>1315</TD>
</TR>
How would I use PHP to determine how many columns this table has?

Count <TD>'s with substr_count(). If your string contains more than one row, then count <TR>'s and divide total number of <TD>'s by number of <TR>'s.

As others have mentioned, if you have guaranteed valid HTML, and the table is guaranteed to have equal length rows, you can use simple string manipulation. Split the string on <tr>, then count the number of <td> in each piece:
function count_table_columns($html) {
$html = strtolower($html);
$rows = split('<tr>', $html);
foreach($rows as $row) {
if(!trim($row)) { continue; }
return substr_count($row, '<td>');
}
}
If there is the possibility of malformed HTML, use an HTML parser to parse the table, then iterate through the <tr> nodes, and count the subnodes of type <td>.
Here's one HTML parser to consider:
http://sourceforge.net/projects/simplehtmldom/

This is simplehtmldom code that basically gets the number of columns in the HTML table
$table = $html->find('table',0);
foreach($table->find('tr') as $tr)
{
$c=0;
foreach($tr->find('td') as $td) $c++;
if( $c>$largest OR !isset($largest) ) $largest = $c;
}
$largest will give you column count.

Related

PHP:Simple Dom Parser Find Nth Element Class Exist

I am using the PHP Simple DOM Parser for parsing the HTML Page, Now i am lacking in particular point of how to find the nth element class should be a particular class
For Example:
<table>
<tr>
<th class="h1">ONE</td>
<th class="h2">TWO</td>
<th class="h3">THREE</td>
</tr>
<tr>
<td class="one">Apple</td>
<td class="two">Orange</td>
<td class="null">N/A</td>
</tr>
<tr>
<td class="one">Apple</td>
<td class="null">N/A</td>
<td class="three">Banana</td>
</tr>
</table>
The table looks something like this , so i am traversing the table via tr
foreach ($demo->find("tr") as $val)
{
if(is_object($val->find('td.null', 0))
{
echo "FOUND";
}
}
But the above foreach loop returns "FOUND" if td.null exist.
But I need to find if 2nd element td class is null i need to return as TWO, If third td element class is null i need to return as Three
I hope so that you understand that what i am asking for , so please help me of how to find the nth element class is null
First, what I would do is also iterate each td's thru foreach. So that you'll be able to get which index number key it falls into. (Note that of course its indexing is zero based so it actually starts at 0).
Then inside the inner loop, just check if the class is null, then map it in the corresponding word 1 = one, 2 = two, etc...
Rough example:
$map = array(1 => 'one', 2 => 'two', 3 => 'three');
foreach ($demo->find('tr') as $tr) { // loop each table row
// then loop each td
foreach($tr->find('td') as $i => $td) { // indexing starts at zero
if($td->class == 'null') { // if its class is null
echo $map[$i+1]; // map it to its corresponding word equivalent
}
}
}
So in this case, this would output three and then two. Inside the second table row, the null lands on the third, inside the third row it lands into the second.
It's a pain to do things like that with simple html dom, if you switch to this one you will be able to do things like:
foreach($demo->find("td.null") as $td){
echo $td->index;
}
as well as lots of other jquery-style things that you would expect in a modern parsing lib.

Parsing and calculating the sum of a column in a variable-row table

In the past, I've parsed things quite simply with something like the following:
$dom=new DOMDocument();
$dom->loadHTML(file_get_contents('http://...'));
$tables=$dom->getElementsByTagName('table');
$trs=$tables->item(0)->getElementsByTagName('tr');
$tds=$trs->item(0)->getElementsByTagName('td');
$json=array(
"item1"=>$tds->item(0)->textContent,
"item2"=>$tds->item(1)->textContent,
"item2"=>$tds->item(2)->textContent,
"item2"=>$tds->item(3)->textContent,
);
However, I need to parse a few things such that I can add their values together and obtain a sum to store in an array.
To clarify: Suppose there is a page that has a table. The number of rows in this table will vary, but in one of the columns, there are integers that I would like to add together, so that the sum of all rows of this particular column is stored in my array, with the exception of the first row, since it contains the column names.
At this stage, all I know is that I'm supposed to use a foreach statement to reliably obtain each row's values.
Since you have to skip the first element of the list, a for loop may be better. The code below totals the first column in the rows.
$total = 0;
$trs=$tables->item(0)->getElementsByTagName('tr');
for ($rownum = 1; $rownum < $trs->length; $rownum++) {
$row = $trs->item($rownum);
$td = $row->getElementsByTagName('td')->item(0);
$total += $td->textContent;
}
XPath provides a sum function that would be helpful here:
<?php
$html = '<table>
<tr><td>heading</td><td>heading</td></tr>
<tr><td>1</td><td>2</td></tr>
<tr><td>4</td><td>8</td></tr>
</table>';
$doc = new DOMDocument;
$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
// sum of cells in the second column, skipping the first row
print $xpath->evaluate('sum(//table//tr[position() > 1]/td[2])');
// output: 10

Php parsed html table and count specific <td> similar to another

This question follows another, just solved here
Now I want to do a different count, more difficult to figure out.
In my parsed HTML table, every rows contains two very similar, and consequential, 'td' (number 4 and 5):
<tr>
(1) <td class="tdClass" ....</td>
(2) <td class="tdClass" ....</td>
(3) <td class="tdClass" ....</td>
(4) <td class="tdClass" align="center" nowrap="">No</td>
(5) <td class="tdClass" align="center" nowrap="">No</td>
</tr>
The strings could be "No" in the first 'td' and "Yes' in the second one, and vice versa, both "Yes" or both "No".
I want to count how many 'td', of the 5 kind, contains "No"
By now I counted other 'td'-s by looping through them (see the selected answer for my previous question linked at the top) and selecting only ones matching a target string.
This could be done because those target strings appear only one time in each row.
In this case, instead, the target string ("No") is not unique for each row, because, as in the example above, could exist two times (in the 'td' 4 & 5) in the same 'tr'.
By that, I really don't know how to select only the second (5) 'td' for each row, which match the target string "No", and to exclude the (4) 'td' (which could match the same string) from the count.
Obviously, these two 'td'-s are under different column titles, but this couldn't be useful to distinguish them.
The only solution I have thought about, is to count the 'td' position from left, and to select only the 5th one, but I don't know if it's possible.
You will indeed have to update some parts. First you will want the 4th and 5th element, so you'll have to check for that (keep a counter or use a for loop). Secondly you don't need the break in this case, since it stops the loop.
Code:
<?php
$targetString = 'No';
$rows = $table->find('.trClass');
$count = 0;
foreach($rows as $row) {
$tds = $row->find('td');
for (i = 0; i < count($tds); $i++) {
// Check for the 4th and 5th element
if (($i === 3 || $i === 4) && $tds[$i]->innertext === $targetString) {
$count++;
}
}
}
Here I use a for loop instead of a foreach loop because I don't want to keep manually a counter. I can use the $i easily for this and just use it as an index as well.
Taking the code from your previous question, you should already have this:
$targetString = 'TARGET STRING';
$rows = $table->find('.trClass');
$count = 0;
foreach($rows as $row) {
foreach($row->find('td') as $td) {
if ($td->innertext === $targetString) {
$count++;
break;
}
}
}
Since you're already going through the td's, it would be quite simple to do what you said - "count the 'td' position from left, and to select only the 5th". As long as you know that it is definitely the fifth td you can do:
foreach($rows as $row) {
$tdcount = 0;
foreach($row->find('td') as $td) {
//...
//Bear in mind the first td will have tdcount=0, second tdcount=1 etc. so fifth:
if($tdcount === 4 && ( 'Yes'===$td->innertext || 'No'===$td->innertext) ) {
//do whatever you want with this td
}
$tdcount++;
}
}

Question about output formatting in PHP/XHTML

Is there a way to only print or output 10 elements per row inside a table in PHP from an indexed array with X number of elements of type int.
Maybe some function, I have not heard of?
Or some technique that's used by professionals or beginners alike.
I will embed some code for the TR and TD; however, I was just wondering if there is something I could use to only have 10 elements per row inside of my table.
[Disclaimer: I am new to PHP, and I am just learning, so please no flamers, it really hinders the learning process when one is trying to find solutions or information, thank you.]
You can use a counter in your loop to keep track of how many <td> elements you have generated in the current row:
echo "<tr>";
$i = 0;
foreach (...)
{
if ($i++ % 10 == 0)
{
echo "</tr><tr>";
}
echo "<td></td>";
}
echo "</tr>";
I like using: foreach, array_chunk, and implode to do this. It's better than having to futz with incrementing variables. I suggest you review array basics -- it's powerful stuff.
// here's an array with integers 1 to 50, for example:
$your_array = range(1,50);
// set how many you want in each row
$per_row = 10;
print "<table cellspacing='0' cellpadding='2' border='1'>\n";
Here's the code you can drop in to replace what you have:
foreach (array_chunk($your_array, $per_row) as $set_of_numbers) {
print "<tr><td>";
print implode('</td><td>',$set_of_numbers);
print "</td></tr>\n";
}
And finish with:
print "</table>\n";
That should get you the output you describe, a set of integers in a table, with 10 items per row. Now, if the size of $your_array has a remainder when divided by 10, you may need to pad those cells for looks, but that's secondary.
Output:
<table cellspacing='0' cellpadding='2' border='1'>
<tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td></tr>
<tr><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td></tr>
<tr><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td></tr>
<tr><td>31</td><td>32</td><td>33</td><td>34</td><td>35</td><td>36</td><td>37</td><td>38</td><td>39</td><td>40</td></tr>
<tr><td>41</td><td>42</td><td>43</td><td>44</td><td>45</td><td>46</td><td>47</td><td>48</td><td>49</td><td>50</td></tr>
</table>

limiting array results returned

Hi I would like to limit the results returned depending on what type of product a user selects, $r[1] is the type of product with the array:
foreach($list as $r)
{
$row_color = ($row_count % 2) ? $color1 : $color2;
$size2 = $r[2];
echo "<tr>
<td id=\"id\"><span id=\"non_sorting_header\">" .$r[0]. "</span></td>
<td id=\"name\"><span id=\"non_sorting_header\">" .$r[1]. "</span></td>
<td id=\"speed\"><span id=\"sorting_header\">" .kMGTB2($size2). "</span></td>
<td id=\"download\"><span id=\"sorting_header\">" .$r[3]. " Gb<br />per month</span></td>
<td id=\"contract\"><span id=\"sorting_header\">1<br />month</span></td>
<td id=\"info\"><span id=\"non_sorting_header\">".$r[5]."</span></td>
<td id=\"buy\"><span id=\"non_sorting_header\">".$r[4]."<br />".$r[6]."</span></td>
</tr>";
$row_count++;
}
So if the user enters 'banana' then the function will find all instances within the array where $r[1] starts with the word 'banana' - start is key as it could be 'banana 1' 'banana 2' etc....
I cannot figure out a slick way to do this...any help would be appreciated!
You could use something like the following
function selectFromArray($prefix="", $productArray=array()) {
return array_filter($productArray,
create_function('$element',
'return (stripos($element[1],"'.$prefix.'") === 0); '));
}
Used like:
$list = selectFromArray("Banana", $list);
then proceed with the rest of your page.
That said, it's usually better to do this kind of filtering in your sql query with a WHERE clause
I'm not sure if Jonathan's answer is really the best way to do this.
First of all, create_function() is best avoided. Even without that, though, when using a separate function for it, you're still looping through the list twice. First you loop through the entire list to filter it, and then you loop through the filtered list to print the details. You can easily combine those two:
foreach ($list as $r)
{
if (stripos($r[1], $prefix) === 0) {
continue;
}
// as before...
}
If that condition gets any more complicated you can of course split it out into a separate function as well.

Categories