Multilevel tree with PHP - php

I have one table of folders
We dont know the depth of array and first I am trying to take all folder with parentid 0 and then am again calling the same function in foreach so that iterates each folder. Below is the dummy table
Id parentid label
-- -------- ------
1 0 ABC
2 1 XYZ
3 2 ABC2
4 0 DUMMY
I am trying to make tree with below code:
public function getFolder($parentId = null)
{
if(IS_NULL($parentId)){
$parentId = 0;
}
$folders = Connection::queryBuilder()->select('f.id,f.parentid,f.label')
->from('folders','f')
->where('parentid='.$parentId)->execute()->fetchAll(PDO::FETCH_OBJ);
$temp = array();
foreach($folders as $folder){
$subfolders = self::getFolder($folder->id,$temp);
array_push($temp,$folder,$subfolders);
}
return $temp;
}
But I am not getting the array the way I want. Can anyone help me?
Thanks in advance

There was a very close question: How to created nested multi dimensional array from database result array with parent id
#!/usr/bin/env php
<?php
// ordered by parent
$src = array(
['cid'=>'1','name'=>'Rootcat','parent'=>'0'],
['cid'=>'3','name'=>'dddd','parent'=>'1'],
['cid'=>'4','name'=>'test1','parent'=>'3'],
['cid'=>'5','name'=>'Test123 rec','parent'=>'3'],
['cid'=>'6','name'=>'abceg','parent'=>'3'],
['cid'=>'7','name'=>'JHGSGF','parent'=>'5'],
);
// inverse array, better take it sorted by parent_id desc
$data = array_reverse($src,false);
$for_parent = [];
foreach($data as $rec) {
if (isset($for_parent[$rec['cid']])) {
$rec['sub'] = $for_parent[$rec['cid']];
unset($for_parent[$rec['cid']]);
}
$for_parent[$rec['parent']] [$rec['cid']]= $rec;
}
print_r($for_parent);
So you can create array without recursive function.

Related

CSV to Associative Array (One column as key and another as value)

I need to convert a CSV file into an associative array, using one column as key and another as value. For example, I have this:
Application,Warehouse,Item,UPC,MFUSA Item No.,"PRODUCT DESCR1","PRODUCT DESCR2",Warehouse ID,Qty
INVENTORY:,Muscle Foods USA,1st STEP B12 LIQUID 16oz,673131100064,8890004,1st STEP B12 LIQUID 16oz,CHERRY CHARGE,11,29
INVENTORY:,Muscle Foods USA,1st STEP B12 LIQUID 16oz,673131100316,8890007,1st STEP B12 LIQUID 16oz,TROPICAL BLAST,11,26
INVENTORY:,Muscle Foods USA,1st STEP B12 LIQUID 16oz,673131100064,8890004,1st STEP B12 LIQUID 16oz,CHERRY CHARGE,12,6
I have to use the column UPC as the key and Qty as the value, so that the array looks like..
array(
'673131100064' => '29',
'673131100316 => '26',
'673131100064' => '6',
);
I have tried several solutions found in Google as well as here, but none of them are close to what I need to achieve. This Question was something similar, but it was written in Python.
Sorry for my poor knowledge in PHP. Can you please guide me to the right direction?
With array_map you can parse the csv data and have an array to do as you wish.
Example:
// Parsing the data in csv file
$csv = array_map('str_getcsv', file('path/file.csv'));
/*At this point you already have an array with the data but
* the first row is the header row in your csv file */
//remove header row
array_shift($csv);
$data = [];
//Walk the array and add the needed data into some another array
array_walk($csv, function($row) use (&$data) {
$data[$row[3]] = $row[8];
});
And that's it.
However the data you show as an example has duplicate UPCs. You will overwrite some data if you want an array with the structure 'UPC' => 'Qty' . You can't have duplicate keys in an array.
If what you are looking for is to get the total Qty for each UPCs then you just need to add the already existing Qty with the new one if the UPC key already exists.
// Parsing the data in csv file
$csv = array_map('str_getcsv', file('file.csv'));
//remove header row
array_shift($csv);
$data = [];
//Walk the array and add the needed data into another array
array_walk($csv, function($row) use (&$data) {
$data[$row[3]] = ($data[$row[3]] ? $data[$row[3]] + (int) $row[8] : (int) $row[8]);
});
Or longer but clearer.
//Walk the array and add the needed data into another array
array_walk($csv, function($row) use (&$data) {
if(!empty($data[$row[3]]))
{
$data[$row[3]] += (int) $row[8];
}
else {
$data[$row[3]] = (int) $row[8];
}
});
Explanation in the comments of the code below
$array = [];
// Open file "$file" and checking that it is not empty
if (($handle = fopen($file, "r")) !== false) {
// loop on each csv line, stopping at end of file
while (($data = fgetcsv($handle)) !== false) {
// Excluding header row & checking data is not empty
if ($data[0] !== 'Application' && !empty($data[0])) {
// fgetcsv returns an array, on your example UPC is on key 3 and Qty on key 9
$array[] = [$data[3] => $data[9]];
}
}
fclose($handle);
}
return $array;
Here the keys are hard-coded but maybe you have a way to put it dynamically, it depends of your code & workflow. This is just a simple (I hope) demonstration.
I use the SplfileObject for reading. Then the first line is used as the key for all values. array_column with the column names can now be used for the desired result.
$csv = new SplFileObject('datei.csv');
$csv->setFlags(SplFileObject::READ_CSV
| SplFileObject::SKIP_EMPTY
| SplFileObject::READ_AHEAD
| SplFileObject::DROP_NEW_LINE
);
//Combine first row as key with values
$csvArr = [];
foreach($csv as $key => $row){
if($key === 0) $firstRow = $row;
else $csvArr[] = array_combine($firstRow,$row);
}
$arrQty = array_column($csvArr,'Qty','UPC');

