php - nav/subnav structure from array - php

I'm trying to create a "plugin" like script for adding to a "menu array".... I'll go straight in..
Lets say I initiate a nav item like so:
$sections->add_section('dashboard');
$DashBoardSection = $sections->load_section('dashboard');
$DashBoardSection->set_role(NULL);
$DashBoardSection->set_nav_item(array(
'identifier' => 'dashboard',
'text' => 'Dashboard',
'url' => NULL,
'position' => NULL
));
starts off by creating a new section and the getting the instance of.
We are then setting a "role" in which we will test against to see if the user is authenticated as being verified to view.
The set nav item simply stores the array. identifier is a reference to the item ( its for when we want to add sub items), all standard apart from "position" which states where it is to sit in the nav i.e. NULL is top level, array('topnav','subnav') will be topnav->subnav->dashboard.
as a test, this could be stored as follows:
Array
(
[0] => Array
(
[identifier] => dashboard
[text] => Dashboard
[url] =>
[position] =>
)
[1] => Array
(
[identifier] => dashboard2
[text] => Dashboard2
[url] =>
[position] => Array
(
[0] => dashboard
)
)
)
my question being how I turn that into the following structure:
Array
(
[0] => Array
(
[identifier] => dashboard
[text] => Dashboard
[url] =>
[position] =>
[children] => Array
(
[0] => Array
(
[identifier] => dashboard2
[text] => Dashboard2
[url] =>
)
)
)
)
pulling my hair out over this one, any help would be very much appreciated.
regards
I currently have
public function build_navigation( $role ){
$role = (int)$role;
$nav = array();
foreach( $this->sections as $section ) {
if( $section->get_role() === NULL || $section->get_role() === $role ) {
$nav_array = $section->get_nav();
foreach( $nav_array as $key => $nav_item ) {
if( $nav_item['position'] === NULL ) {
$nav[$nav_item['identifier']] = $nav_item;
}elseif( is_array( $nav_item['position'] ) ){
#...#
}
}
}
}
return $nav;
}
EDIT
Imagine this is the array given (it can be in any order)
Array
(
[0] => Array
(
[identifier] => dashboard_child2
[text] => Dashboard Child 2
[url] =>
[position] => Array
(
[0] => dashboard
)
)
[1] => Array
(
[identifier] => dashboard_child_child_1
[text] => Dashboard Child Child 1
[url] =>
[position] => Array
(
[0] => dashboard
[1] => dashboard_child1
)
)
[2] => Array
(
[identifier] => dashboard_child1
[text] => Dashboard Child 1
[url] =>
[position] => Array
(
[0] => dashboard
)
)
[3] => Array
(
[identifier] => dashboard
[text] => Dashboard
[url] =>
[position] =>
)
[4] => Array
(
[identifier] => dashboard2
[text] => Dashboard2
[url] =>
[position] => Array
(
[0] => dashboard
)
)
)
Which needs to be formatted as:
Array
(
[dashboard] => Array
(
[text] => Dashboard
[url] =>
[children] => Array
(
[dashboard_child2] => Array
(
[text] => Dashboard Child 2
[url] =>
)
[dashboard_child1] => Array
(
[text] => Dashboard Child 1
[url] =>
[children] => Array
(
[dashboard_child_child_1] => Array
(
[text] => Dashboard Child Child 1
[url] =>
)
)
)
[dashboard2] => Array
(
[text] => Dashboard2
[url] =>
)
)
)
)

