Convert multidimensional array to nested set array - php

im having a little problem i need some help with.
Im trying to convert a multidimensional array into a flatten array with nested set values right and left like so:
$array = {
'id' => 1
'name' => 'john'
'childs' => array(
array(
'id' => 1
'name' => 'jane'
)
)
}
to
$array = {
array(
'id' => 1,
'name' => 'john'
'left' => '1'
'right' => '4'
),
array(
'id' => 1,
'name' => 'jane'
'left' => '2'
'right' => '3'
)
}
Any help is appreciated!

I asked a very similar question and got a result, so thought I'd send it on for you. I realise this is quite an old topic, but still worth getting an answer. I've included my data, but would be easily adapted for yours.
$JSON = '[{"id":1,"children":[{"id":2,"children":[{"id":3},{"id":4}]},{"id":5}]}]';
$cleanJSON = json_decode($JSON,true);
$a_newTree = array();
function recurseTree($structure,$previousLeft)
{
global $a_newTree; // Get global Variable to store results in.
$indexed = array(); // Bucket of results.
$indexed['id'] = $structure['id']; // Set ID
$indexed['left'] = $previousLeft + 1; // Set Left
$lastRight = $indexed['left'];
$i_count = 0;
if ($structure['children'])
{
foreach ($structure['children'] as $a_child)
{
$lastRight = recurseTree($structure['children'][$i_count],$lastRight);
$i_count++;
}
}
$indexed['right'] = $lastRight + 1; // Set Right
array_push($a_newTree,$indexed); // Push onto stack
return $indexed['right'];
}
recurseTree($cleanJSON[0],0);
print_r($a_newTree);

function restructRecursive($array, $left = 1) {
if (isset($array['childs'])) {
$result = array();
foreach ($array['childs'] as $child) {
$result = array_merge($result, restructRecursive($child, $left+1));
}
unset($array['childs']);
}
$array['left'] = $left;
return array_merge(array($array), $result);
}
$newStruct = restructRecursive($oldStruct);

For anyone coming here and looking for a solution, loneTraceur's solution works fine. However, I needed one in OOP, so here is my version of it.
<?php
class NestedSet
{
protected $tree = [];
public function deconstruct($tree, $left = 0)
{
$this->flattenTree($tree, $left);
return $this->tree;
}
protected function flattenTree($tree, $left)
{
$indexed = [];
$indexed['id'] = $tree['id'];
$indexed['_lft'] = $left + 1;
$right = $indexed['_lft'];
if (isset($tree['children']) && count($tree['children'])) {
foreach ($tree['children'] as $child) {
$right = $this->flattenTree($child, $right);
}
}
$indexed['_rgt'] = $right + 1;
$this->tree[] = $indexed;
return $indexed['_rgt'];
}
}
You would run it like this:
$NestedSet = new NestedSet;
$flat = $NestedSet->deconstruct($tree):
This code is based on the other answer.

Related

PHP - add values to already existing array

