I am scraping an ecommerce website and need to get some data from products, like product name, price, ...
For that, I have:
...
// library includes...
$html = file_get_html($link);
foreach($html->find('.productBoxClass') as $element){
foreach($element->find('.productTitle') as $product) {
$product = $product->plaintext;
}
foreach($element->find('.price') as $price) {
$price = $price->outertext;
}
// and so on...
}
I wanna save this data in a database. So, I want to save all the data in an array for after verify each product if I have to insert or just update. I am intending to populate an multi-dimensional array with this data:
Each position of the array with another array containing the information about one product... To make it easier to save in the database after...
Any help?
This seems like an abnormal data structure or you should be looping through it differently. But if it is an abnormal structure and the product and price aren't grouped together, they are just listed in the same order, then this should work:
$products = [];
$i = 0;
foreach($element->find('.productTitle') as $product) {
$products[$i++]['product'] = $product->plaintext;
}
$i = 0;
foreach($element->find('.price') as $price) {
$products[$i++]['price'] = $price->outertext;
}
Note the $i++ as the key which will increment $i each loop.
If the product and pricing are grouped together in an element, then you should be looping on that element and there should be no need for a foreach for product and price.
Please check the below code, let me know your thoughts...
<?php
// library includes...
$html = file_get_html($link);
$productArr = array();
foreach($html->find('.productBoxClass') as $element){
$tempArr = array('title' => '','price' => 0,'other' => ''); // declare temp array for stroing each product nodes
foreach($element->find('.productTitle') as $product) {
$tempArr['title'] = $product->plaintext; // To do check for empty text here
}
foreach($element->find('.price') as $price) {
$tempArr['price'] = $price->outertext; // To do validate the price
}
foreach($element->find('.other-features') as $price) {
$tempArr['other'] = $price->outertext; // To do validate the price
}
// and so on... with $tempArr['key']
// then assign
$productArr[] = $tempArr; // save temp array in global product array
}
// Product array
echo '<pre>';print_r($productArr);die;
Use in first foreach the count item:
...
// library includes...
$html = file_get_html($link);
// Array declaration
$products = array();
foreach($html->find('.productBoxClass') as $i => $element){
foreach($element->find('.productTitle') as $product) {
$products[$i]['name'] = $product->plaintext;
}
foreach($element->find('.price') as $price) {
$products[$i]['price'] = $price->outertext;
}
// and so on...
}
And will result:
Array
(
[0] => Array
(
[name] => Product 1
[price] => 1.00
)
[1] => Array
(
[name] => Product 1
[price] => 1.00
),
...
)
Related
I have an array of the form:
class anim {
public $qs;
public $dp;
public $cg;
public $timestamp;
}
$animArray = array();
$myAnim = new anim();
$myAnim->qs = "fred";
$myAnim->dp = "shorts";
$myAnim->cg = "dino";
$myAnim->timestamp = 1590157029399;
$animArray[] = $myAnim;
$myAnim = new anim();
$myAnim->qs = "barney";
$myAnim->dp = "tshirt";
$myAnim->cg = "bird";
$myAnim->timestamp = 1590133656330;
$animArray[] = $myAnim;
$myAnim = new anim();
$myAnim->qs = "fred";
$myAnim->dp = "tshirt";
$myAnim->cg = "bird";
$myAnim->timestamp = 1590117032286;
$animArray[] = $myAnim;
How do I create a new array containing only the non-duplicates (and the latest entry where duplicates are found) of $animArray, where a duplicate is defined as:
one where $myAnim->dp has the same value as that of another array element's $myAnim->dp AND the $myAnim->cg from the first and the $myAnim->cg from the second have the same value as each other.
In the example above, only the first element is unique by that definition.
I'm hoping there's an elegant solution. I've been through all the array functions in the PHP manual but can't see how it could be achieved.
I could loop through each array element checking if $myAnim->dp has the same value as that of another array element's $myAnim->dp, saving the matches into a new array and then looping through that new array, checking for its $myAnim->cg matching the $myAnim->cg of any other element in that new array.
A more elegant solution would allow me to to change which combination of key-value pairs determine whether there's a duplicate, without having to recast much code.
Does such a solution exist?
Thanks for helping this novice :)
While there is nothing built-in that can be used directly out of the box, there isn't a lot of code necessary to handle an arbitrary number of properties to consider for uniqueness. By keeping track of each unique property in a lookup array, we can build an array where the leaf nodes (i.e. the ones that isn't arrays themselves) are the objects.
We do this by keeping a reference (&) to the current level in the array, then continue building our lookup array for each property.
function find_uniques($list, $properties) {
$lookup = [];
$unique = [];
$last_idx = count($properties) - 1;
// Build our lookup array - the leaf nodes will be the items themselves,
// located on a level that matches the number of properties to look at
// to consider a duplicate
foreach ($list as $item) {
$current = &$lookup;
foreach ($properties as $idx => $property) {
// last level, keep object for future reference
if ($idx == $last_idx) {
$current[$item->$property] = $item;
break;
} else if (!isset($current[$item->$property])) {
// otherwise, if not already set, create empty array
$current[$item->$property] = [];
}
// next iteration starts on this level as its current level
$current = &$current[$item->$property];
}
}
// awr only calls the callback for leaf nodes - i.e. our items.
array_walk_recursive($lookup, function ($item) use (&$unique) {
$unique[] = $item;
});
return $unique;
}
Called with your data above, and the requirement being that uniques and the last element of duplicates being returned, we get the following result:
var_dump(find_uniques($animArray, ['dp', 'cg']));
array(2) {
[0] =>
class anim#1 (4) {
public $qs =>
string(4) "fred"
public $dp =>
string(6) "shorts"
public $cg =>
string(4) "dino"
public $timestamp =>
int(1590157029399)
}
[1] =>
class anim#3 (4) {
public $qs =>
string(4) "fred"
public $dp =>
string(6) "tshirt"
public $cg =>
string(4) "bird"
public $timestamp =>
int(1590117032286)
}
}
Which maps to element [0] and element [2] in your example. If you instead want to keep the first object for duplicates, add an isset that terminates the inner loop if property value has been seen already:
foreach ($properties as $idx => $property) {
if ($idx == $last_idx) {
if (isset($current[$item->$property])) {
break;
}
$current[$item->$property] = $item;
} else {
$current[$item->$property] = [];
}
// next iteration starts on this level as its current level
$current = &$current[$item->$property];
}
It's important to note that this has been written with the assumption that the array you want to check for uniqueness doesn't contain arrays themselves (since we're looking up properties with -> and since we're using array_walk_recursive to find anything that isn't an array).
This was fun:
array_multisort(array_column($animArray, 'timestamp'), SORT_DESC, $animArray);
$result = array_intersect_key($animArray,
array_unique(array_map(function($v) { return $v->dp.'-'.$v->cg; }, $animArray)));
First, extract the timestamp and sort that array descending, thereby sorting the original array.
Then, map to create a new array using the dp and cg combinations.
Next, make the combination array unique which will keep the first duplicate encountered (that's why we sorted descending).
Finally, get the intersection of keys of the original array and the unique one.
In a function with dynamic properties:
function array_unique_custom($array, $props) {
array_multisort(array_column($array, 'timestamp'), SORT_DESC, $array);
$result = array_intersect_key($array,
array_unique(array_map(function($v) use ($props) {
return implode('-', array_map(function($p) use($v) { return $v->$p; }, $props));;
},
$array)));
return $result;
}
$result = array_unique_custom($animArray, ['dp', 'cg']);
Another option would be to sort it ascending and then build an array with a dp and cg combination as the key, which will keep the last duplicate:
array_multisort(array_column($animArray, 'timestamp'), SORT_ASC, $animArray);
foreach($animArray as $v) {
$result[$v->dp.'-'.$v->cg] = $v;
}
In a function with dynamic properties:
function array_unique_custom($array, $props) {
array_multisort(array_column($array, 'timestamp'), SORT_ASC, $array);
foreach($array as $v) {
$key = implode(array_map(function($p) use($v) { return $v->$p; }, $props));
$result[$key] = $v;
}
return $result;
}
$result = array_unique_custom($animArray, ['dp', 'cg']);
//Create an array with dp and cg values only
$new_arr = [];
foreach($animArray as $key=>$item) {
$new_arr[] = $item->dp.','.$item->cg;
}
$cvs = array_count_values($new_arr);
$final_array = [];
foreach($cvs as $cvs_key=>$occurences) {
if ($occurences == 1) {
$filter_key = array_keys($new_arr, $cvs_key)[0];
$final_array[$filter_key] = $animArray[$filter_key];
}
}
The final result would be (from your example) in $final_array:
[0] => anim Object
(
[qs] => fred
[dp] => shorts
[cg] => dino
[timestamp] => 1590157029399
)
Some explanation:
//Create a new array based on your array of objects with the attributes dp and cg
//with a comma between them
$new_arr = [];
foreach($animArray as $key=>$item) {
$new_arr[] = $item->dp.','.$item->cg;
}
/*
$new_arr now contains:
[0] => shorts,dino
[1] => tshirt,bird
[2] => tshirt,bird
*/
//Use builtin-function array_count_values to get the nr of occurences for
//each item in an array
$cvs = array_count_values($new_arr);
/*
$cvs would contain:
(
[shorts,dino] => 1
[tshirt,bird] => 2
)
*/
//Iterate through the $cvs array.
//Where there are only one occurence (no duplicates)
//create a final array $final_array
$final_array = [];
foreach($cvs as $cvs_key=>$occurences) {
if ($occurences == 1) {
/*
array_keys with second argument $csv_key searches for key with
with the key from $cvs-key
so basically search for:
shorts,dino and retrieve the key 0 (first element)
*/
$filter_key = array_keys($new_arr, $cvs_key)[0];
/*
Add a new item to the $final_array based on the key in
the original array $animArray
if you don't want the original key in the new array
you could just do $final_array[] instead of
$final_array[$filter_key]
*/
$final_array[$filter_key] = $animArray[$filter_key];
}
}
You said you would like to have some kind of functionality test different attributes. I believe it would just be making a function/method where you pass in two values to the arguments $attr1 ('dp'?), $attr2('cg'?) or similar.
UPDATE
I had not grasped that you wanted the last value as well. This actually seemed as an easier task. Maybe I am missing something but it was fun to come up with a different approach than other answers :-)
//Create an array with dp and cg values only
$new_arr = [];
foreach($animArray as $key=>$item) {
$new_arr[] = $item->dp.','.$item->cg;
}
//Sort keys descending order
krsort($new_arr);
//Because of sending order of keys above, the unique values would return the
//last item of the duplicates
$new_arr2 = array_unique($new_arr);
//Switch order of keys back to normal (ascending)
ksort($new_arr2);
//Create a new array based on the keys set in $new_arr2
//
$final_arr = [];
foreach($new_arr2 as $key=>$item) {
$final_arr[] = $animArray[$key];
}
The output of $final_arr[] would be (in your example)
Array
(
[0] => anim Object
(
[qs] => fred
[dp] => shorts
[cg] => dino
[timestamp] => 1590157029399
)
[1] => anim Object
(
[qs] => fred
[dp] => tshirt
[cg] => bird
[timestamp] => 1590117032286
)
)
I am creating following array that contains all products and all their categories:
$result = $wpdb->get_results("SELECT product_nr, category FROM erp_product_categories",ARRAY_A);
$product_categories = array();
foreach($result as $row){
$product_categories[$row["product_nr"]][] = $row["category"];
}
(product_nr is an integer and category is a string)
Then i want to check if one of the categories of a product matches with an other variable and return true if thats the case:
foreach($product_categories[$ean] as $product_categorie) {
$manages_post = in_array( $product_categorie, $this->term_link_conditions );
if($manages_post == true){
break;
}
}
return $manages_post;
But I am getting the error
Invalid argument supplied for foreach()
is it not possible to loop only through elements of an array with a specific key?
Edit:
The array looks like this
Array
(
[10001] => Array //product_nr
(
[0] => 1 //category
[1] => 4 //category
)
[10002] => Array
(
[0] => 1
[1] => 20
)
//...
)
You should check that what you are passing to foreach is an array by using the is_array function
If you are not sure it's going to be an array you can always check using the following PHP example code:
if (is_array($product_categories[$ean])) {
foreach ($product_categories[$ean] as $product_categorie) {
//do something
}
}
Check out all your foreach statements, and look if the thing before the as, to make sure it is actually an array. Usevar_dump to dump it.
Try this :
if(is_array($product_categories) && sizeof($product_categories) > 0) {
foreach($product_categories as $key => $product_categorie) {
if($manages_post = in_array($key, $this->term_link_conditions)){
return $manages_post;
}
}
}
I figured out a way to do this
$product_category = $product_categories[$ean];
if (is_array($product_category)) {
$matches = array_intersect($product_category, $this->term_link_conditions);
if(sizeof($matches) > 0){
$manages_post = true;
}
}
i have products that belong to categories and i need to get such categories and output them as a single array. This is my this code:
$act_prod = array(0=>1,1=>10);
$active_cat = array();
foreach ($act_prod as $act) {
$cat = $this->getDi()->productTable->load($act);
$active_cat[$act] = $cat->getCategories();
}
print_r($active_cat);
Which will output:
Array ( [1] => Array ( [0] => 1 ) [10] => Array ( [0] => 2 ) )
This means product 1 belongs to category 1 and product 10 to category 2 but i dont need all that. I only need the categories like this: Array (1, 2) or Array (0=>1, 1=>2).
What should i use so i get the correct output?
Thank you.
Modified your code to build up just the list you want.
$act_prod = array(0=>1,1=>10);
$active_cat = array(); // will be a flat list of categories
foreach ($act_prod as $act) {
$cat = $this->getDi()->productTable->load($act);
foreach($cat->getCategories as $category) {
// if we have not seen this category on any previous category, push it
if(!in_array($cat->getCategories(), $active_cat)) {
array_push($active_cat, $cat->getCategories());
}
}
}
// if desired, sort array first
print_r($active_cat);
foreach ($act_prod as $act) {
$cat = $this->getDi()->productTable->load($act);
$cats = $cat->getCategories();
foreach($cats as $cat)
{
$active_cat[] = $cat['cat_id'];
}
}
Assuming cat_id is your category id
You need to flatten the $active_cat array, like this:
// ...
foreach ($cat->getCategories() as $category) {
$active_cat[] = $category;
}
// ...
Afterwards, make sure there are no duplicates:
$active_cat = array_unique($active_cat);
I am trying to combine keys and values in arrays. I have an product_id with different price.
Let say
Product id and price
id 101 and price is 100
id 105 and price is 200
id 101 and price is 300
list of product ids in array with $product_ids[] and list of price also $price_amount[]
So I preferred to combine the two arrays using array_combine
I made array_combine($product_ids,$price_amount);
Now it appears look like this way
array(2) { [101]=> float(100) [105]=> float(300) }
Is there is a way to add the key elements to the id as something like
array(2) {
[101] => float(400) (100+300)
[105] => float(300)
}
Here is the idea i tried
$products = array();
$order_totalss = array();
foreach (get_posts('post_type=shop_order&numberposts=-1&post_status=publish') as $order) {
$order = new WC_Order($order->ID);
if (wc_customer_bought_product($order->billing_email, $order->user_id, $product_id)) {
$productcounts[] = $product_id;
$order_totalss[] = $order->get_total();
}
}
$arraymergeme = array_combine($productcounts, $order_totalss);
You will have to do this manually I'm afraid:
$total = array();
foreach ($product_ids as $key => $value) {
// each value of product_ids becomes the key
if (isset($total[$value])) {
// we have seen this key before
$total[$value] += $price_amount[$key];
} else {
// new key
$total[$value] = $price_amount[$key];
}
}
Simple code so you can see what's happening clearly:
$ids = array(101, 105, 101);
$prices = array(100, 200, 300);
$totals = array();
foreach ($ids as $key => $id)
{
// Make sure index is defined
if ( ! isset($totals[$id]))
{
// Make sure we have a value
$totals[$id] = 0;
}
// Assuming index $key matches... add to the total
$totals[$id] += $prices[$key];
}
PHP arrays are associative so you can write something like: price['101'] = 100 thereby using the product id as the array index.
Thinking you are looking for something like this. I haven't done php in awhile, so the syntax may need tweaking, but I think the logic is correct.
$cart = array(
"101" => 100,
"105" => 200,
"101" => 300
);
$product_id_arr = array();
foreach ($cart as $product_id => $price) {
if(array_key_exists($product_id, $product_id_arr)){
$product_id_arr[$product_id] = $product_id_arr[$product_id] + $price;
}else{
$product_id_arr[$product_id] = $price;
}
}
array_combine will not do the trick for you. You will have to iterate through the array and total them as you go. Here's an example:
<?php
$product_ids = array('101', '105', '101');
$price_amount = array(100, 200, 300);
$combined = array();
$productCount = count($product_ids);
for($i = 0; $i < $productCount; $i++) {
// if this product_id is not in the $combined array, add its price
// as associative array ('101'=>100)
// but if it is found in $combined, add to its current price
if (!array_key_exists($product_ids[$i], $combined)) {
$combined[$product_ids[$i]] = $price_amount[$i];
} else {
$combined[$product_ids[$i]] += $price_amount[$i];
}
}
print_r($combined);
?>
Results:
Array
(
[101] => 400
[105] => 200
)
Try this
$final_arr = array();
for($i=0;$i<count($product_ids);$i++) {
if(!isset($final_arr[$product_ids[$i]])) {
$final_arr[$product_ids[$i]] = 0;
}
$final_arr[$product_ids[$i]] += $price_amount[$i];
}
Yes you can add key elements to id, basically an array can be created by using the array() language construct. It takes any number of comma-separated key => value pairs as arguments.
array(
key => value,
key2 => value2,
key3 => value3,
...
)
the one you are looking for is associative array. you can definitely specify the key you want and store the value you want at that key.
here is a link that would be helpful
I'm actually working on ZF. I have a category table with which, I want to create a tree in order to get display the data as below :
Category
--Sub cat 1
--Sub cat 2
----Su sub cat 1
Another Category
--Sub cat 1
//...etc...
I'm using the fetchAll method to get all my data. Everyting works fine. But then I'm now trying to create my tree into a double foreach loop as below :
$tree = array();
foreach($data as $parent){
$tree[$parent->name] = array();
foreach($data as $child){
if($child->parent_id == $parent->id){
$tree[$parent->name][] = $child->name;
}
}
}
The problem is that the loop stop after the main loop first iteration so I'm just getting the first parent and it's sub category but it does not continue to the second parent.
My database table as the following fields :
id, name, parent_id
Any idea?
EDIT
Thanks to you Thibault, it did work using the good old for loop :
for($i=0;$i<count($data);$i++){
$tree[$data[$i]->name] = array();
for($j=0;$j<count($data);$j++){
if($data[$j]->parent_id == $data[$i]->id){
$tree[$data[$i]->name][] = $data[$j]->name;
}
}
}
You may have a conflict between the cursor of both $data variables.
You should use a copy of $data for the second foreach loop.
Or use for loops with $i and $j index, and call them via $data[$i] and $data[$j] to access the array, so the loops don't get messed up.
EDIT
Happy i could help, but after some research, i created this piece of code :
<?
class o {
public $id;
public $name;
public $parent_id;
function __construct($_id,$_name,$_parent) {
$this->id = $_id;
$this->name = $_name;
$this->parent_id = $_parent;
}
}
$data = array(
new o(1,'toto',0),
new o(2,'truc',1),
new o(3,'machin',1),
new o(4,'bidule',2),
new o(5,'titi',3),
new o(6,'tutu',3),
);
$tree = array();
foreach($data as $parent){
$tree[$parent->name] = array();
foreach($data as $child){
if($child->parent_id == $parent->id){
$tree[$parent->name][] = $child->name;
}
}
}
print_r($tree);
And your code works just fine :
(something must be wrong somewhere else ...)
Array
(
[toto] => Array
(
[0] => truc
[1] => machin
)
[truc] => Array
(
[0] => bidule
)
[machin] => Array
(
[0] => titi
[1] => tutu
)
[bidule] => Array
(
)
[titi] => Array
(
)
[tutu] => Array
(
)
)