Parse DOM document to array php - php

*
What I want is, for the DOM to instead of printing the results line by line in a "foreach" loop, rather store it in an array.... So it should look like a list i.e.
"[0] 16GB USB Stick" "[1] Computer monitor" "[2] wireless keyboard"
etc etc
So far I have this, but it only stores the last value from the for each loop.. Please help!
*
$html = new DOMDocument();
#$html->loadHtmlFile('some online shop');
$xpath = new DOMXPath($html);
$nodelist = $xpath->query( "//div[#class='productname']/p" );
foreach ($nodelist as $n)
{
$value = $n->nodeValue;
$list = array($value);
}
echo $list[0];

That's because you're overriding it in each loop. Create an array, and add to that array:
$list = array();
foreach ($nodelist as $n)
{
$value = $n->nodeValue;
$list[] = $value;
}
// Check there's at least one item in the array before accessing it
if (count($list) > 0)
{
echo $list[0];
}

You need to look into how arrays work in PHP. What you're doing wrong is you are re-declaring the array on each iteration, instead of adding more information to it.
$list = array();
foreach ($nodelist as $n) {
$list[] = $n->nodeValue;
}
var_dump($list);
Explanation:
[] basically means - add an item in this array, and auto generate the key.
The foreach I wrote is equivalent to this one:
$i = 0;
foreach ($nodelist as $n) {
$list[$i] = $n->nodeValue;
$i ++;
}

Related

Can this iteration through a XML be rewritten?

I have below code to iterate through a xml file.
$nodelist = $xml->getElementsByTagName('cv');
foreach ($nodelist as $node) {
$naam = $node->getElementsByTagName('naam');
$naamid = $naam->item(0)->nodeValue;
}
It there a way to get the value in one line?
$nodelist = $xml->getElementsByTagName('cv');
foreach ($nodelist as $node) {
$naamid = <a one line to get the nodevalue of naam out of the xml>;
}
You could even do the entire program on one line, using Generators you could create a one-liner function which gets all instances of the naam element:
// Returns a generator which gets all elements with 'naam'
function getNaamElements()
{
foreach ($xml->getElementsByTagName('cv') as $node)
{
yield $node->getElementsByTagName('naam')->item(0)->nodeValue;
}
}
// To get an array:
$array = iterator_to_array(getNaamElements());
// To loop over all 'naam' elements in another loop, use the generator instead
foreach (getNaamElements() as $naam)
{
// Do stuff with $naam
}

PHP Simple DOM Parser extract single value from two url

I use dom parser to grab text from two html documents with the same li class and I retrieved a double value.
<?php
include_once('simple_html_dom.php');
$links = array (
"Model_one" => "car.html",
"Model_two" => "car/edition.html"
);
foreach ($links as $key=>$link) {
$html = file_get_html($link);
$ret[] = $html->find('ul li[class=dotCar]',0)->plaintext;
$pattern = '/.\d+(?:\.\d{2})?((?<=[0-9])(?= usd))/';
preg_match_all($pattern, $ret[0], $result);
$price = array();
foreach($result[0] as $k=>$v) {
$price[] = $v;
echo $price[0];
}
}
// $price[0]= 10.55 11
?>
How can I associate the model_key from $links array to value $price to obtain the result:
model_one 10.55
model_two 11.00
In this way I can retrieve the single value to insert in a MySQL table.
Perhaps something like this:
foreach($result as $k=>$v) {
//$v is the price (10.55 etc.)
foreach($links as $kk=>$vv) {
//$vv is the link (model_one etc.)
$priceAndLinks[$vv] = $v;
}
}
This might give you an idea of the logic needed.

How to use array_unique correctly to echo only one time the same posts

I am trying to display the posts of some RSS Feeds and I came up with a question/problem I have, when I have two same feeds I am trying to show not all the posts but the unique. What I was using is this, that shows me all the posts twice (this is logical)
<?php
$feeds = array(
'feed.xml', 'feed.xml'
);
// Get all feed entries
$entries = array();
foreach ($feeds as $feed) {
$xml = simplexml_load_file($feed);
$entries = array_merge($entries, $xml->xpath('/rss/channel//item'));
}
// Sort feed entries by pubDate (ascending)
usort($entries, 'mysort');
function mysort($x, $y) {
return strtotime($y->pubDate) - strtotime($x->pubDate);
}
foreach ($entries as $entry) {
echo $entry->title;
echo "<br>";
}
?>
but when I changed that line to
$entries = array_unique(array_merge($entries, $xml->xpath('/rss/channel//item')));
I get only one post shown.
How can I correctly show the posts only once? Thank you.
Update:
function mysort($x, $y) {
return strtotime($y->pubDate) - strtotime($x->pubDate);
}
$feeds = array( 'http://feeds.bbci.co.uk/news/world/rss.xml',
'http://feeds.bbci.co.uk/news/world/rss.xml'
);
// Get all feed entries
$entries = array();
foreach ($feeds as $feed) {
$xml = simplexml_load_file($feed);
$entries = array_merge($entries, $xml->xpath('/rss/channel//item'));
}
$uniqueEntries = array();
foreach ($entries as $entry) {
$uniqueEntries[(string)$entry->title] = $entry;
}
// Sort feed entries by pubDate (ascending)
usort($entries, 'mysort');
foreach ($uniqueEntries as $entry) {
echo $entry->title;
echo "<br>";
}
From the documentation of array_unique
Note: Two elements are considered equal if and only if (string) $elem1
=== (string) $elem2. In words: when the string representation is the same. The first element will be used.
In this case, the objects you're getting out of the XPath query translate to string form like "SimpleXML object" (not exactly like that, but the exact representation is not important). According to the above rules, then, every element looks exactly the same to array_unique.
Unfortunately, there's no way to make array_unique behave the way you want, so you will need to fake it yourself:
$feeds = array(
'myfeed.xml', 'myfeed.xml'
);
// Get all feed entries
$entries = array();
foreach ($feeds as $feed) {
$xml = simplexml_load_file($feed);
$tmp = $xml->xpath('/rss/channel//item');
foreach ($tmp as $item) {
if(!in_array($tmp, $entries)) {
$entries[] = $tmp;
}
}
}
I'm not sure if this will work, as it depends on being able to compare objects, and also I don't know that identical nodes from separate XML documents would compare the same anyway. But try it, and let me know. I can whip something else up if this doesn't work.

