I have an array of locations. Each of these locations can have child locations. Each of the child locations can also have children, and so on:
$locations = array(
array("id" => 1, "parent_id" => 0, "name" => "England"),
array("id" => 2, "parent_id" => 0, "name" => "Scotland"),
array("id" => 3, "parent_id" => 0, "name" => "Ireland"),
array("id" => 4, "parent_id" => 0, "name" => "Wales"),
array("id" => 5, "parent_id" => 1, "name" => "East England"),
array("id" => 6, "parent_id" => 1, "name" => "London"),
array("id" => 7, "parent_id" => 6, "name" => "West London"),
array("id" => 8, "parent_id" => 6, "name" => "East London"),
array("id" => 9, "parent_id" => 1, "name" => "East Midlands"),
array("id" => 10, "parent_id" => 9, "name" => "Derbyshire")
);
I want to re-structure this array so that the children are arrays of the parent. Something like this (untested):
$locations = array("id" => 1, "parent_id" => 0, "name" => "England", "children" => array(
array("id" => 5, "parent_id" => 1, "name" => "East England"),
array("id" => 6, "parent_id" => 1, "name" => "London", "children" => array(
array("id" => 7, "parent_id" => 6, "name" => "West London"),
array("id" => 8, "parent_id" => 6, "name" => "East London")))));
This is so I can then print them out using indents like so:
LOCATIONS
England
- East England
- London
-- West London
-- East London
- East Midlands
-- Derbyshire
Scotland
Ireland
Wales
I have tried several ways, like grouping them by parent ID, but I just can't work out the logic for this, and there may be a better way of doing it (recursion, perhaps?).
Many thanks.
Hi perhaps this will help you, i just wrote it to quickly convert a mysql result containing a parent_id into a usable data hierarchy. Your input array should also work. It's just a couple of lines with two basic loops. No recursion required. Some comments included:
<?php
$max = count($inputArray);
$tree = array();
$flat = array();
// Create a flat hierarchy array of all entries by their id
for ($i = 0; $i < $max; $i++) {
$n = $inputArray[$i];
$id = $n['page_id'];
$flat[$id] = $n;
}
// Then check all those entries by reference
foreach ($flat as $key => &$child) {
// Add a children array if not already existing
if (!isset($child['children']))
$child['children'] = array();
$id = $child['page_id'];
$pid = $child['parent_id'];
// If childs parent id is larger then zero
if ($pid > 0) {
// Append it by reference, which means it will reference
// the same object across different carriers
$flat[$pid]['children'][] = &$child;
} else {
// Otherwise it is zero level, which initiates the tree
$tree[$id] = &$child;
}
}
$tree = array_values($tree); // Indices fixed, there we go, use $tree further
?>
So notice the by '& reference' characters. They do all the work allowing the tree to be built from the flat array by pointing to the same objects.
See this excellent anwser from Bill Kalwin.
As well as his presentation on "Models for hierarchical data (PHP and SQL)"
Also, you may be interested in storing directly data as a "nested set" structure in your DB.
Related
I have created an Array but I am not getting the correct output, I am just wondering where I made error in code below
The Out put should display the name as below
John
James
Jonny
Jeni
<?php
$multiArray = Array(
Array("id" => 1, "name" => "John"),
Array("id" => 2, "name" => "James"),
Array("id" => 3, "name" => "Jonny"),
Array("id" => 4, "name" => "Jeni"));
$tmp = Array();
foreach($multArray as &$ma)
$tmp[] = &$ma[name];
array_multisort($tmp, $multiArray);
foreach($multiArray as &ma)
echo $ma["name"]."<br/>";
?>
What are you trying to achieve here? Are you trying to only print the content of the array?
Just do it like this is enough
<?php
$multiArray = Array(
Array("id" => 1, "name" => "John"),
Array("id" => 2, "name" => "James"),
Array("id" => 3, "name" => "Jonny"),
Array("id" => 4, "name" => "Jeni")
);
foreach($multiArray as $ma)
{
echo $ma['name'] . "<br>";
}
?>
Also I noticed that in your codes, there is a lot of typo there.
This is your codes after I fixed all the typos:
<?php
$multiArray = Array(
Array("id" => 1, "name" => "John"),
Array("id" => 2, "name" => "James"),
Array("id" => 3, "name" => "Jonny"),
Array("id" => 4, "name" => "Jeni")
);
$tmp = Array();
foreach($multiArray as $ma)
$tmp[] = $ma['name'];
array_multisort($tmp, $multiArray);
foreach($multiArray as $ma)
echo $ma["name"]."<br/>";
?>
Output:
James
Jeni
John
Jonny
Yes the output is different from what you want. this is because you sort the array using array_multisort. read: http://php.net/manual/en/function.array-multisort.php
I have 2 arrays, $array0 and $array1. Let's pre-fill them:
$user01 = array("no" => 1, "name" => "john");
$user02 = array("no" => 2, "name" => "lewis");
$user03 = array("no" => 3, "name" => "dan");
$array0 = array($user01, $user02, $user03, $user04);
$user11 = array("id" => 1, "name" => "john", "attr" => "foo");
$user12 = array("id" => 7, "name" => "mark", "attr" => "bar");
$array1 = array($user11, $user12);
I want to get all users from $array0 who are not in $array1, so I use array_udiff:
$diff = array_udiff($array0, $array1, function ($userA, $userB) {
return $userA['no'] == $userB['id'];
});
However, inside the anonymous compare function, if I do a var_dump of $userA and $userB, they both seem to belong to $array0, while the behavior I was expecting is for $userA to belong to $array0 and $userB to $array1.
I.e., the expected answer is [[2, "lewis"], [3, "dan"]], but I get a 'not found index': "Undefined index id" in the line of the comparison function.
Am I missing something on array_udiff behavior?
I'm pretty sure PHP expects the arguments to the comparison function to be interchangeable, you either need to change the array indexes to be common, or implement logic in the comparison function to deal with this.
That's not how a comparison function works.
The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
So:
$user01 = array("id" => 1, "name" => "john"); // "no" changed to "id"
$user02 = array("id" => 2, "name" => "lewis");
$user03 = array("id" => 3, "name" => "dan");
$array0 = array($user01, $user02, $user03); // non-existant $user04 removed
$user11 = array("id" => 1, "name" => "john", "attr" => "foo");
$user12 = array("id" => 7, "name" => "mark", "attr" => "bar");
$array1 = array($user11, $user12);
$diff = array_udiff($array0, $array1, function ($userA, $userB) {
if( $userA['id'] == $userB['id'] ) { return 0; } // compare function re-jiggered
else { return $userA['id'] - $userB['id']; }
});
print_r($diff);
Yields:
Array
(
[1] => Array
(
[id] => 2
[name] => lewis
)
[2] => Array
(
[id] => 3
[name] => dan
)
)
I have the following table:
CREATE TABLE sample (id INT AUTO_INCREMENT PRIMARY KEY, entry_id INT NOT NULL, last_change timestamp);
INSERT INTO sample (entry_id) VALUES (1), (2), (3), (4);
My code does the following:
Receives POST input and compares the values to the table above.
If the the POST data has a value that does not exist in the entry_id column, then it will perform an INSERT statement.
If the table has a value in the entry_id column that does not exist in the POST data, then it will perform a DELETE statement
PHP Code:
<?php
// $_POST data:
$submittedEntries = array(1, 3, 4, 5, 6);
// $dbORM->getEntries()
$currentEntries = array(
array("id" => 100, "entry_id" => 1, "timestamp" => "2014-07-24 2:14:00"),
array("id" => 101, "entry_id" => 2, "timestamp" => "2014-07-24 2:14:00"),
array("id" => 102, "entry_id" => 3, "timestamp" => "2014-07-24 2:14:00"),
array("id" => 103, "entry_id" => 4, "timestamp" => "2014-07-24 2:14:00"),
)
$entriesToAdd = array();
$entriesToRemove = array();
// Find Entries to Add
// Loop through each entry submitted
foreach($submittedEntries as $entry_id) {
$exists = false;
// Loop through all Current Entries
foreach($currentEntries as $existingEntry) {
// Find matching IDs
if ($existingEntry["entry_id"] == $entry_id) {
$exists = true;
}
}
if ($exists == false) {
$entriesToAdd[] = $entry_id;
}
}
// Find Entries to Remove
// Loop through all Current Entries
foreach($currentEntries as $existingEntry) {
$remove = true;
// Loop through each entry submitted
foreach($submittedEntries as $entry_id) {
// Find matching IDs
if ($existingEntry["entry_id"] == $entry_id) {
$remove = false;
}
}
if ($remove == true) {
$entriesToRemove[] = $existingEntry["entry_id"];
}
}
// Add New Entries
foreach($entriesToAdd as $entry_id) {
$dbORM->addEntry($entry_id);
}
// Remove Entries
foreach($entriesToRemove as $entry_id) {
$dbORM->removeEntry($entry_id);
}
$entries = $dbORM->getEntries(); // SELECT * from sample;
print_r($entries);
/*
Expected Output:
array(
array("id" => 100, "entry_id" => 1, "timestamp" => "2014-07-24 2:14:00"),
array("id" => 102, "entry_id" => 3, "timestamp" => "2014-07-24 2:14:00"),
array("id" => 103, "entry_id" => 4, "timestamp" => "2014-07-24 2:14:00"),
array("id" => 104, "entry_id" => 5, "timestamp" => "2014-07-24 3:27:00"),
array("id" => 105, "entry_id" => 6, "timestamp" => "2014-07-24 3:27:00")
)
*/
Is there a better way to do this? I looked into all the php array functions and they do not seem to be able to deep search of multidimensional arrays.
If the table has to always be exactly like the POST, simplest way is to DELETE all rows and then INSERT all posts.
Another solution could be the use of unique key on entry_id...
This way you could really simply do 2 small queries:
$insert = "INSERT IGNORE INTO sample (entry_id)VALUES(".implode('),(',$submittedEntries).")";
$delete = "DELETE FROM sample WHERE entry_id NOT IN(".implode(',',$submittedEntries).")";
If you want to compair the two lists in PHP then you can use array_walk() to run through the db list and then use in_array() to see if it's in the submitted POST. You can then filter into two arrays.
<?php
$entriesToAdd = array();
$entriesToRemove = array();
$submittedEntries = array(1, 3, 4, 5, 6);
$currentEntries = array(
array("id" => 100, "entry_id" => 1),
array("id" => 101, "entry_id" => 2),
array("id" => 102, "entry_id" => 3),
array("id" => 103, "entry_id" => 4),
);
function AWS(&$item, $key) {
global $submittedEntries, $entriesToAdd, $entriesToRemove;
if(in_array($item["entry_id"], $submittedEntries))
{
array_push($entriesToAdd, $item);
}
else
{
array_push($entriesToRemove, $item);
}
}
array_walk( $currentEntries, 'AWS' );
echo "Add:";
print_r($entriesToAdd);
echo "Remove:";
print_r($entriesToRemove);
This outputs:
Add:Array
(
[0] => Array
(
[id] => 100
[entry_id] => 1
)
[1] => Array
(
[id] => 102
[entry_id] => 3
)
[2] => Array
(
[id] => 103
[entry_id] => 4
)
)
Remove:Array
(
[0] => Array
(
[id] => 101
[entry_id] => 2
)
)
I've a multidimensional array and would like to update/add some values, recursive. I'd like to avoid building a new array, if possible. But I've two main problems:
How to update the values "on-the-fly"? It should be possible with the &-operator.
How to get values to extend the array if the function getDetails() is out of scope.
My alternative is to rebuild everything, but I think there should be a more clean possibility.
I've added some pseudocode and hope it's not too weird. I appreciate your help!
Here's the fiddle: http://phpfiddle.org/main/code/m2g-ign
PHP
// build the array
$myArray = array(
array(
"id" => 1,
"name" => "A (Level 1)",
"children" => array(
array(
"id" => 3,
"name" => "C (Level 2)",
"children" => array(
"id" => 4,
"name" => "D (Level 3)",
"children" => null
)
),
array(
"id" => 6,
"name" => "F (Level 2)",
"children" => array(
"id" => 7,
"name" => "G (Level 3)",
"children" => null
)
)
)
),
array(
"id" => 2,
"name" => "B (Level 1)",
"children" => array(
array(
"id" => 5,
"name" => "E (Level 2)",
"children" => null
)
)
)
);
// returns detailed data, important: it's out of scope
function getDetails($id) {
// select dataset from DB
// ...
return $details;
}
// loop the array
$RAI = new RecursiveArrayIterator($myArray);
function updateMyArray($iterator) {
while ($iterator->valid()) {
if ($iterator->hasChildren()) {
// recursive
updateMyArray($iterator->getChildren());
} else {
/*
// 1. set "name" to uppercase
// Pseudocode:
$iterator->activeChild()->change(function(&$value) {
$value = toUpperCase($value);
});
// 2. add Array
// Pseudocode:
$id = $iterator->activeChild()->getValue("id");
$iterator->activeChild()->add("details", getDetails($id)); // getDetails() is out of scope, I guess
*/
}
$iterator->next();
}
}
echo "<strong>Before:</strong><pre>";
print_r($myArray);
echo "</pre>";
iterator_apply($RAI, 'updateMyArray', array($RAI));
echo "<strong>After:</strong><pre>";
print_r($myArray);
echo "</pre>";
i have this multi array "$marray wich has inside some array with some similar key = value and some not look like this :
$marray = array(
array("id" => "1", "be_pro" => 6, "name" => "a1", "service" => 4a),
array("id" => "2", "be_pro" => 6, "name" => "a1", "service" => 4d),
array("id" => "3", "be_pro" => 4, "name" => "a4", "service" => 3d),
array("id" => "4", "be_pro" => 4, "name" => "a4", "service" => 3s),
array("id" => "6", "be_pro" => 4, "name" => "a4", "service" => 34),
array("id" => "8", "be_pro" => 3, "name" => "a3", "service" => 4r),
array("id" => "8", "be_pro" => 3, "name" => "a3", "service" => 4d)
);
So i would like to get new arrays with "id", "be_pro" and "name" once then "service" plus "service" from next array till "be_pro" in the new array is different , so if is different put in the next array.
How should i do this?
what i need is print a multi array and within and array with every row with similar be_pro
This help me:
// first get all bepro_id and put in array
foreach($all_rows as $service){
$bepros[$service['bepro_id']]++;
}
//var_dump($bepros);
//Then for each be pro_id print every row , so print every service , then for be pro_id,id,name just print the first key "[0]" the array inside $new_array
foreach ($bepros as $key=>$bepro){
if(isset($new_bep)){
$x = 0 + $new_bep;
}else{
$x = 0;
}
$new_array[] = array_slice($all_rows,$x,$bepro);
// get where let before
$new_bep=$x + $bepro;
}