Create a hierarchical list of array elements - php

I have a an array of products. Each product contains its category and subcategories listed in a hierarchical order:
Array
(
[product_id_1] => Array
(
[0] => Men
[1] => Sunglasses
[2] => Luxury
[3] => Ray-ban
)
[product_id_2] => Array
(
[0] => Women
[1] => Lenses
[2] => Casual
[3] => Gucci
)
[product_id_3] => Array
(
[0] => Men
[1] => Sunglasses
[2] => Casual
[3] => Prada
)
[...]
)
I want to create an unordered hierarchical HTML menu like so:
-Men
--Sunglasses
---Luxury
----Ray-ban
---Casual
----Prada
-Women
--Lenses
---Casual
----Gucci
The function should strip out repetitive categories and subcategories. This script returns the array of products that I've posted at the top:
<?php
function displayNestedMenu( $posts, $taxonomies ) {
foreach ( $posts as $post ) {
foreach ( $taxonomies as $key => $taxonomy ) {
$push = wp_get_object_terms( $post->ID, $taxonomy );
if ( !empty( $push ) ) {
$list[$post->ID][] = $push[0]->name;
}
}
}
return $list;
}
print_r( displayNestedMenu( $posts, $taxonomies ) );
?>
I imagine the solution should invoke the function inside the function but after trying a couple of methods I haven't succeeded yet. Any suggestions are appreciated!

Here is a simple idea :
$array = array(
'product_id_1' => array(
'Men',
'Sunglasses',
'Luxury',
'Ray-ban'
),
'product_id_2' => array(
'Women',
'Lenses',
'Casual',
'Gucci',
),
'product_id_3' => array(
'Men',
'Sunglasses',
'Casual',
'Prada'
)
);
The idea is to recreate the keys depending on the parent category, after that we sort them using ksort():
function tree($array){
$newArray = array();
foreach ($array as $arr) {
foreach ($arr as $key => $row) {
if ($key > 0) {
$index = array();
for ($i = 0; $i <= $key; $i++)
$index[] = $arr[$i];
$index = implode('_', $index);
} else
$index = $row;
$newArray[$index] = $row;
}
}
ksort($newArray);
return $newArray;
}
Then display the HTML :
$products = tree($array);
$i = 0;
echo '<ul style="list-style-type:none">';
foreach ($products as $key => $row) {
if(strcmp($row, $key) == 0 && $i != 0)
echo '</ul><br><ul style="list-style-type:none">';
++$i;
$level = count(explode('_', $key));
$padding = 15 * (--$level);
echo
'<li style="padding-left:' . $padding . 'px">
<span style="border-left:1px dashed black;border-bottom:1px dashed black;"> ' . $row . '</span>
</li>';
}
echo '</ul>';

PHP has powerful array features: string-indexed arrays can help provide solutions to problems like this one.
For the array conversion step:
$hrchy=array();
foreach($products AS $product){//$products is as per your first array, at start…
hrchy_ins($hrchy,$product);
}
function hrchy_ins(array &$hierarchy,array $product){//$hierarchy should be passed by reference…
if(\count($product)>0){//Condition necessary to implement base case, avoiding infinite recursion
if(!isset($hierarchy[$product[0]])){$hierarchy[$product[0]]=array();}//Conditional execution ignores duplicates…
if(\count($product)>1){hrchy_ins($hierarchy[$product[0]],\array_slice($product,1));}//Condition may not be strictly necessary (see condition above!)
} }
We might now use a recursive approach for a further HTML-writing step (the recursion secret sauce = a simple recursive function including a branch-on-condition for the base-case):
function prod_list(array $hierarchy){
if(\count($hierarchy)===0){
return '';
}else{
$list='';
$list.='<ul>';
foreach($hierarchy AS $cat => $children){
$list.='<li>'.$cat;
$list.=prod_list($children);//Recursive step…
$list.='</li>';
}
$list.='<ul>';
return $list;
}
}
Finally, after defining the function, we invoke it:
echo(prod_list($hrchy));
Disclaimer: I have not tested this code.

You could transform the array in a static way, as the structure you describe always has four parts;
$hierarchy = array();
foreach($products as $product_id => $product) {
list($gender, $category, $type, $brand) = $product;
$hierarchy[$gender][$category][$type][$brand][] = $product_id;
}

Related

php array returning same values in loop