PHP foreach Loop Element Index

I have an XPath query that gets Genres of a movie.
$genreXpath = $xml_data->xpath("//category");
I get the attributes from $genreXpath like this
$genreName=array();
$genresID=array();
$i=0;
foreach($genreXpath as $node) {
$genre = $node->attributes();
$genreName[$i] = $node["name"];
$genresID[$i] = $node["id"];
$i++;
}
I'm going to be writing these values to a Db hence the two different arrays.
This code works but I know there has to be a better way of doing this be it with a 2 d array, not using a $i counter or something more obvious that I haven't figured out....any pointers???
foreach($genreXpath as $i=>$node) { //note $i is your index of the current $node
$genre = $node->attributes();
$genreName[$i] = $node["name"];
$genresID[$i] = $node["id"];
}
It auto increments and you do not need to declare it above.
Use foreach($genreXpath as $key => $node) {
If you looking to a multidimensional you could do:
$genres = array();
foreach($genreXpath as $node) {
$genre = $node->attributes();
$genres[] = array($node["name"], $node["id"]);
}

in foreach, isLastItem() exists?

Using a regular for loop, it's possible to comapred the current index with the last to tell if I'm in the last iteration of the loop. Is there a similar thing when using foreach? I mean something like this.
foreach($array as $item){
//do stuff
//then check if we're in the last iteration of the loop
$last_iteration = islast(); //boolean true/false
}
If not, is there at least a way to know the current index of the current iteration like $iteration = 5, so I can manually compare it to the length of the $array?
The counter method is probably the easiest.
$i = count($array);
foreach($array as $item){
//do stuff
//then check if we're in the last iteration of the loop
$last_iteration = !(--$i); //boolean true/false
}
You can use a combination of SPL’s ArrayIterator and CachingIterator class to have a hasNext method:
$iter = new CachingIterator(new ArrayIterator($arr));
foreach ($iter as $value) {
$last_iteration = !$iter->hasNext();
}
Here are a few methods for this;
$items = ["Bhir", "Ekky", null, "Uych", "foo"=>"bar"];
$values = array_values($items);
// Bhir, Ekky, Uych, bar
foreach ($values as $i => $item) {
print("$item");
$next = isset($values[$i + 1]);
if ($next) {
print(", ");
}
}
// Bhir, Ekky, , Uych, bar
foreach ($values as $i => $item) {
print("$item");
$next = array_key_exists($i + 1, $values);
if ($next) {
print(", ");
}
}
// Bhir, Ekky, , Uych, bar
$i = count($values);
foreach ($items as $item) {
print("$item");
$next = !!(--$i);
if ($next) {
print(", ");
}
}
// Bhir, Ekky, , Uych, bar
$items = new \CachingIterator(new \ArrayIterator($items));
foreach ($items as $item) {
print("$item");
$next = $items->hasNext();
if ($next) {
print(", ");
}
}
No, you need to have a counter and know the amount of items in the list. You can use end() to get the last item in an array and see if it matches the current value in your foreach.
If you know that the values of the array will always be unique, you can compare the current $item to end($array) to know if you're at the last item yet. Otherwise, no, you need a counter.
You can get the key and the value in foreach() like this:
foreach($array as $key=>$value) { ... }
Alternatively, you could do a count() of the array so you know how many items there are and have an incrementing counter so that you know when you've reached the last item.
end($array);
$lastKey = key($array);
foreach($array as $key => $value) {
if ($key === $lastKey) {
// do something endish
}
}
The valid() method says if the ArrayIterator object has more elements.
See:
$arr = array("Banana","Abacaxi","Abacate","Morango");
$iter = new ArrayIterator($arr);
while($iter->valid()){
echo $iter->key()." - ".$iter->current()."<br/>";
$iter->next();
}

Categories