How do I handle Nestable output using PHP for any amount of nested items?

I'm trying to properly read output from a plugin I'm using for a menu editor. The output comes in the form a JSON string.
Example JSON Data:
[{"id":13},{"id":14},{"id":15,"children":[{"id":16},{"id":17},{"id":18}]}]
This is the plugin in action: http://jsfiddle.net/Av8q5/ (capped at 5 levels currently)
I'm using PHP to try to properly loop through any number of nested items, but I'm just not sure how to do this. My attempt at doing so works (but I think it's needs work) for only 2 levels deep, but I need this to expand based on any number of nested items. I'm relatively new to managing JSON strings with PHP, but here is my attempt to grab all of the ID's (my menu code has data-content as well, not just data-id's as the example shows above):
//Sample data from above
$data = '[{"id":13},{"id":14},{"id":15,"children":[{"id":16},{"id":17},{"id":18}]}]';
//Decode the JSON string to build the menu
$menu = json_decode($data, true);
//Store all of the ID's in an array
$ids = array();
//Hold the parent id to target this key when adding children to it
$y = 0;
//Start the loop through the menu array
foreach ($menu as $parentHolder) {
foreach ($parentHolder as $parentKey => $parent) {
//Checks to see when this jumps into the children array
if (!is_array($parent)) {
//Only run on the content key
if ($parentKey == "id") {
if ($parent) { $ids[] = $parent; $y++; } //Only to array if not empty
} //end check for content as key for the parents
} else {
//This is an array, which means this parent has children
foreach ($parent as $childHolder) {
foreach ($childHolder as $childKey => $child) {
//Only run on the content key
if ($childKey == "id") {
if ($child) { $ids[$y][] = $child; } //Only to array if not empty
} //end check for content as key for the children
} //End child foreach loop
} //end childholder foreach loop
} //Check if key is = content
} //end Parent loop
} //end the main loop
Output (based on example of JSON above):
While looking at this output again, I don't even think this is right... I think the child array should be appended to the 15, not on its own in the next key.
Array
(
[0] => 13
[1] => 14
[2] => 15
[3] => Array
(
[0] => 16
[1] => 17
[2] => 18
)
)
What I'm trying to do is properly loop through some JSON like this...
Example:
[{"id":13,"children":[{"id":20,"children":[{"id":21},{"id":22}]}]},{"id":15,"children":[{"id":16,"children":[{"id":18},{"id":14},{"id":19}]},{"id":17},{"id":23}]}]
In this example, there are children within children, how to I properly get this out into a nested array in PHP while keeping in mind that this menu could go as deep as 10 nested lists?
One solution would be to write a recursive function. A function that calls itself. This is a rough example using your example code. I broke your output code into a function. In the case that the nested content is an array, simply call the function and pass in the nested data as the parameter.
function output( $menu )
{
//Start the loop through the menu array
foreach ($menu as $parentHolder) {
foreach ($parentHolder as $parentKey => $parent) {
//Checks to see when this jumps into the children array
if (!is_array($parent)) {
//Only run on the content key
if ($parentKey == "id") {
if ($parent) { $ids[] = $parent; $y++; } //Only to array if not empty
} //end check for content as key for the parents
} else {
//This is an array, which means this parent has children
foreach ($parent as $childHolder) {
foreach ($childHolder as $childKey => $child) {
// recursive function call here
output( $child )
} //end check for content as key for the children
} //End child foreach loop
} //end childholder foreach loop
} //Check if key is = content
} //end Parent loop
} //end the main loop
}
Hello friends ، A convenient code
$d=json_decode('[{"id":1},{"id":2,"children":[{"id":3},{"id":4},{"id":5,"children":[{"id":6},{"id":7},{"id":8}]},{"id":9},{"id":10}]},{"id":11},{"id":12}]');
output($d);
function output($menu,$parent=0)
{
foreach ($menu as $handle)
{
if(isset($handle->children))
{
echo 'node : '.$handle->id.' - Pid : '.$parent.'<br>';
// Insert into DataBase Code (menuId = $handle->id and ParentId = $parent)
output($handle->children,$handle->id);
}
else if(isset($handle->id))
{
echo 'node : '.$handle->id.' - Pid : '.$parent.'<br>';
// Insert into DataBase Code (menuId = $handle->id and ParentId = $parent)
}
}
}
// print
node : 1 - pid : 0
node : 2 - pid : 0
node : 3 - pid : 2
node : 4 - pid : 2
node : 5 - pid : 2
node : 6 - pid : 5
node : 7 - pid : 5
node : 8 - pid : 5
node : 9 - pid : 2
node : 10 - pid : 2
node : 11 - pid : 0
node : 12 - pid : 0