I have a already defined array, containing values just like the one below:
$arr = ['a','b','c'];
How could one add the following using PHP?
$arr = [
'a' => 10,
'b' => 5,
'c' => 21
]
I have tried:
$arr['a'] = 10 but it throws the error: Undefined index: a
I am surely that I do a stupid mistake.. could someone open my eyes?
Full code below:
$finishes = []; //define array to hold finish types
foreach ($projectstages as $stage) {
if ($stage->finish_type) {
if(!in_array($stage->finish_type, $finishes)){
array_push($finishes, $stage->finish_type);
}
}
}
foreach ($projectunits as $unit) {
$data[$i] = [
'id' => $unit->id,
'project_name' => $unit->project_name,
'block_title' => $unit->block_title,
'unit' => $unit->unit,
'core' => $unit->core,
'floor' => $unit->floor,
'unit_type' => $unit->unit_type,
'tenure_type' => $unit->tenure_type,
'floors' => $unit->unit_floors,
'weelchair' => $unit->weelchair,
'dual_aspect' => $unit->dual_aspect
];
$st = array();
$bs = '';
foreach ($projectstages as $stage) {
$projectmeasure = ProjectMeasure::select('measure')
->where('project_id',$this->projectId)
->where('build_stage_id', $stage->id)
->where('unit_id', $unit->id)
->where('block_id', $unit->block_id)
->where('build_stage_type_id', $stage->build_stage_type_id)
->first();
$st += [
'BST-'.$stage->build_stage_type_id => ($projectmeasure ? $projectmeasure->measure : '0')
];
if (($stage->is_square_meter == 0) && ($stage->is_draft == 0)) {
$height = ($stage->height_override == 0 ? $unit->gross_floor_height : $stage->height_override); //08.14.20: override default height if build stage type has it's own custom height
$st += [
'BST-sqm-'.$stage->build_stage_type_id => ($projectmeasure ? $projectmeasure->measure * $height: '0')
];
if ($stage->finish_type) {
$finishes[$stage->finish_type] += ($projectmeasure ? $projectmeasure->measure * $height: '0') * ($stage->both_side ? 2 : 1); //error is thrown at this line
}
} else {
if ($stage->finish_type) {
$finishes[$stage->finish_type] += ($projectmeasure ? $projectmeasure->measure : '0');
}
}
}
$data[$i] = array_merge($data[$i], $st);
$data[$i] = array_merge($data[$i], $finishes[$stage->finish_type]);
$i++;
}
The above code is used as is and the array $finishes is the one from the first example, called $arr
You're using += in your real code instead of =. That tries to do maths to add to an existing value, whereas = can just assign a new index with that value if it doesn't exist.
+= can't do maths to add a number to nothing. You need to check first if the index exists yet. If it doesn't exist, then assign it with an initial value. If it already exists with a value, then you can add the new value to the existing value.
If you want to convert the array of strings to a collection of keys (elements) and values (integers), you can try the following:
$arr = ['a','b','c'];
$newVals = [10, 5, 21];
function convertArr($arr, $newVals){
if(count($arr) == count($newVals)){
$len = count($arr);
for($i = 0; $i < $len; $i++){
$temp = $arr[$i];
$arr[$temp] = $newVals[$i];
unset($arr[$i]);
}
}
return $arr;
}
print_r(convertArr($arr, $newVals));
Output:
Array ( [a] => 10 [b] => 5 [c] => 21 )

php - how to convert 'tree-array' to array

I have gotten the tree-array like that:
Now I want to convert it to an array like this:
array(
1 => array('id'=>'1','parentid'=>0),
2 => array('id'=>'2','parentid'=>0),
3 => array('id'=>'3','parentid'=>1),
4 => array('id'=>'4','parentid'=>1),
5 => array('id'=>'5','parentid'=>2),
6 => array('id'=>'6','parentid'=>3),
7 => array('id'=>'7','parentid'=>3)
);
I had already coded something like this:
private function _getOrderData($datas)
{
$_data = [];
static $i = 0;
foreach ($datas as $data) {
$i++;
$rows = ['id' => $data['id'], 'pid' => isset($data['children']) ? $data['id'] : 0, 'menu_order' => $i];
if(isset($data['children'])) {
$this->_getOrderData($data['children']);
}
$_data[] = $rows;
}
return $_data;
}
But it didn't work
Cry~~
How can I fix my code to get the array? Thanks~
BTW, my English is pool.
So much as I don't know you can read my description of the problem or not.
I had already solved this problem, Thx~
private function _getOrderData($datas, $parentid = 0)
{
$array = [];
foreach ($datas as $val) {
$indata = array("id" => $val["id"], "parentid" => $parentid);
$array[] = $indata;
if (isset($val["children"])) {
$children = $this->_getOrderData($val["children"], $val["id"]);
if ($children) {
$array = array_merge($array, $children);
}
}
}
return $array;
}
The array look like:
array