I am trying to loop through each key but i am facing a problem of same value repeating inside for each loop
Here is example of my current code and result (click here)
here is my code so far
<?php
$data2 = array(
'category_name' => '33287*100*prescription*1,32457*1250*lab*1'
);
$result = array('0' => (object)$data2);
foreach ($result as $key => $category) {
$category_name = explode(',', $category->category_name);
}
$newresults=[];
foreach ($category_name as $key) {
$category->category_name = $key;
$newresults[]=$category;
}
$result=$newresults;
$newresults=[];
$category->items_count = 0;
foreach ($result as $key => $value) {
list($sale_key, $sale_value) = explode('*', $value->category_name);
// $category->items_count += count($sale_value);
$newresults[]=$category;
}
$result=$newresults;
i am expect the result should be
Array
(
[0] => stdClass Object
(
[category_name] => 33287*100*prescription*1
[items_count] => 0
)
[1] => stdClass Object
(
[category_name] => 32457*1250*lab*1
[items_count] => 0
)
)
The bug is that you're relying only on the last version of $category after you've finished looping it earlier - you'd have to be using it within the loop where it's assigned, in order to get each value in turn, or you could use $value from your last foreach loop.
But as a general observation, this code has way too many loops etc. just for processing one array in the way you've requested. Here's a much simpler version:
$category_name = '33287*100*prescription*1,32457*1250*lab*1';
$category_name_arr = explode(',', $category_name);
print_r($category_name_arr);
$newresults=[];
foreach ($category_name_arr as $cat) {
$newresults[] = (object) array("category_name" => $cat, "items_count" => 0);
}
print_r($newresults);
Demo: http://sandbox.onlinephpfunctions.com/code/065a32b40f67e00aa85f0f5b58ecacd510f2f38a
If you still need to be able to support multiple lines of input, you can do it like this, by just merging the exploded arrays before you process them:
$data = array(
'33287*100*prescription*1,32457*1250*lab*1',
'33222*900*prescription*3,22233*1200*lab*2',
);
$category_name_arr = [];
foreach ($data as $category_name)
{
$category_name_arr = array_merge($category_name_arr, explode(',', $category_name));
}
print_r($category_name_arr);
$newresults=[];
foreach ($category_name_arr as $cat) {
$newresults[] = (object) array("category_name" => $cat, "items_count" => 0);
}
print_r($newresults);
adding $category = new stdClass();
foreach ($category_name as $key) {
$category = new stdClass();
$category->category_name = $key;
$newresults[]=$category;
}

Building a string Concat array with the same ID inside a query loop

i have a query inside a for loop that getting the product name of every array element. Now in every element of my array, i have an ID, where i want to concat all product names with the-same shipping_id.
Here i have my array with values like these:
Array name:id with values of:
Array
(
[0] => Array
(
[product_id] => 1
[shipping_id] => 1
)
[1] => Array
(
[product_id] => 2
[shipping_id] => 1
)
[2] => Array
(
[product_id] => 1
[shipping_id] => 2
)
)
now i made this code with these:
$first = true;
$temp_ship_id = "";
$product_list = "";
foreach ($ids as $product) {
$productname = $this->getproductname($product[0][product_id]);
// if($first) {
// $temp_ship_id = $product[0][shipping_id];
// $first = false;
// }
// if($product[0][shipping_id] == $temp_ship_id) {
// $product_list .= $productname.";
// } else {
// $product_list .= $productname.";
// //$product_list = "";
// $temp_ship_id = $product[0]->shipping_id;
// }
}
public function getproductname($product_id) {
$product = DB::table('products')->select('product_name')
->where(['products.product_id'=>$product_id])
->first();
return $product->product_name;
}
what am i doing is, i am getting the first shipping id and store it and i made a condition if they are thesame then i go concat the productname but, i see my logic is bad.
Please help me in other way. Something like This line of code to begin with:
foreach ($ids as $product) {
$productname = $this->getproductname($product[0][product_id]);
//code for concat goes here
}
public function getproductname($product_id) {
$product = DB::table('products')->select('product_name')
->where(['products.product_id'=>$product_id])
->first();
return $product->product_name;
}
Adjust below to your actual data, let me know if you have questions.
<?php
$concat = array();
$array = array( array( 'product_id'=>1, 'shipping_id'=>1, 'product_name' => 'a' ), array( 'product_id'=>2, 'shipping_id'=>1, 'product_name' => 'b' ), array( 'product_id'=>3, 'shipping_id'=>2, 'product_name' => 'c' ), array( 'product_id'=>4, 'shipping_id'=>1, 'product_name' => 'd' ) );
foreach( $array as $row ) {
if( isset( $concat[ $row['shipping_id'] ] ) ) {
$concat[ $row['shipping_id'] ] .= ',' . $row['product_name'];
} else {
$concat[ $row['shipping_id'] ] .= $row['product_name'];
}
}
var_dump( $concat );
?>