Here's my take on the problem, solved with recursion.
You can use multiple positions (i guess this is why it's an array), it will ignore missing positions if at least on of the positions is found, but will complain if every position is missing.
function translate($in) {
$out = array();
// first pass, move root nodes to output
foreach ($in as $k => $row) {
if (!$row['position']) {
$out[$row['identifier']] = $row;
unset($in[$k]);
}
}
// while we have input
do {
$elements_placed = 0;
// step trough input
foreach ($in as $row_index => $row) {
foreach ($row['position'] as $pos) {
// build context for the node placing
$data = array(
'row' => $row,
'in' => &$in,
'row_index' => $row_index,
'elements_placed' => &$elements_placed,
'pos' => $pos,
);
// kick of recursion
walker($out, $data);
}
}
} while ($elements_placed != 0);
if (count($in)) {
trigger_error("Error in user data, can't place every item");
}
return $out;
}
function walker(&$out, $data) {
foreach ($out as &$row) {
// it looks like a node array
if (is_array($row) && isset($row['identifier'])) {
// if it has children recurse in there too
if (isset($row['children'])) {
walker($row['children'], $data);
}
// it looks like a node array that we are looking for place the row
if ($row['identifier'] == $data['pos']) {
if (!isset($row['children'])) {
$row['children'] = array($data['row']['identifier'] => $data['row']);
} else {
$row['children'][$data['row']['identifier']] = $data['row'];
}
// report back to the solver that we found a place
++$data['elements_placed'];
// remove the row from the $in array
unset($data['in'][$data['row_index']]);
}
}
}
}
$in = array (
array (
'identifier' => 'secondlevelchild2',
'text' => 'secondlevelchild2',
'url' => '',
'position' => array (
'dashboard2',
),
),
array (
'identifier' => 'secondlevelchild',
'text' => 'secondlevelchild',
'url' => '',
'position' => array (
'dashboard2',
),
),
array (
'identifier' => 'dashboard',
'text' => 'Dashboard',
'url' => '',
'position' => '',
),
array (
'identifier' => 'dashboard2',
'text' => 'Dashboard2',
'url' => '',
'position' => array (
'dashboard', 'home',
),
),
array (
'identifier' => 'thirdlevelchild',
'text' => 'thirdlevelchild',
'url' => '',
'position' => array (
'secondlevelchild2',
),
),
);
$out = translate($in);
var_export($out);
In it's current form it doesn't remove the identifier or position keys from the node arrays once they are placed.

You need a temporary array that maps the identifier to the children array of it so that you can add it there.
If I see that right, you have something like that already here when you add the parent:
$nav[$nav_item['identifier']] = $nav_item;
Edit: Adding the parent needs some caution as pointed out in the comment:
$node = &$nav[$nav_item['identifier']];
$children = isset($node['children']) ? $node['children'] : array();
$node = $nav_item;
$node['children'] = $children;
unset($node);
Just add to the children then:
foreach($nav_item['position'] as $identifier)
{
$nav[$identifier]['children'][] = $nav_item;
}
Also you could use objects, not arrays, so that you do not duplicate data that much (or you can change it later), however, just thinking, this must not be necessary to solve your problem so probably something for later.

Related

PHP multi-dimensional associative array conversion (URL folder structure as hierarchical array)

I'm twisting my brain trying to convert a multi-dimensional associative array into a multi-dimensional non-associative one and got to the point where I decided to seek help..
The whole story:
A. I've got a set of URLs and their KPIs (with an arbitrary folder depth):
URL, Visits
www.example.com
www.example.com/resource1, 100
www.example.com/folderA/resource2, 200
www.example.com/folderA/resource3, 300
www.example.com/folderB/resource4, 400
B. With help of StackOverflow (and surprisingly few lines of PHP -> see here) I was able to transform this into a hierarchical array, representing the URL-structure as folders:
Array
(
[www.example.com] => Array
(
[folderA] => Array
(
[resource2] => Array
(
[visits] => 200
)
[resource3] => Array
(
[visits] => 300
)
)
[folderB] => Array
(
[resource4] => Array
(
[visits] => 400
)
)
[resource1] => Array
(
[visits] => 100
)
)
)
C. Now, however, I need to get this array into the following structure (children must be non-associative arrays) which is a real brain twister to me...
Array
(
[name] => www.example.com
[isFolder] => 1
[children] => Array
(
[0] => Array
(
[name] => folderA
[isFolder] => 1
[children] => Array
(
[0] => Array
(
[name] => resource2
[isFolder] => 0
[kpis] => Array
(
[visits] => 200
)
[children] => Array
(
)
)
[1] => Array
(
[name] => resource3
[isFolder] => 0
[kpis] => Array
(
[visits] => 300
)
[children] => Array
(
)
)
)
)
[1] => Array
(
[name] => folderB
[isFolder] => 1
[children] => Array
(
[0] => Array
(
[name] => resource4
[isFolder] => 0
[kpis] => Array
(
[visits] => 400
)
[children] => Array
(
)
)
)
)
[2] => Array
(
[name] => resource1
[isFolder] => 0
[kpis] => Array
(
[visits] => 100
)
[children] => Array
(
)
)
)
)
Can anyone help with an approach how to achieve this? Either by transforming the array from step B. or starting from scratch with the original URLs from step A...
Any help is greatly appreciated!
Thanks a lot!
Below is a little function that will do just what you want. It uses a reference that is moved through the tree and adds data to the array referenced by that reference where necessary.
I hope the comments give enough explanation of what the code does.
$sourceArray = [
'www.example.com' => 0,
'www.example.com/resource1' => 100,
'www.example.com/folderA/resource2' => 200,
'www.example.com/folderA/resource3' => 300,
'www.example.com/folderB/resource4' => 400,
];
function convertToStructure($sourceArray)
{
// Initialize the tree
$tree = [
'name' => 'root',
'isFolder' => 1,
'children' => [],
];
// Do nothing if sourceArray is not an array
if(!is_array($sourceArray)){
return $tree;
}
// Loop through the source array
foreach($sourceArray as $path => $visits){
// Get a reference to the target tree
$treeReference = &$tree;
// Split the path into pieces
$path = explode('/', $path);
// For each piece:
foreach($path as $key => $name){
// Get the childKey if the child already exists
$childKey = findChild($treeReference, $name);
// Create a new child otherwise
if ($childKey === false) {
$treeReference['children'][] = [
'name' => $name,
'isFolder' => 0,
'children' => [],
];
// Get the key of the new child item
end($treeReference['children']);
$childKey = key($treeReference['children']);
}
// Change the reference to the correct child item.
$treeReference = &$treeReference['children'][$childKey];
// Set isFolder if necessary
$isFolder = ($key == count($path) - 1 ? 0 : 1);
if(!empty($isFolder)){
$treeReference['isFolder'] = $isFolder;
}
// Set visits if necessary
if(!empty($visits)){
$treeReference['isFolder'] = ['kpis' => ['visits' => $visits]];
}
}
}
return $tree;
}
function findChild($tree, $name)
{
if(empty($tree['children'])){
return false;
}
foreach($tree['children'] as $key => $child){
if($child['name'] == $name){
return $key;
}
}
return false;
}
$treeStructure = convertToStructure($sourceArray);

Counting multiple values from a query

I'm getting an array of this type from the database: (I can't change the type of data received, as it comes from an outside source)
Array
(
[0] => Es\Result Object
(
[_hit:protected] => Array
(
[_index] => website
[_type] => structure
[_id] => 8
[_score] => 0.625
[_source] => Array
(
[name] => Test1
[locality] => Array
(
[locality] => Bologna
[sign] => BO
[province] => Array
(
[province] => Bologna
[region] => Array
(
[region] => Emilia Romagna
)
)
)
)
)
)
[1] => Elastica\Result Object
(
[_hit:protected] => Array
(
[_index] => website
[_type] => structure
[_id] => 6
[_score] => 0.625
[_source] => Array
(
[name] => Test2
[locality] => Array
(
[locality] => Bologna
[sign] => BO
[province] => Array
(
[province] => Bologna
[region] => Array
(
[region] => Emilia Romagna
)
)
)
)
)
)
......
I enter into a json object value of "locality" and count how many "locality" are equal, for example:
foreach ($results as $result) {
$source = $result->getSource();
$locality = $source['locality']['locality'];
$dataLocality[] = array(
'type' => 'locality',
'label' => $locality,
'count' => 1, //How to increase the count here?
);
}
I tried to put everything in a loop but I can't count the values ​​correctly.
Is there a way to do this?
EDIT
This should be the result that I get:
let's say I get the array has 3 "locality":
2 "foo"
1 "bar"
$dataLocality =
Array
(
[0] => Array
(
[type] => 'locality'
[locality] => 'foo'
[count] => 2
)
[1] => Array
(
[type] => 'locality'
[locality] => 'bar'
[count] => 1
)
)
Does the value of $locality exist in the array? If no, then store it with a count of 1. If yes, then add 1 to the count.
foreach ($results as $result) {
$source = $result->getSource();
$locality = $source['locality']['locality'];
// Manually specify the array key. This makes life much easier down the road,
// especially when you need to find items in the array.
if ( !isset($dataLocality[$locality]) ) {
$dataLocality[$locality] = array('type' => 'locality',
'label' => $locality,
'count' => 1);
} else {
$dataLocality[$locality]['count'] += 1;
}
}
Declare an array (say $dataLocality) outside your foreach loop. In each loop you search for an existant locality in $datalocality (if $result is in $dataLocality). If there is one, you increment the counter by $dataLocality[$indexOfFounding]['count']++.
This off course requires the adding of your array of 'type', 'label' and 'count' if you don't find any locality in $dataLocality.
After the foreach loop you can go through your array and output or do anything with your results.
If you are needing the count of locality array then make use of count Try:
foreach ($results as $result) {
$source = $result->getSource();
$locality = $source['locality']['locality'];
$dataLocality[] = array(
'type' => 'locality',
'label' => $locality,
'count' => count($source['locality']), //get locality array count
);
}
Thanks to #Dave's reply I solved the problem, but since I could not have named keys, I added outside the loop:
$dataLocality = array_values($dataLocality);
so the complete code to solve the problem, but on occasions someone had a problem similar to mine is:
foreach ($results as $result) {
$source = $result->getSource();
if(!isset($dataLocality[$locality = $source['locality']['locality']])) {
$dataLocality[$locality] = array(
'type' => 'locality',
'label' => $locality,
'count' => 1,
);
} else {
$dataLocality[$locality]['count']++;
}
}
$dataLocality = array_values($dataLocality);

How to combine same name with its value in array

hi I have array like this
Array
(
[0] => stdClass Object
(
[name] => text_input
[value] => kalpit
)
[1] => stdClass Object
(
[name] => wpc_chkbox[]
[value] => Option two
)
[2] => stdClass Object
(
[name] => wpc_chkboxasdf[]
[value] => Option one
)
[3] => stdClass Object
(
[name] => wpc_chkboxasdf[]
[value] => Option two
)
[4] => stdClass Object
(
[name] => wpc_inline_chkbox[]
[value] => 1
)
[5] => stdClass Object
(
[name] => wpc_inline_chkbox[]
[value] => 2
)
[6] => stdClass Object
(
[name] => wpc_inline_chkbox[]
[value] => 3
)
[7] => stdClass Object
(
[name] => wpc_radios
[value] => Option one
)
)
now my question is how to combine same name value in onc place, here in above array I have wpc_inline_checkbox[] is repeating 3 times so I want to make it .. I can use array_uniqe() but I want value of other duplicate index...
[4] => stdClass Object
(
[name] => wpc_inline_chkbox[]
[value] => 1,2,3
)
can anyone help me to solve this
Thanks in advance
This is assuming both name/value are strings
<?php
$objects; // This is your array
$sorted = array(); // This is the sorted array
// Loop over your array
foreach($objects as $object)
{
// Check if object exists in sorted array
if( array_key_exists($object->name, $sorted) )
{
// Object with this name is already in sorted array simply add to it
$obj = $sorted[$object->name];
// Update the values
$obj->value = $obj->value . ',' . $object->value;
} else {
// Object is not in the sorted array add it now
$sorted[$object->name] = $object;
}
}
?>
Sorry, your case is not ordinary, so you will need to do it by hand
$properArray = array();
foreach ($originArray as $value) {
if ( ! isset($properArray[$value['name']])) {
$properArray[$value['name']] = array(
'name' = $value['name'],
'value' = ''
);
}
if (isset($properArray[$value['name']]['value'])) {
$properArray[$value['name']]['value'] = $properArray[$value['name']]['value'] . ',' .$value['value']; //better to use array in this place
} else {
$properArray[$value['name']]['value'] = $value['value']
}
}
$originArray = array_values($properArray);
regards,
You have an array of object. You will have to loop through and assign them. Something like
$newArray = [];
foreach ($array as $obj) {
if (!isset($newArray[$obj->name])) {
$newArray[$obj->name] = $obj;
} else {
$newArray[$obj->name]->value .= ",{$obj->value}";
}
}
The new array should look like
["wpc_inline_chkbox[]"] => stdClass Object
(
[name] => wpc_inline_chkbox[]
[value] => "1,2,3"
)
But you should change [value] from string to array.
## Use this ##
$arr = array(
array(
'name' => 'hi'
,'value' => 1
)
,array(
'name' => 'hi'
,'value' => 2
)
, array(
'name' => 'hi2'
,'value' => 1
)
, array(
'name' => 'hi4'
,'value' => 1
)
,array(
'name' => 'hi0'
,'value' => 4
)
, array(
'name' => 'hi0'
,'value' => 3
)
, array(
'name' => 'hi1'
,'value' => 10
)
);
print_r($arr);
$arrTracker = array();
for ($i=0; $i <sizeof($arr) ;$i++) {
for($j = $i+1; $j<sizeof($arr);$j++){
if($arr[$i]['name'] == $arr[$j]['name']){
$arr[$i]['value'] .= ','.$arr[$j]['value'];
$arrTracker[$j] = $j;
}
}
}
// if you want to unset duplicate name and corresponding value, use below forloop, otherwise it's unnecessary
foreach($arrTracker as $tracker){
unset($arr[$tracker]);
}
$arr = array_values($arr);
print_r($arr);

Build an array out of another with a different structure

I have this array as follow:
Array (
[pageid_01_page_name] => first Page
[pageid_01_Style] => full
[thePageID_1] => pageid_01
[theElementID_1] => 1
[source_type_1] => slideshow_box
[pageid_01_1_slideshow_box] => Array ( [layout] => one
[content_id] => Array ( [0] => 856 )
[slideshow_timeout] => 8
[slideshow_height] => 400
[image_resize] => on
[image_crop] => on
[list_orderby] => date
[list_order] => DESC
[slideshow_buttons] => on )
[thePageID_2] => pageid_01
[theElementID_2] => 2
[source_type_2] => banner_box
[pageid_01_2_banner_box] => Array ( [layout] => one
[text] => test
[button_text] => Button Text
[button_link] => a link )
)
I need to build another array out of the one above, with this structure:
Array (
[pages] => Array ( [pageid_01] => stdClass Object
( [pageName] => first Page
[style] => full
[pageID] => pageid_001
[contents] => Array (
[2] => stdClass Object (
[element_id] => 1
[content_type] => slideshow_box
[layout] => one
[content_id] => Array ( [0] => 856 )
[slideshow_timeout] => 8
[slideshow_height] => 400
[image_resize] => on
[image_crop] => on
[list_orderby] => date
[list_order] => DESC
[slideshow_buttons] => on
)
[3] => stdClass Object (
[element_id] => 2
[content_type] => banner_box
[layout] => one
[text] => test
[button_text] =>Button text
[button_link] => a link
)
)
Updated with more details:
The way i'm building the second array right now is like this:
$pages = new \stdClass();
$i=0;
$page=0;
$thepagename = '_page_name';
if(isset($thepagename))
{
if(in_array($thepagename, $AllPages))
{ foreach($AllPages as $k => $v) {
$page = str_replace('_page_name','',$k);
#$pages->pages[$page]->pageID = $page;
#$pages->pages[$page]->pageName = $v;
if(stristr($k, 'theElementID_') == true) {
$element_id = $v;
}
if(stristr($k, '_slideshow_box') == true) {
$i++;
#$pages->pages[$page]->contents[$i]->element_id = $element_id;
$pages->pages[$page]->contents[$i]->content_type = 'slideshow_box';
$pages->pages[$page]->contents[$i]->layout = $v["layout"];
$pages->pages[$page]->contents[$i]->content_id = #$v["content_id"];
$pages->pages[$page]->contents[$i]->slideshow_timeout = $v["slideshow_timeout"];
$pages->pages[$page]->contents[$i]->slideshow_height = $v["slideshow_height"];
$pages->pages[$page]->contents[$i]->image_resize = $v["image_resize"];
$pages->pages[$page]->contents[$i]->image_crop = $v["image_crop"];
$pages->pages[$page]->contents[$i]->list_orderby = $v["slideshow_orderby"];
$pages->pages[$page]->contents[$i]->list_order = $v["slideshow_order"];
$pages->pages[$page]->contents[$i]->slideshow_buttons = $v["slideshow_buttons"];
}
}
}
}
and so on....
but this way takes a lot of coding while the contents can be much much more...
First of all,you can loop your array, get all values and keys, The second,built another array, push all key and values into the new array. just like
$new_array = ($new_array,values1,values2,.......);
hope it can help you

Highlighting array values

I have an array of statistics on racing dogs. I need to highlight the maximum and minimum values in each element by adding another element. Let me explain.
Example of array
Array
(
[1] => Array
(
[fast_calc_7] => 31.06
[av_calc_7] => 25.03
)
[2] => Array
(
[fast_calc_7] => 16.74
[av_calc_7] => 18.06
)
[3] => Array
(
[fast_calc_7] => 30.93
[av_calc_7] => 31.06
)
[4] => Array
(
[fast_calc_7] => 29.01
[av_calc_7] => 25.08
)
[5] => Array
(
[fast_calc_7] => 30.72
[av_calc_7] => 31.02
)
[6] => Array
(
[fast_calc_7] => 31.16
[av_calc_7] => 36.02
)
)
Example of resulting array
I need to compare these values and add another element specifying if it is the highest of lowest value. For example, it should generate an array such as the following.
Array
(
[1] => Array
(
[fast_calc_7] => 31.06
[av_calc_7] => 25.03
)
[2] => Array
(
[fast_calc_7] => 16.74
[fast_calc_7_lowest] => TRUE
[av_calc_7] => 18.06
[av_calc_7_lowest] => TRUE
)
[3] => Array
(
[fast_calc_7] => 30.93
[av_calc_7] => 37.06
[av_calc_7_highest] => TRUE
)
[4] => Array
(
[fast_calc_7] => 29.01
[av_calc_7] => 25.08
)
[5] => Array
(
[fast_calc_7] => 30.72
[av_calc_7] => 31.02
)
[6] => Array
(
[fast_calc_7] => 31.16
[fast_calc_7_highest] => TRUE
[av_calc_7] => 36.02
)
)
I'm sure there is a really simple way to do this. The more I have to iterate through arrays, the slower my application gets!
Thanks guys.
EDIT: How data is sourced
JSON array gathered from greyhound website for each dogs "race history"
JSON array is processed to form a nice array for each dogs history with the data for example the time for each race, finishing position etc.
This data is then calculated for each dog to find the fastest time, average time and other data. This is the array above. (Each array is a different dog).
At this point, I need the array processed to add in the highlighting elements.
There isn't much to it. You need to loop over the array, maintaining variables with the indexes of the high/low elements so far for each attribute. After the loop, access those elements and add your extra data.
$lowAverage = $highAverage = array('index' => null, 'value' => 0);
$lowFast = $highFast = array('index' => null, 'value' => 0);
foreach($dogs as $index => $data) {
if($lowAverage['index'] == null || $data['av_calc_7'] < $lowAverage['value']) {
$lowAverage = array('index' => $index, 'value' => $data['av_calc_7']);
}
if($highAverage['index'] == null || $data['av_calc_7'] > $highAverage['value']) {
$highAverage = array('index' => $index, 'value' => $data['av_calc_7']);
}
// same for $lowFast, $highFast
}
if($lowAverage['index'] != null) {
$dogs[$lowAverage['index']]['av_calc_7_lowest'] = true;
}
// same for the other 3 values

Categories