PHP sort array of arrays [duplicate]

This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Closed 8 years ago.
I have an array of arrays in PHP that I created like the following:
$wp_players = array();
while ($wp_player = mysql_fetch_array($wp_player_query))
{
$wp_player_ranking = mysql_query(get_ranking_sql($wp_player['id'])) or die(mysql_error());
$wp_ranking = mysql_fetch_array($wp_player_ranking);
array_push($wp_players, array('first_name' => $wp_player['first_name'],
'last_name' => $wp_player['last_name'],
'school_name' => $wp_player['school_name'],
'1st' => $wp_ranking['1st'],
'2nd' => $wp_ranking['2nd'],
'3rd' => $wp_ranking['3rd'],
'4th' => $wp_ranking['4th'],
'5th' => $wp_ranking['5th'],
'total' => ($wp_ranking['1st'] + $wp_ranking['2nd'] + $wp_ranking['3rd'] + $wp_ranking['4th'] + $wp_ranking['5th'])));
}
What I want to do now is have $wp_players array sorted by the 'total' key that's inside each of its elements. Since the Array is not flat, and is an array of arrays, what's the best way to do this in PHP?
array_multisort() will accomplish precisely what you're looking to achieve:
$totals = array();
foreach ($wp_players as $key => $row) {
$totals[$key] = $row['total'];
}
array_multisort($totals, SORT_DESC, $wp_players);
This function will be what you want, this function if from my development framework, It suits for many situations, has order control and reserveKey control parameters.
<?php
function sortByField($arr, $fieldName, $flag = 'desc', $reserveKey = true)
{
$indexArr = array();
foreach ($arr as $idx => $item)
{
$indexArr[$idx] = $item[$fieldName];
}
if ('desc' == $flag)
{
arsort($indexArr);
}
else
{
asort($indexArr);
}
$result = array();
foreach ($indexArr as $idx => $field)
{
if($reserveKey)
{
$result[$idx] = $arr[$idx];
}
else
{
$result[] = $arr[$idx];
}
}
return $result;
}
usage
$wp_players = sortByField($wp_players, 'total');

call_user_func_array with array_multisort [duplicate]

