I'm not so strong with arrays but I need to determine how to count the number of parents a child array has in order to determine the indenting to display it as an option in a SELECT.
So, if I have this array:
array(
'World'=>array(
'North America'=>array(
'Canada'=>array(
'City'=>'Toronto'
)
)
)
);
How would I go about determining how many parents 'City' has in order to translate that into the number of spaces I want to use as an indent?
Thanks for any help.
EDIT: Let's see if I can explain myself better:
I have this code I'm using to build the OPTIONS list for a SELECT:
function toOptions($array) {
foreach ($array as $key=>$value) {
$html .= "<option value=\"" . $key . "\" >";
$html .= $value['title'];
$html .= "</option>";
if (array_key_exists('children', $value)) {
$html .= toOptions($value['children']);
}
}
return $html;
}
print toOptions($list);
So, I'm trying to determine how to get the number of parents in order to add spaces before the title in this line:
$html .= $value['title'];
Like:
$html .= " " . $value['title'];
But, I'm not sure how to figure out how many spaces to add.
Hopefully this is more clear.
Thanks for any help so far.
$x = array(
'World'=>array(
'North America'=>array(
'Canada'=>array(
'City'=>'Toronto'
)
)
)
);
// This function do something with the key you've found in the array
function visit($name, $depth)
{
echo $name . ' has ' . $depth . ' parents.';
}
// This function visits all the contents aff $array
function find_recursive($array, $depth = 0)
{
if (is_array($array)) {
foreach ($array as $k => $value) {
visit($k, $depth + 1);
find_recursive($array, $depth + 1);
}
}
}
For visiting:
find_recursive($x);
Well. Off the top what you are dealing with is a multi dimensional array.
You could run a count w/ foreach on each level of the array, and use the count number returned +1 for each level the foreach loops through.
I'm not sure if this answers your question, but I am trying to see exactly what it is you are trying to achieve.
As you are already using a recursive function to display that data, you can just extend your function. There is no need to traverse the array more often than one time:
function getWhitespaces($count) {
$result = '';
while($count--) {
$result .= '$nbsp;';
}
return $result;
}
function toOptions($array, $level=0) {
foreach ($array as $key=>$value) {
$html .= "<option value=\"" . $key . "\" >";
$html .= getWhitespaces($level) + $value['title'];
$html .= "</option>";
if (array_key_exists('children', $value)) {
$html .= toOptions($value['children'], $level + 1);
}
}
return $html;
}
print toOptions($list);
Try the following.. Your solution screams for recursion in my mind. Its a bit ugly but it seems to work
$totraverse = array(
'Moon' => array(
'Dark Side' => "Death Valley"
),
'Halley Commet' => "Solar System",
'World' => array(
'North America' => array(
'Canada' => array(
'City' => 'Toronto'
)
), 'South America' => array(
'Argentina' => array(
'City' => 'Toronto'
)
)
)
);
function traverse($totraverse_, $path="", $count=0) {
global $array;
// echo count($totraverse_) . " count\n";
if (!is_array($totraverse_)) {
echo "returning $path and $key\n";
return array($path, $count);
} else {
foreach ($totraverse_ as $key => $val) {
echo "assting $path and $key\n";
$result = traverse($val, $path . "/" . $key, $count + 1);
if($result){
$array[]=$result;
}
}
}
echo false;
}
$array = array();
traverse($totraverse);
foreach($array as $item){
echo "{$item[0]}--->{$item[1]}\n";
}
Related
I've got this array:
Array
(
[0] => Array
(
[name] => System
[order] => 1
[icon] => stats.svg
[0] => Array
(
[title] => Multilingual
)
[1] => Array
(
[title] => Coloring
)
[2] => Array
(
[title] => Team work
)
[3] => Array
(
[title] => Tutorials
)
)
)
I want to loop into this to show the section name and after all the features containing in the following array.
So, this is what I made:
foreach ($features as $feature => $info) {
echo '
'.$info['name'].'
<ul class="menu-vertical bullets">
';
foreach (array_values($info) as $i => $key) {
echo '
<li>'.$key['title'].'</li>
';
}
echo '
</ul>
';
}
It works except for the first third <li> where I have the first char of name, order and icon value.
Do you know why ?
Thanks.
array_values return value of array so for info values is name, order, icon, 0, 1, ...
Your values foreach is wrong if you just want print title you can use:
foreach ($features as $feature => $info) {
echo '
'.$info['name'].'
<ul class="menu-vertical bullets">
';
//Remove some keys from info array
$removeKeys = array('name', 'order', 'icon');
$arr = $info;
foreach($removeKeys as $key) {
unset($arr[$key]);
}
foreach (array_values($arr) as $i => $key) {
echo '
<li>'.$key['title'].'</li>
';
}
echo '
</ul>
';
}
In php, array_values means all the values of the array. So array_values($info) is array($info['name'], $info['order'], $info['icon'], $info[0], $info[1], $info[2], $info[3])
in your example, you can skip the non-integer keys of the $info to get your titles:
<?php
$features = array();
$info = array();
$info['name'] = 'System';
$info['order'] = 1;
$info['icon'] = 'stats.svg';
$info[] = array('title'=>'Multilingual');
$info[] = array('title'=>'Coloring');
$features[] = $info;
foreach ($features as $feature => $info) {
echo $info['name'] . PHP_EOL;
echo '<ul class="menu-vertical bullets">' . PHP_EOL;
foreach ($info as $k => $item) {
if(!is_int($k)) continue;
echo '<li>' . $item['title'] . '</li>' . PHP_EOL;
}
echo '</ul>' . PHP_EOL;
}
BUT, your original data structure is not well designed and hard to use. For a better design, you can consider the following code, move your items to a sub array of $info:
<?php
$features = array();
$info = array();
$info['name'] = 'System';
$info['order'] = 1;
$info['icon'] = 'stats.svg';
$info['items'] = array();
$info['items'][] = array('title'=>'Multilingual');
$info['items'][] = array('title'=>'Coloring');
$features[] = $info;
foreach ($features as $feature => $info) {
echo $info['name'] . PHP_EOL;
echo '<ul class="menu-vertical bullets">' . PHP_EOL;
foreach ($info['items'] as $item) {
echo '<li>' . $item['title'] . '</li>' . PHP_EOL;
}
echo '</ul>' . PHP_EOL;
}
Sample output of the two demos:
System
<ul class="menu-vertical bullets">
<li>Multilingual</li>
<li>Coloring</li>
</ul>
It works except for the first third li where I have the first char of name, order and icon value. Do you know why ?
Why you see first chars of the values of 'name', 'order', 'icon'? Let see how PHP works.
Take the first loop as an example: foreach (array_values($info) as $i => $key)
Then $i == 0, $key == 'System'
We know that $key[0] == 'S', $key[1] == 'y', $key[2] == 's', etc.
Then you try to access $key['title'], but the string 'title' is not valid as a string offset, so it is converted to an integer by PHP: intval('title') == 0.
Then $key['title'] == $key[intval('title')] == 'S'
That's what you see.
array_value() returns the values of the array, here you will get the value of the array $info and what I understand is that is not what you need. See details for array_value().
You can check if the key for the $info is an integer. if yes, echo the title. Give this a try.
foreach ($features as $feature => $info) {
echo $info['name'].'<ul class="menu-vertical bullets">';
foreach ($info as $key => $value) {
if (is_int($key)) {
echo '<li>'.$key['title'].'</li>';
}
}
echo '</ul>';
}
I'm trying to build a tree-map from categories.
I have the categories (I have a lot of categories and I want to remove duplicates and show them in a tree-map view)
$cat = array(
"Sneakers/Men",
"Sneakers/Women",
"Accessories/Jewellery/Men",
"Accessories/Jewellery/Women",
"Accessories/Jewellery/Men
");
...and I want them like this
$categories = array(
"Sneakers" => array(
"Men" => array(),
"Women" => array()
),
"Accessories" => array(
"Jewellery" => array(
"Men" => array(),
"Women" => array()
)
)
);
to print them like this
- Sneakers
-- Men
-- Women
- Accessories
-- Jewellery
--- Men
--- Women
Try this:
<?php
$cat = array(
"Sneakers/Men",
"Sneakers/Women",
"Accessories/Jewellery/Men",
"Accessories/Jewellery/Women",
"Accessories/Jewellery/Men
");
function buildTree($categories, $result = []){
$temp = [];
foreach($categories as $categoryString){
$catParts = explode('/',$categoryString);
if(count($catParts) > 1){
$temp[$catParts[0]][] = str_replace($catParts[0].'/','',$categoryString);
} else {
$temp[$catParts[0]] = [];
}
}
foreach($temp as $elemName => $elemVal){
$result[$elemName] = buildTree($elemVal);
}
return $result;
}
var_dump(buildTree($cat));
The most simple way is to use references, like this:
$out = [];
foreach ($cat as $str) {
$lookup =& $out;
foreach (explode("/", $str) as $part) {
$lookup =& $lookup[$part];
if (!isset($lookup)) {
$lookup = [];
}
}
}
$lookup initially refers to the whole expected result, then the reference is extended at each step to follow the path of nested members.
Note that each new member added looks like member-name => [], so that actually even final leaves are arrays: it may seem a bit weird, but is a pretty way to have a reduced code (each member is always ready to receive children).
And it's not a difficulty, though, to use the resulting array to then print it like the OP asked:
function nest_print($src, $level = 0) {
$prefix = '<br />' . str_repeat('- ', ++$level);
foreach ($src as $key => $val) {
echo $prefix . $key;
if ($val) {
nest_print($val, $level);
}
}
}
nest_print($out);
EDIT
Here is an alternate solution, including the count of final leaves, as asked by the OP in his comment:
$out = [];
foreach ($cat as $str) {
$lookup =& $out;
$parts = explode("/", $str);
foreach ($parts as $part) {
$lookup =& $lookup[$part];
if (!isset($lookup)) {
$lookup = [];
}
// when $part is a final leaf, count its occurrences
if ($part == end($parts)) {
$lookup = is_array($lookup) ? 1 : ++$lookup;
}
}
}
(might likely be improved in a more elegant way, though)
And here is how to modify the print-result snippet accordingly:
function nest_print($src, $level = 0) {
$prefix = '<br />' . str_repeat('- ', ++$level);
foreach ($src as $key => $val) {
echo $prefix . $key;
if (is_array($val)) {
nest_print($val, $level);
} else {
echo ': ' . $val;
}
}
}
nest_print($out);
First assumption: Assume, we defined a variable (its name is $tmp) in a function(functioin name is 'ExpMenu') for temporary calculating and in end of function we return this variable.
Second assumption: Assume, we call that function recursively for create a navigation menu base on a multidimensional array.
My question is about scope of that variable ($tmp). In every call funtion, will its value overwritten? In other words, by every function call we lose previous value?
For more detail, please review below code:
/// --- { Declaration Block
$content = array(
array(
'level'=>'1',
'order'=>'1',
'text'=>'New Solution WorkFlow',
'is_parent'=>'yes',
'child'=> array(
array(
'level'=>'2',
'order'=>'1',
'text'=>'Define New Solution',
'is_parent'=>'no',
'url'=>'#'
),
array(
'level'=>'2',
'order'=>'2',
'text'=>'View Solutions',
'is_parent'=>'no',
'url'=>'#'
),
array(
'level'=>'2',
'order'=>'3',
'text'=>'View Solutions',
'is_parent'=>'no',
'url'=>'#'
)
)
),
array(
'level'=>'1',
'order'=>'2',
'text'=>'Solution Modify WorkFlow',
'is_parent'=>'yes',
'child'=> array(
array(
'level'=>'2',
'order'=>'1',
'text'=>'Request For Solution Modify',
'is_parent'=>'no',
'url'=>'#'
)
)
),
array(
'level'=>'1',
'order'=>'3',
'text'=>'Solution Close WorkFlow',
'is_parent'=>'yes',
'child'=> array(
array(
'level'=>'2',
'order'=>'1',
'text'=>'Declare For Solution Close',
'is_parent'=>'no',
'url'=>'#'
)
)
)
);
function ExpMenu($item_array ) {
$tmp='';
foreach ($item_array as $item) {
if ($item['is_parent']=='yes') {
$tmp = '<li class="hasChild">' . $item["text"] . '<ul>';
$tmp .= ExpMenu($item['child']);
$tmp .= '</ul></li>';
} else {
$tmp = '<li>';
$tmp .= ''. $item['text'] . '' ;
$tmp .= '</li>';
}
}
return $tmp;
}
/// --- }
$menu='<div><ul>';
$menu .= ExpMenu($content);
$menu.='</ul></div>';
echo $m . '<br />';
It seams by every call function we lose pervious value.
I thank #l0rkaY for her/him solution, But I found another solution that doesn't need add new parameter in my function.
Because $tmp scope is in 'ExpMenu' function and we call recursively function, therefore variable still alive and wasn't terminated.
So, I modify my function a bit:
function ExpMenu($item_array ) {
$tmp='';
foreach ($item_array as $item) {
if ($item['is_parent']=='yes') {
$tmp .= '<li class="hasChild">' . $item["text"] . '<ul>';
$tmp .= ExpMenu($item['child']);
$tmp .= '</ul></li>';
} else {
$tmp .= '<li>';
$tmp .= ''. $item['text'] . '' ;
$tmp .= '</li>';
}
}
return $tmp;
}
I assume your actual problem is, that your function generates only single item with a single child item.
It's because you are overwriting your previous item in your if/else blocks. That's why you only get the last item.
You just need to concatenate them to the existing items.
function ExpMenu($item_array ) {
$tmp='';
foreach ($item_array as $item) {
if ($item['is_parent']=='yes') {
$tmp .= '<li class="hasChild">' . $item["text"] . '<ul>';
$tmp .= ExpMenu($item['child']);
$tmp .= '</ul></li>';
} else {
$tmp .= '<li>';
$tmp .= ''. $item['text'] . '' ;
$tmp .= '</li>';
}
}
return $tmp;
}
This is an array i have
<?php
$page['Home']='index.html';
$page['Service']='services.html';
?>
How do i get to echo something like this for individual one like
Home is at index.html
and again how can i do this through a loop and echo all?
foreach($page as $key => $value) {
echo "$key is at $value";
}
For 'without loop' version I'll just ask "why?"
Without a loop, just for the kicks of it...
You can either convert the array to a non-associative one, by doing:
$page = array_values($page);
And then acessing each element by it's zero-based index:
echo $page[0]; // 'index.html'
echo $page[1]; // 'services.html'
Or you can use a slightly more complicated version:
$value = array_slice($page, 0, 1);
echo key($value); // Home
echo current($value); // index.html
$value = array_slice($page, 1, 1);
echo key($value); // Service
echo current($value); // services.html
If you must not use a loop (why?), you could use array_walk,
function printer($v, $k) {
echo "$k is at $v\n";
}
array_walk($page, "printer");
See http://www.ideone.com/aV5X6.
Echo key and value of an array without and with loop
$array = array(
'kk6NFKK'=>'name',
'nnbDDD'=>'claGg',
'nnbDDD'=>'kaoOPOP',
'nnbDDD'=>'JWIDE4',
'nnbDDD'=>'lopO'
);
print_r(each($array));
Output
Array
(
[1] => name
[value] => name
[0] => kk6NFKK
[key] => kk6NFKK
)
for the first question
$key = 'Home';
echo $key." is at ".$page[$key];
function displayArrayValue($array,$key) {
if (array_key_exists($key,$array)) echo "$key is at ".$array[$key];
}
displayArrayValue($page, "Service");
How to echo key and value of an array without and with loop
$keys = array_keys($page);
implode(',',$keys);
echo $keys[0].' is at '.$page['Home'];
My version without a loop would be like this:
echo implode(
"\n",
array_map(
function ($k, $v) {
return "$k is at $v";
},
array_keys($page),
array_values($page)
)
);
array_walk($v, function(&$value, $key) {
echo $key . '--'. $value;
});
Learn more about array_walk
A recursive function for a change;) I use it to output the media information for videos etc elements of which can use nested array / attributes.
function custom_print_array($arr = array()) {
$output = '';
foreach($arr as $key => $val) {
if(is_array($val)){
$output .= '<li><strong>' . ucwords(str_replace('_',' ', $key)) . ':</strong><ul class="children">' . custom_print_array($val) . '</ul>' . '</li>';
}
else {
$output .= '<li><strong>' . ucwords(str_replace('_',' ', $key)) . ':</strong> ' . $val . '</li>';
}
}
return $output;
}
You can try following code:
foreach ($arry as $key => $value)
{
echo $key;
foreach ($value as $val)
{
echo $val;
}
}
print "<ul>";
foreach ($arr as $value) {
echo("<li>" . $value[storeid] . " " . ($value[dvdstock] + $value[vhsstock]) . "</li>");
}
print "</ul>";
Will output
•2 20
•2 10
•1 20
•1 20
•1 10
I was wondering how I would adapt this loop so it outputs the total values for each &value[storeid]
•1 50
•2 30
Thanks very much :)
Use another array to calculate the values you want:
// setup a quick place to store the data
$stores = array();
foreach ($arr as $value) {
if(!isset($stores[$value['storeid']])){ // init check required to avoid Notices
$stores[$value['storeid']] = $value['dvdstock'] + $value['vhsstock'];
}else{
$stores[$value['storeid']] += $value['dvdstock'] + $value['vhsstock'];
}
}
ksort($stores); // sort by storeid ASC
print "<ul>";
// loop through the new data
foreach ($stores as $id => $value) {
echo("<li>" . $id . " " . ($value) . "</li>");
}
print "</ul>";
Demo Link
If you are getting the data from an SQL database then you should do this using SUM() functions in the SQL as it is more efficient. If the data source is from somewhere else you should do something like this:
//Sum data
foreach ($arr as $value) {
if (!isset($sums[$value['storeid']])) { // init check required to avoid Notices
$sums[$value['storeid']] = $value['dvdstock'] + $value['vhsstock'];
} else {
$sums[$value['storeid']] += $value['dvdstock'] + $value['vhsstock'];
}
}
ksort($sums); // sort by storeid ASC
print "<ul>";
foreach ($sums as $key => $sum) {
echo("<li>$key $sum</li>");
}
print "</ul>";
Demo Link
It is a for loop and you have to make two of them. First to compute the sum and then to iterate over these values:
$data = array();
foreach ($arr as $value) {
if (!isset($data[$value['storeid']])) { // init check required to avoid Notices
$data[$value['storeid']] = $value['dvdstock'] + $value['vhsstock'];
} else {
$data[$value['storeid']] += $value['dvdstock'] + $value['vhsstock'];
}
}
ksort($data); // sort by storeid ASC
print "<ul>";
foreach ($data as $storeid => $sum) {
echo('<li>' . $storeid . ' ' . ($sum) . '</li>');
}
print "</ul>";
Demo Link
Btw one word about strings:
Either use single quotes ' with concatenation .: 'foo: ' . $bar.
Or double quotes " and put the variables inside the string: "foo: $bar".
<?php
$sums = array();
foreach ($arr as $value)
{
$sums[$value['storeid']] += $value['dvdstock'];
}
print_r($sums);
?>
Correct version:
<?php
$initial = array (
array (
'id' => 1,
'amount' => 10
),
array (
'id' => 1,
'amount' => 10
),
array (
'id' => 2,
'amount' => 20
),
array (
'id' => 2,
'amount' => 20
),
array (
'id' => 2,
'amount' => 20
),
);
$result = array ();
foreach ($initial as $value) {
$result[$value['id']] += $value['amount'];
}
print_r($result);
?>