PHP Simple Dom - Loop through one class at a time? - php

Is there any way to do this? currently I'm using this method to go through the td's for every table in a wikipedia entry that have a class of wikitable.
foreach ($html->find('table.wikitable td') as $key => $info)
{
$text = $info->innertext;
}
However, what I want to do is have seperate loops for each table that share the class name of wikitable. I can't figure out how to do this.
Is there some kind of syntax? I want to do something like this
$first_table = $html->find('table.wikitable td', 0); // return all the td's for the first table
$second_table = $html->find('table.wikitable td', 1); // second one

I might not fully understand your question but it seems that $html->find simply returns an array, in your case an array of tables:
$tables = $html->find('table.wikitable');
You can then loop through your tables and find the td's in each table:
foreach( $tables as $table )
{
$tds = $table->find('td');
foreach( $tds as $td )
{
...
}
}
If you only want to target the second table you can use:
$table = $tables[1];
Or something like that.

Related

take one in foreach loop instead of all

I use the code below to iterate all div's on a page with id = news using PHPScraper. Is it possible to only take the first div it find so that the array only contains one entry? I was thinking of maybe (if possible) only take one in the foreach loop like you can do in c# (myList.Take(1))
$dom = file_get_html('http://localhost/test.html');
//collect all news entries into an array
$myArray = array();
if(!empty($dom)) {
$divClass = $title = '';
foreach($dom->find("div[id*=news]") as $divClass) {
You can use break to stop the loop from continuing after you've added the first div.
Something like this:
foreach($dom->find("div[id*=news]") as $divClass) {
$myArray[] = $divClass; // Just assuming you're doing something like this
break;
}
Side note: The code $divClass = $title = ''; before the loop doesn't serve any purpose in your posted code. The variable $divClass will be completely overwritten on each iteration of your foreach.
I'm guessing you're using PHP Simple HTML DOM Parser.
To grab only one element, you can simply pass 0 as the second argument of find:
$firstDiv = $dom->find('div[id*=news]', 0);
foreach($dom->find("div[id*=news]") as $divClass) {
/// work here
break;
}
break; statement is used to stop loop from further processing. So if you use it directly then loop would only execute once.

Get specific array value where another value is "1"?

I have an array that looks like this:
Id = "ADA001"
Stock: 15
The array has about 1700 records that looks the same, how would I search the array for the ID 1 and return the stock?
Edit: I will need to access the stock of each one of these 17000 records
Edit: I have been getting some help from Daniel Centore, he told me to set an arrays primary key to the id of the item and that it is equal to the stock, but I can't get it to work.
I am currently getting the data from an MySQL database and I store it in an array, like so:
$data[] = array();
$getdisposabletest = mysqli_query($connect, "Select id, disposable FROM products");
while ($z = mysqli_fetch_array($getdisposabletest)) {
$data = $z;
}
Now when I use Daniels code that looks like this:
$myMap = [];
foreach($data as $item) {
$myMap[$item['id']] = $item['disposable'];
}
It doesn't return anything when I try to echo my product with the ID "ADA001"
echo $myMap["ADA001"];
Also when I do "count($mymap)" it says its 2 records big, when it should be muuuch larger than that?
Thanks for help
I would use array_filter. Return the result of a comparitor.
$results = array_filter($targetArray, function($el) {
return $el === 1;
});
Edit: Now that it has been made clear that the OP wants to query from thousands of items, the correct way to do this is to make the Id the key to a map in PHP, like this:
$myMap = [];
foreach($array as $item) {
$myMap[$item['Id']] = $item['Stock'];
}
Now, whenever you want to access item '12', simply use $myMap['12'].
The reason this is faster is because of something called algorithmic complexity. You should read about Big-O notation for more info. Essentially, the first operation here is on the order of n and then looping through each of the items that comes out is on the order of n*log(n), so the final result is on the order of n*log(n) which is the best you'll be able to do without more information. However, if you were only accessing one element, just accessing that one element via MySQL would be better because it would be on the order of log(n), which is faster.
Edit 2: Also notice that if you were to access mutliple fields (ie not just the stock) you could do the following:
$myMap = [];
foreach($array as $item) {
$myMap[$item['Id']] = $item;
}
And simply access item 12's stock like this: $myMap['12']['stock'] or its name like this: $myMap['12']['name'].
You would do something like this.
$newArray=[];
foreach($array as $item){
if($item['Id'] === 1){
$newArray[] = $item;
}
}
$stockArray = array_column($newArray,'Stock');

PHP: foreach loop inside PHP variable?

I'm trying to dynamically generate html select options using PHP based on whatever its stored in mysql database.
the column that stores the data called sizez.
the data in that column is stored like so:
small,large,xlarge,xxlarge
so basically the data is separated by a comma.
now in my php page I simply pull the data and display it on my page using a while loop for each product that is stored in the mysql database.
the issue that I am having is that I need to generate a select option dropdown list based on the sizez column for each item.
for that I am using the explode() function and it will generate the select option successfully too.
however, the issue is that it will only get the strings from the first sizez column and ignores the rest of the items But it will display the string from the first column for other items too and it repeats them!
this is my code:
while($row = mysqli_fetch_array($query, MYSQLI_ASSOC)){
$id = $row["id"];
$sizez = $row["sizez"];
$sizez = preg_replace('/\.$/', '', $sizez); //Remove dot at end if exists
$array = explode(',', $sizez); //split string into array seperated by ','
foreach($array as $value) //loop over values
{
//echo $value . PHP_EOL; //print value
$sizesOption .='<option>'.$value.'</option>';
}
$all_list .="<select>
'.$sizesOption.'
</select>";
so I thought to put the foreach($array as $value) inside the $all_list .= but that approach is wrong.
could someone please advise on this issue?
any help would be appreciated.
EDIT:
The expected result should be like this:
item one item two item three
small large small
large xxlarge xxlarge
However, with my code I get the result like this:
item one item two item three
small small small
large large large
small small
large large
small
large
so basically, it will get the sizes column from the first item and it will repeat it inside select options for other items exactly like the example above.
Since you are generating separate <select> for each iteration, you have to reset $sizeOptions. I suggest using arrays instead of just concatenating strings:
$allList = array();
while($row = mysqli_fetch_array($query, MYSQLI_ASSOC)){
$sizesOption = array();
$sizez = preg_replace('/\.$/', '', $row["sizez"]);
$array = explode(',', $sizez);
foreach ($array as $value) {
$sizesOption[] = "<option>{$value}</option>";
}
$all_list[] = '<select>'.implode("\r\n", $sizesOption).'</select>';
}
echo implode("\r\n", $allList);
From what you have posted it looks like you're regenerating $all_list in every iteration of your while loop.
So if you echo $all_list outside of the while loop it will only have the last iteration available - all the other ones having been overwritten during the process of the while loop.
Maybe I am wrong. but as simple as:
$all_list ="<select>".$sizesOption."</select>";
Close the opening braces for while loop before the '$all_list .="' statement.You are iterating the statement inside while loop.
`
while($row = mysqli_fetch_array($query, MYSQLI_ASSOC))
{
$id = $row["id"];
$sizez = $row["sizez"];
$sizez = preg_replace('/\.$/', '', $sizez); //Remove dot at end if exists
$array = explode(',', $sizez); //split string into array seperated by ','
foreach($array as $value) //loop over values
{
//echo $value . PHP_EOL; //print value
$sizesOption .='<option>'.$value.'</option>';
}
}
$all_list .="<select>
'.$sizesOption.'
</select>";
`
May be this will work
I figured it out. Thanks to Justinas's answer. I realized that I had to put my php variable inside the while loop.
so all I had to do was to put $sizesOption =array(); inside my while loop and everything works fine now.
P.S. i haven't made any other changes to my code above.

How to get a score of a Field on Zend Lucene

I have something similar to the following code:
[...]
while($result=mysql_fetch_assoc($query)){
//Build a doc
$doc = new Zend_Search_Lucene_Document();
[...]
$doc->addField(Zend_Search_Lucene_Field::text('category', sanitize($result["category"])));
$all_values=explode(',',$result["value"]);
$i=0;
foreach ($all_values as $value) {
$doc->addField(Zend_Search_Lucene_Field::text('value_'.($i++), urlencode($value)));
}
[...]
//Add to doc
$index->addDocument($doc);
}
[...]
Now I would like to show only the field value_... which have the greater score after a search.
How could I do it?
After thinking a bit more on this, I've got a solution to my problem.
Zend Lucene orders the results by score and so, what I need to do is create a doc for each $value of the foreach loop and then, create an index for the ID of my current result (called res_id) that is inside of the foreach. This way, i can programatically show only one result of the same res_id after a search.
So my code becomes:
[...]
while($result=mysql_fetch_assoc($query)){
$all_values=explode(',',$result["value"]);
//Create a Doc foreach value of $all_values and gave them the same category of the result of the query
foreach ($all_values as $value) {
//Build a doc
$doc = new Zend_Search_Lucene_Document();
[...]
//Added res_id just to help grouping the results
$doc->addField(Zend_Search_Lucene_Field::unIndexed('res_id', sanitize($result["id"])));
$doc->addField(Zend_Search_Lucene_Field::text('category', sanitize($result["category"])));
//Renamed the field to value
$doc->addField(Zend_Search_Lucene_Field::text('value', urlencode($value)));
[...]
//Add to doc
$index->addDocument($doc);
}
}
[...]
Now, when I make a search, I show only the first occurrence of res_id (the one that have more score).
Here's the code that I have on my Controller that sends to the view an array with the items and the total items found:
[...]
public function indexAction()
{
$this->view->query=urldecode($this->_getParam("query"));
if ($this->view->query){
//open the index
$index = new Zend_Search_Lucene('path/to/lucene/index');
$hits = $index->find($this->view->query);
$executed_ids=array();
$total=0;
$this->view->items=array();
foreach ($hits as $hit) {
//Pass item to view only if it is not listed yet on $executed_ids
if (!$executed_ids[$hit->res_id]){
$this->view->items[]=$hit;
$executed_ids[$hit->res_id]=true;
$total++;
}
}
$this->view->total_hits=$total;
}
}
[...]

php domdocument parse nested tables

I got a table which looks like this: http://pastebin.com/jjZxeNHF
I got it as a PHP-DOMDocument.
Now I want to "parse" this table.
If I am correct, something like the following is not going to work because $superTable->getElementsByTagName('tr') is not only going to get outer tr's but also the inner ones.
foreach ($superTable->getElementsByTagName('tr') as $superRow) {
foreach ($superRow->getElementsByTagName('td') as $superCol) {
foreach ($superCol->getElementsByTagName('table') as $table) {
foreach ($table->getElementsByTagName('tr') as $row) {
foreach ($row->getElementsByTagName('td') as $col) {
}
}
}
}
}
How can I go trough all the tables, field by field, as described in the second snippet.
This is my solution:
foreach ($raumplan->getElementsByTagName('tr') as $superRow) {
if ($superRow->getElementsByTagName('table')->length > 0) {
foreach ($superRow->getElementsByTagName('td') as $superCol) {
if ($superCol->getElementsByTagName('table')->length > 0) {
foreach ($superCol->getElementsByTagName('table') as $table) {
foreach ($table->getElementsByTagName('tr') as $row) {
foreach ($row->getElementsByTagName('td') as $col) {
}
}
}
}
}
}
}
It checks if you are in the outer table by looking if there is a table contained in the element.
You could use XPath to eliminate a lot of the blatantly low-level iteration and reduce the apparent complexity of all this...
$xpath = new DOMXPath($document);
foreach ($xpath->query('//selector/for/superTable//table') as $table) {
// in case you really wanted them...
$superCol = $table->parentNode;
$superRow = $superCol->parentNode;
foreach ($table->getElementsByTagName('td') as $col) {
$row = $td->parentNode;
// do your thing with each cell here
}
}
You could drill down further than this, if you wanted -- if you just wanted every cell in the inner tables, you could reduce it to one loop over //selector/for/superTable//table//td.
Course, if you're dealing with valid HTML, then you could just loop over each element's children as well. It all depends on what the HTML will look like, and exactly what you need from it.
Edit: If you can't use XPath for some reason, you might could do something like
// I assume you've found $superTable already
foreach ($superTable->getElementsByTagName('table') as $table) {
$superCol = $table->parentNode;
$superRow = $superCol->parentNode;
foreach ($table->getElementsByTagName('td') as $col) {
$row = $col->parentNode;
// do your thing here
}
}
Note that neither solution bothers to iterate over the rows etc. That's a big part of what obviates the need to get only rows in the current table. You're only looking for tables within the table, which by definition (1) will be the sub-tables and (2) will be within a column within a row within the main table, and you can get the parent row and column from the table element itself.
Of course, both solutions assume you're only nesting tables one level deep. If it's more than that, you're going to want to look at a recursive solution and DOMElement's childNodes property. Or, a more narrowly focused XPath query.

Categories