Getting key from returned array

I'm working in WooCommerce, but my question is related to basic PHP foreach statements.
I have a function that returns a multidimensional array. The topmost dimension, the key string is what I need. If you've familiar, $order->get_items();.
The order_item_id is stored as the key like this:
'488' => Array( /* Array stuffs */ )
That 488 is what I need. It wouldn't be a problem if the function I'm working with's foreach statement was:
foreach ($items as $k => $v)
Where I could just use $k, but it's set up as:
foreach ($items as $item)
Is there a way to set the value of the key in the array and pass it along as a parameter?
UPDATE:
Here is the function as it works.
foreach($order_items as $item) {
$product = $order->get_product_from_item($item);
$gc_enabled = get_post_meta($product->id, 'ignite_gift_enabled', true);
if ( ! $gc_enabled)
continue;
$coupon_prefix = get_post_meta($product->id, '_coupon_prefix', true);
if ( ! $coupon_prefix)
$coupon_prefix = '';
for ($x = 1; $x <= $item['qty']; $x++) {
$new_coupon = $this->adjust_voucher( $coupon_prefix, $mode, $msg_details, $order_id, $order_item_id, $product->id );
}
}
$order_items is an array of items, the keys of which I need to pass along to other functions referenced, specificaly adjust_voucher.
Just change the foreach syntax to be foreach($items as $key => $item). This would give you access to $key while not impacting any existing code in the loop which relies on $item.
Here's a quick and dirty approach. Would be expensive if you have a large array -- but otherwise it would work.
$items = $order->get_items();
array_walk($items, function(&$v, $i) { $v['id'] = $i; });
This code adds another key to the array called id and assigns it the current key value.
Requires PHP 5.4 +
Example
$arr = array(
486 => array(
'name' => 'Bob',
),
500 => array(
'name' => 'Sam',
),
);
array_walk($arr, function(&$v, $i) { $v['id'] = $i; });
print_r($arr);
Example Result
Array (
[486] => Array (
[name] => Bob
[id] => 486
)
[500] => Array (
[name] => Sam
[id] => 500
)
)
You can set the values in your array (if needed up the call stack)
I tried that, but there are values that are retrieved with
$item['foo']. I tried setting it to $v['foo'], but it broke.
foreach( $items as $key => &$item ) {
$item['id'] = $key;
/* $item now looks like
* [ 'id' => ...,
* ... ]
*/
} unset( $item ); //Needed otherwise references can become complicated

PHP: Count the appearance of particular value in array

I am wondering if I could explain this.
I have a multidimensional array , I would like to get the count of particular value appearing in that array
Below I am showing the snippet of array . I am just checking with the profile_type .
So I am trying to display the count of profile_type in the array
EDIT
Sorry I've forgot mention something, not something its the main thing , I need the count of profile_type==p
Array
(
[0] => Array
(
[Driver] => Array
(
[id] => 4
[profile_type] => p
[birthyear] => 1978
[is_elite] => 0
)
)
[1] => Array
(
[Driver] => Array
(
[id] => 4
[profile_type] => d
[birthyear] => 1972
[is_elite] => 1
)
)
)
Easy solution with RecursiveArrayIterator, so you don't have to care about the dimensions:
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$counter = 0
foreach ($iterator as $key => $value) {
if ($key == 'profile_type' && $value == 'p') {
$counter++;
}
}
echo $counter;
Something like this might work...
$counts = array();
foreach ($array as $key=>$val) {
foreach ($innerArray as $driver=>$arr) {
$counts[] = $arr['profile_type'];
}
}
$solution = array_count_values($counts);
I'd do something like:
$profile = array();
foreach($array as $elem) {
if (isset($elem['Driver']['profile_type'])) {
$profile[$elem['Driver']['profile_type']]++;
} else {
$profile[$elem['Driver']['profile_type']] = 1;
}
}
print_r($profile);
You may also use array_walk($array,"test") and define a function "test" that checks each item of the array for 'type' and calls recursively array_walk($arrayElement,"test") for items of type 'array' , else checks for the condition. If condition satisfies, increment a count.
Hi You can get count of profuke_type==p from a multi dimensiona array
$arr = array();
$arr[0]['Driver']['id'] = 4;
$arr[0]['Driver']['profile_type'] = 'p';
$arr[0]['Driver']['birthyear'] = 1978;
$arr[0]['Driver']['is_elite'] = 0;
$arr[1]['Driver']['id'] = 4;
$arr[1]['Driver']['profile_type'] = 'd';
$arr[1]['Driver']['birthyear'] = 1972;
$arr[1]['Driver']['is_elite'] = 1;
$arr[2]['profile_type'] = 'p';
$result = 0;
get_count($arr, 'profile_type', 'd' , $result);
echo $result;
function get_count($array, $key, $value , &$result){
if(!is_array($array)){
return;
}
if($array[$key] == $value){
$result++;
}
foreach($array AS $arr){
get_count($arr, $key, $value , $result);
}
}
try this..
thanks