Flatten RecursiveIteratorIterator array by children

I can't seem to figure out the best way to do this. I have a RecursiveIteratorIterator.
$info = new RecursiveIteratorIterator(
new GroupIterator($X), # This is a class that implements RecursiveIterator
RecursiveIteratorIterator::SELF_FIRST
);
Note: GroupIterator is a class that handles our custom formatted data
When I loop through it, I get exactly what I expect.
foreach($info as $data){
echo $info->getDepth().'-'.$data."\n";
}
The output is:
0-a
1-b
2-c
2-d
1-e
2-f
0-g
1-h
2-i
This is correct, so far. Now, what I want is to flatten the parents and children into a single array. I want one row for each max-depth child. The output I am trying to get is:
0-a 1-b 2-c
0-a 1-b 2-d
0-a 1-e 2-f
0-g 1-h 2-i
I can't figure out how to do this. Each iteration over the loop gives me another row, how can I combine the rows together that I want?
I managed to figure it out. #ComFreek pointed me in the right direction. Instead of using a counter, I used the current depth to check when I hit the lowest child, then I added the data to the final array, otherwise I added it to a temp array.
$finalArray = array();
$maxDepth = 2;
foreach($info as $data){
$currentDepth = $info->getDepth();
// Reset values for next parent
if($currentDepth === 0){
$currentRow = array();
}
// Add values for this depth
$currentRow[$currentDepth] = $data;
// When at lowest child, add to final array
if($currentDepth === $maxDepth){
$finalArray[] = $currentRow;
}
}
Try adding a counter variable:
// rowNr loops through 0, 1, 2
$rowNr = 0;
$curData = [];
$outputData = [];
foreach($info as $data){
// got last element, add the temp data to the actual array
// and reset temp array and counter
if ($rowNr == 2) {
$outputData[] = $curData;
$curData = [];
$rowNr == 0;
}
else {
// save temp data
$curData[] = $info->getDepth() . '-' . $data;
}
$rowNr++;
}

String together values of parent nodes from a multidimensional PHP array