This question already has answers here:
Sort array using array_multisort() with dynamic number of arguments/parameters/rules/data
(5 answers)
Closed 2 years ago.
I have the problem with sort direction. I try to sort multi-dimensional array with direction. I can't use array_multisort() directly, because I don't know how many parametrs will be. I use call_user_func_array('array_multisort', $params); And it works, but I can't set sort direction (SORT_ASC,SORT_DESC). How can I set sort direction for call_user_func_array('array_multisort', $params);?
Here is my code, you can try it
function get_fields($data, $order_by) {
$order_row = preg_split("/[\s,]+/", $order_by);
for ($i=0;$i<count($order_row);$i++) {
foreach ($data as $key => $row) {
$tmp[$i][$key] = $row[$order_row[$i]];
}
}
return $tmp;
}
function ordering($data, $order_by) {
$tmp = get_fields($data, $order_by);
$params = array();
foreach($tmp as &$t){
$params[] = &$t;
}
$params[1] = array("SORT_DESC","SORT_DESC","SORT_DESC","SORT_DESC"); // like that no warning but no sorting
$params[] = &$data;
call_user_func_array('array_multisort', $params);
return array_pop($params);
}
$data = array (
array('id' => 1,'name' => 'Barack','city' => 9),
array('id' => 7,'name' => 'boris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2)
);
$order_by = "city desc, name";
echo "<br>ORDER BY $order_by<br>";
$ordered = ordering($data, $order_by);
echo "<pre>";
var_dump($ordered);
echo "</pre>";
I want to do a sort like MySQL ORDER BY city DESC, name. It's my goal.
To be able to sort an array multiple times and achieve a result like ORDER BY city DESC, name ASC you need a function that does a stable sort.
As far as I know PHP doesn't have one so you have to sort it once with a comparator function like this
$data = array (
array('id' => 3,'name' => 'coris','city' => 2),
array('id' => 1,'name' => 'Barack','city' => 9),
array('id' => 7,'name' => 'boris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2),
);
$order_by = array(
'city' => array('dir' => SORT_DESC, 'type' => SORT_NUMERIC),
'name' => array('dir' => SORT_ASC, 'type' => SORT_STRING),
);
function compare($row1,$row2) {
/* this function should determine which row is greater based on all of the criteria
and return a negative number when $row1 < $row2
a positive number when $row1 > $row2
0 when $row1 == $row2
*/
global $order_by;
foreach($order_by as $field => $sort) {
if($sort['type'] != SORT_NUMERIC) {
// strings are compared case insensitive and assumed to be in the mb_internal_encoding
$cmp = strcmp(mb_strtolower($row1[$field]), mb_strtolower($row2[$field]));
} else {
$cmp = doubleval($row1[$field]) - doubleval($row2[$field]);
}
if($sort['dir'] != SORT_ASC) $cmp = -$cmp;
if($cmp != 0) return $cmp;
}
return 0;
}
usort($data,'compare');
I had the same problem. It seems that call_user_func_array() can't handle the constants.
I've solved this problem by dynamically building an argument string and evaluating this string:
$args = array($arr1, $arr2);
$order = array(SORT_ASC, SORT_DESC);
$evalstring = '';
foreach($args as $i=>$arg){
if($evalstring == ''){ $evalstring.= ', '; }
$evalstring.= '$arg';
$evalstring.= ', '.$order[$i];
}
eval("array_multisort($evalstring);");
I know eval() is evil and this is not a clean way, but it works ;-)
It is working for me :
$arrayThatNeedToSort = array('data..');
$multiSortprop = array(['data.....']=> SORT_DESC,['data.....'] => SORT_ASC)
$properties = array();
foreach ($multiSortprop as $sortArr => $sortArg) {
array_push($properties,$sortArr);
array_push($properties,$sortArg);
}
array_push($properties,$arrayThatNeedToSort);
array_multisort(...$properties);
var_dump(end($properties));

How to sort this array by type (dir first and then files)

I wanna know how to sort arrays like this:
$array[] = Array (
'name' => '/home/gtsvetan/public_html/presta/cms.php'
'type' => 'text/x-php'
'size' => 1128
'lastmod' => 1339984800
);
$array[] = Array (
'name' => '/home/gtsvetan/public_html/presta/docs/'
'type' => 'dir'
'size' => 0
'lastmod' => 1329253246
);
I wanna to sort it first by type (folders first and then files) and then alphabetical. But I don't know how to sort it.
Best regards,
George!
you can use usort()
In compare function you do two comparisions on name & type - something like below:
function compare_f($a,$b) {
if($a['type']=='dir'&&$b['type']!='dir') return 1;
if($a['type']!='dir'&&$b['type']=='dir') return -1;
if(substr($a['name'],-1,1)=='/') $a['name']=substr($a['name'],0,-1);
if(substr($b['name'],-1,1)=='/') $b['name']=substr($b['name'],0,-1);
$af_array=explode('/',$a['name']);
$a_name=$af_array[count($af_array)-1];
$bf_array=explode('/',$b['name']);
$b_name=$bf_array[count($bf_array)-1];
if($a_name>$b_name)
return 1;
return -1;
}
usort($array,'compare_f');
You can do something like this..
$dir = array();
$file = array();
$dir = array();
$file = array();
foreach($array as $b=>$v) {
if($v['type'] == "dir") {
$dir[] = $v;
} else {
$file[] = $v;
}
}
$combined = array_merge($dir, $file);
Feel free to adjust it.

Categories