php array_multisort() by 2 field

I use array_multisort() function to sort an array by a field value. what I need is to sort it by 2 field values, by date and by time.
here is the structure of the array:
Array
(
[0] => Array
(
[id_art] => 5292
[free_art] => 0
[apero_art] => 0
[name_cat] => Teatro
[date_dat] => 2010-11-24
[date2_dat] => 0000-00-00
[name_spa] => Cinema Teatro
[title_int] => Il piacere dell'onestÃ
[id_cat] => 2
[time_tim] => 20:30:00
[intro_int] => Una produzione Teatro Eliseo di Roma - ChiassoCultura
[image_art] => noimage.png
)
[1] => Array
(
[id_art] => 4983
[free_art] => 0
[apero_art] => 0
[name_cat] => Cinema
[date_dat] => 2011-04-20
[date2_dat] => 2011-04-20
[name_spa] => Cinema Morettina
[title_int] => Inland Empire
[id_cat] => 1
[time_tim] => 20:30:00
[intro_int] => Rassegna dedicata a David Lynch
[image_art] => noimage.png
)
[2] => Array
(
[id_art] => 4983
[free_art] => 0
[apero_art] => 0
[name_cat] => Cinema
[date_dat] => 2011-04-22
[date2_dat] => 2011-04-22
[name_spa] => Cinema Iride
[title_int] => Inland Empire
[id_cat] => 1
[time_tim] => 17:00:00
[intro_int] => Rassegna dedicata a David Lynch
[image_art] => noimage.png
)....
What I do now is:
function array_sort_by_column(&$arr, $col, $dir = SORT_ASC) {
$sort_col = array();
foreach ($arr as $key=> $row) {
$sort_col[$key] = $row[$col];
}
array_multisort($sort_col, $dir, $arr);
}
How can I easily make in one time the double sort?
Thanks.
If I understand what you need to do, you can use usort:
usort($array, function($a, $b) {
if ($a['date_cat'] > $b['date_cat']) return 1;
if ($a['date_cat'] < $b['date_cat']) return -1;
if ($a['time_tim'] > $b['time_tim']) return 1;
if ($a['time_tim'] < $b['time_tim']) return -1;
return 0;
});
The way your function works it a bit twisty, to me.
You extract a column of your array to create an array to sort linked to the original array. It took several readings and a php manual check to understand your code (too bad), you should have (to me) detach the creation of the index array into another function prior than calling the sort.
If you need to have dynamic colums selection maybe you can leverage on closures (PHP 5.3+ ?):
function your_sort($orderby, $array) {
return usort($array, function ($a, $b) use ($orderby) {
foreach($orderby as $field) {
if ($a[$field] > $b[$field]) return 1;
if ($a[$field] < $b[$field]) return -1;
}
return 0;
});
}
And then you can call the function like this:
your_sort(array('date_cat','time_tim'), $array);
Usign some code from php.net comment and other adjustment, I wrote a function that accept these argument:
$a = array
$orderby = an order string written in sql language
$children_key = name of the eventual column where eventual children node are saved
Here the function:
function array_multiorderby( $data, $orderby, $children_key=false )
{
// parsing orderby
$args = array();
$x = explode( ' ', str_replace( ',', ' ', $orderby ) );
foreach( $x as $item )
{
$item = trim( $item );
if( $item=='' ) continue;
if( strtolower($item)=='asc' ) $item = SORT_ASC;
else if( strtolower($item)=='desc' ) $item = SORT_DESC;
$args[] = $item;
}
// order
foreach ($args as $n => $field)
{
if (is_string($field))
{
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
$data = array_pop($args);
// order children
if( $children_key )
{
foreach( $data as $k=>$v ) if( is_array($v[$children_key]) ) $data[$k][$children_key] = array_multiorderby( $v[$children_key], $children_key, $orderby );
}
// return
return $data;
}
Application to your specific case:
$array = array_multiorderby( $array, "date_dat DESC, time_tim DESC" );

Categories