I have a table of workplaces and their parent ids. Each parent could have any number of levels.
id workplace parent
1 WCHN 0
2 Acute Services 1
3 Paediatric Medicine 2
4 Surgical Services 2
5 Nursing and Midwifery 1
6 Casual Pool 5
7 Clinical Practice 5
I need to create a single select input that lists the workplaces with all their parent workplaces, a bit like this:
<select>
<option>WCHN > Acute Services > Paediatric Medicine</option>
<option>WCHN > Acute Services > Surgical Services</option>
<option>WCHN > Nursing and Midwifery > Casual Pool</option>
<option>WCHN > Nursing and Midwifery > Clinical Practice</option>
</select>
By modifying this solution I've been able to turn my flat list into a multidimensional array and output pairs of values, but no the full paths.
<?php
public function list_all_workplaces()
{
$temp = $result = array();
$list = array();
// Get the data from the DB
$table = mysql_query("SELECT * FROM workplaces");
// Put it into one dimensional array with the row id as the index
while ($row = mysql_fetch_assoc($table)) {
$temp[$row['workplace_id']] = $row;
}
// Loop the 1D array and create the multi-dimensional array
for ($i = 1; isset($temp[$i]); $i++)
{
if ($temp[$i]['parent'] > 0)
{
$tmpstring = ($temp[$i]['workplace']); // workplace title
// This row has a parent
if (isset($temp[$temp[$i]['parent']])) {
$list[$i] = $temp[$temp[$i]['parent']]['workplace']." > ".$tmpstring;
//example output: Acute Services > Paediatric Medicine
// The parent row exists, add this row to the 'children' key of the parent
$temp[$temp[$i]['parent']]['children'][] =& $temp[$i];
} else {
// The parent row doesn't exist - handle that case here
// For the purposes of this example, we'll treat it as a root node
$result[] =& $temp[$i];
}
} else {
// This row is a root node
$result[] =& $temp[$i];
}
}
// unset the 1D array
unset($temp);
//Here is the result
print_r($result);
}
Example print_r() output:
[1] => WCHN > Acute Services
[2] => Acute Services > Paediatric Medicine
Where do I go from here to get all of the parent workplaces into the select options?
I can't see how your code will produce the print_r output you say you're seeing, as you're not ever using the string you create. But something along these lines should get you on your way - it generates the strings you need.
Replace your for loop with the following:
foreach ($temp as $i => $details)
{
$parentID = $details['parent'];
$tmpstring = ($details['workplace']);
if ($parentID > 0 && isset($temp[$parentID]))
{
$temp[$parentID]['children'][] =& $temp[$i];
while ($parentID > 0 && isset($temp[$parentID]))
{
$tmpstring = $temp[$parentID]['workplace']." > ".$tmpstring;
$parentID = $temp[$parentID]['parent'];
}
}
$result[] = $tmpstring;
}
As #Syx said, you'll need to return something from your function too, probably $result, and then use that to generate your <select>.
I don't understand how you even use your function if there is no return value.
If your array is built correctly and the way you want it and your asking about getting your results into an html select field then that's the easy part.
Let's get back to your function real quick
.
You don't have return $result listed in your function so it's not going to return anything when called.
You'll want to add that to the end of the function to start.
You'll then loop through the array to start your html processing.
$data = list_all_workplaces()
foreach( $data as $key => $value )
{
$options = "<option value='{$key}'>{$value}</option>";
}
echo "<select>{$options}</select>";

shift array without indexOfOutBounds

I have 2 array's with the same length. array $gPositionStudents and array $gPositionInternships. Each student is assigned to a different internship, That part works.
Now I want the first element (index 0) of $gPositionStudent refer to the second (index 1) element of array $gPositionInternship. That implicitly means that the last element of $gPositionStudents refer to the first element of $gPositionInternship. (I included a picture of my explanation).
My Code is:
// Make table
$header = array();
$header[] = array('data' => 'UGentID');
$header[] = array('data' => 'Internships');
// this big array will contains all rows
// global variables.
global $gStartPositionStudents;
global $gStartPositionInternships;
//var_dump($gStartPositionInternships);
$rows = array();
$i = 0;
foreach($gStartPositionStudents as $value) {
foreach($gStartPositionInternships as $value2) {
// each loop will add a row here.
$row = array();
// build the row
$row[] = array('data' => $value[0]['value']);
//if($value[0] != 0 || $value[0] == 0) {
$row[] = array('data' => $gStartPositionInternships[$i]);
}
$i++;
// add the row to the "big row data (contains all rows)
$rows[] = array('data' => $row);
}
$output = theme('table', $header, $rows);
return $output;
Now I want that I can choose how many times, we can shift. 1 shift or 2 or more shifts. What I want exists in PHP?
Something like this:
//get the array keys for the interns and students...
$intern_keys = array_keys($gStartPositionInternships);
$student_keys = array_keys($gStartPositionStudents);
//drop the last intern key off the end and pin it to the front.
array_unshift($intern_keys, array_pop($intern_keys));
//create a mapping array to join the two arrays together.
$student_to_intern_mapping = array();
foreach($student_keys as $key=>$value) {
$student_to_intern_mapping[$value] = $intern_keys[$key];
}
You'll need to modify it to suit the rest of your code, but hopefully this will demonstrate a technique you could use. Note the key line here is the one which does array_unshift() with array_pop(). The comment in the code should explain what it's doing.
I think you want to do array_slice($gPositionsStudents, 0, X) where X is the number of moves to shift. This slices of a number of array elements. Then do array_merge($gPositionsStudents, $arrayOfSlicedOfPositions); to append these to the end of the original array.
Then you can do an array_combine to create one array with key=>value pairs from both arrays.

Categories