When retrieving a hierarchical structure from MySQL (table with one ID column and one PARENT column signifying the hierarchical relationships), I map the result into an enumerated array as follows (for this example the numbers are arbitrary):
Array ( [3] => Array ( [7] => Array () ), [7] => Array ( [8] => Array () ) )
Notice 3 is the parent of 7, and 7 is the parent of 8 (this could go on and on; and any parent could have multiple children).
I wanted to shrink this array into a nested multidimensional array as follows:
Array ( [3] => Array ( [7] => Array ( [8] => Array () ) ) )
That is, each NEW id is automatically assigned an empty array. Regardless, any ID's children will be pushed into their parent's array.
Take a look at the following illustration for further clarification:
alt text http://img263.imageshack.us/img263/4986/array.gif
This will probably result in a complicated recursive operation, since I always have to check whether a parent with any certain ID already exists (and if so, push the value into its array).
Is there a built-in php function that can assist me with this? Do you have any idea as to how to go about constructing this? For what it's worth I'm using this to built a navigation bar in wordpress (which can contain categories, subcategories, posts... essentially anything).
The idea is that you keep an auxiliary array with all the nodes (parent and child) you find. The values of this arrays are references that back your result.
This builds the tree in linear time (array_key_exists does a hash table lookup, which is on average O(1)):
//table contains (id, parent)
$orig = array(
11 => 8,
7 => 3,
8 => 7,
99 => 8,
16 => 8,
);
$childrenTable = array();
$result = array();
foreach ($orig as $n => $p) {
//parent was not seen before, put on root
if (!array_key_exists($p, $childrenTable)) {
$childrenTable[$p] = array();
$result[$p] = &$childrenTable[$p];
}
//child was not seen before
if (!array_key_exists($n, $childrenTable)) {
$childrenTable[$n] = array();
}
//root node has a parent after all, relocate
if (array_key_exists($n, $result)) {
unset($result[$n]);
}
$childrenTable[$p][$n] = &$childrenTable[$n];
}
unset($childrenTable);
var_dump($result);
gives
array(1) {
[3]=>
array(1) {
[7]=>
array(1) {
[8]=>
array(3) {
[11]=>
array(0) {
}
[99]=>
array(0) {
}
[16]=>
array(0) {
}
}
}
}
}
EDIT: unset $childrenTable in the end to clear reference flags. In practice, you will probably want to do the operation inside a function anyway.
This question and it's answers should be helpful to you: turn database result into array.
Be sure to read the PDF presentation by #Bill Karwin, specifically the topics regarding the Closure table.
Related
This question already has answers here:
How to remove duplicate values from an array in PHP
(26 answers)
Closed 4 years ago.
I have an array and it has repeating items. So I trying to delete item if it repeats. For example:
$links:
string(35) "/mjr/semba-tower/outline/index.html"
[1]=>
string(38) "/mjr/mc-futsukaichi/outline/index.html"
[2]=>
string(31) "/mjr/chihaya/outline/index.html"
[3]=>
string(35) "/mjr/semba-tower/outline/index.html"
As you see 2 semba-towers in the array and I want to delete one of if.
I tried this, but output returns 0 item.
$output = [];
foreach(array_count_values($links) as $value => $count)
{
if($count == 1)
{
$output[] = $value;
}
}
var_dump($output);
Any other way to fix this problem?
Use the PHP array_unique() function
You can use the PHP array_unique() function to remove the duplicate elements or vlaues form an array. If the array contains the string keys, then this function will keep the first key encountered for every value, and ignore all the subsequent keys.
$links = array(
"/mjr/semba-tower/outline/index.html",
"/mjr/mc-futsukaichi/outline/index.html",
"/mjr/chihaya/outline/index.html",
"/mjr/semba-tower/outline/index.html"
);
// Deleting the duplicate items
$result = array_unique($links);
print_r($result);
OUTPUT:
Array ( [0] => /mjr/semba-tower/outline/index.html [1] => /mjr/mc-futsukaichi/outline/index.html [2] => /mjr/chihaya/outline/index.html )
This question already has an answer here:
How right replace value element in array?
(1 answer)
Closed 5 years ago.
I am using WordPress and have a custom post type setup with a set number of options, with the basic breakdown being something like this:
30" Round Table
Buffet
Cocktail
Dessert
Banquet Table
Buffet
Gift
DJ Table
I am trying to collect all items into a grouped collection, for use with <optgroup /> to show all tables under Buffet, all under Cocktail, etc. like so:
Buffet
30" Round Table
Banquet Table
Cocktail
Banquet Table
[and so on]
The PHP issue I'm running into is I have a master array that has all the types (buffet, cocktail, etc.) along with an (initially) empty array element called tables for me to add all the tables that support that specific type. Retrieving the options from ACF works fine, as does retrieving the individual posts from WordPress, so this is strictly a packaging issue to kick back as JSON. Here is a code example:
//gets the field associated with the different table types (Buffet, Cocktail, etc.)
$field = get_field_object('field_577ff065e6699');
$list = array();
//create the base array
foreach ($field["choices"] as $type) {
$list[] = array
(
"type" => $type, //Buffet, Cocktail, etc.
"tables" => array() //placeholder to add WP items
);
}
//now get all tables
$tablesQuery = new WP_Query( array( 'post_type' => 'table', 'posts_per_page' => -1, 'order' => 'ASC' ) );
//loop through and add the table(s) to their categories
while ( $tablesQuery->have_posts() ) : $tablesQuery->the_post();
//gets the types supported by this table
$tableTypes = get_field('options');
//individual types
foreach ($tableTypes as $tableType) {
//all types
foreach ($list as $supportedType) {
//see if there is a match and add it
if ($tableType == $supportedType["type"]) {
//add to the array since it matches
$table = array
(
"name" => get_the_title(),
"sqft" => (int)get_field('square_footage'),
"seats" => (int)get_field('seats')
);
array_push($supportedType["tables"], $table);
//shows the single table above, but nothing prior
print_r($supportedType);
}
}
}
endwhile;
wp_reset_postdata();
//all "tables" arrays are empty here
var_dump($list);
The output of print_r($supportedType) above ends up showing all data, however the tables entry is always only one element when it should be multiple:
Array
(
[type] => Buffet
[tables] => Array
(
[0] => Array
(
[name] => 30” Round Bistro/Cocktail Table
[sqft] => 42
[seats] => 2
)
)
)
Array
(
[type] => Cake
[tables] => Array
(
[0] => Array
(
[name] => 30” Round Bistro/Cocktail Table
[sqft] => 42
[seats] => 2
)
)
)
[.. snip ..]
Finally, when I do the var_dump($list) at the end, all of the types show up but their associated tables arrays are all empty:
array(11) {
[0]=>
array(2) {
["type"]=>
string(6) "Buffet"
["tables"]=>
array(0) {
}
}
[1]=>
array(2) {
["type"]=>
string(4) "Cake"
["tables"]=>
array(0) {
}
}
This has me completely lost, even though it has to be something incredibly basic that I'm missing. Any ideas why tables is empty, despite using array_push on the looped item? I also tried $supportedType["tables"][] = $table, but that has the same effect.
I think you're working with a copy of the array inside this foreach: foreach ($list as $supportedType) {
Try changing it to
foreach ($list as $key => $supportedType) {
...
// this operates on the copy
// array_push($supportedType["tables"], $table);
// operate on the original instead
array_push($list[$key]["tables"], $table);
...
}
I have to insert some data from an excel document into the database.
The data has been saved as .csv and then added into an array through PHP.
The data look like:
Column A Column B Column C
100 200 100
50 10 100
200 200 100
30 10 300
Then I use this to separate each columns (within a foreach loop)
list( $columnA, $columnB) = explode( ',', $values[0] );
$columnA= array($columnA);
print_r($columnA);
The above code prints the values of each column.
So I'm trying to find a way to remove duplicates from each column and then from each row (no matter what the column name is). I want to remove duplicates from the whole document. For the data I posted for example I just need the values 100,200,50,10,30,300 (only the unique values from the whole doc).
UPDATE:
What the original array (the one I've created by using for loop and passing all data from .CSV file) shows:
Array ( [0] => G2100,100%,,,,,,,,,200,0.24,77,51,2,47, )
Array ( [0] => G2101,100%,,,,,,,,,200,0.24,77,42,15,43, )
Array ( [0] => G2102,30%,,,,,,,,,200,0.24,77,38,25,37, )
So by using the list function I mentioned before I split all columns and get the values for each column. THEN if i print $columnB array for example it shows this:
Array ( [0] => 100%) Array ( [0] => 100%) Array ( [0] => 30%)
and so on. When I use unique_array it does nothing.
$columnB = array_unique($columnB, SORT_REGULAR);
I tried to use array_map but it doesn't work either.
Any help would be much appreciated.
I don't understand why does array_unique not works in your case, because actually it solves the problem:
<?php
$columnA = array(50,50,200,10);
$columnB = array(100,50,200,100);
$columnC = array(150,50,250);
$merged = array_merge($columnA, $columnB, $columnC);
$result = array_unique($merged);
var_dump($result);
?>
And output is:
array(6) {
[0]=>
int(50)
[2]=>
int(200)
[3]=>
int(10)
[4]=>
int(100)
[8]=>
int(150)
[10]=>
int(250)
}
This is an trivial example, but if you can manage that your inputs are like arrays above, then you can use array_unique to have only unique values...
EDIT 1:
To remove % sign from string just use rtrim where is needed:
$string = '100%';
$trimmed = (int)rtrim($string,'%');//make it int(or float if you like)
var_dump($trimmed);
EDIT 2:
Related to looping through arrays:
//I suppose this
//Array ( [0] => 100%) Array ( [0] => 100%) Array ( [0] => 30%)
//maps to this
$columnA = array(
0=>array('100%'),
1=>array('100%'),
2=>array('30%')
);
//go through every element
$temp = array();
foreach($columnA as $subArray){
//in this case when we know that there is only one element in the array we can do next:
$temp[] = $subArray[0];
}
$result = array_unique($temp);
echo "<pre>";
var_dump($result);
echo "</pre>";
And this would be the output:
array(2) {
[0]=>
string(4) "100%"
[2]=>
string(3) "30%"
}
Let's say this is my json(I wont be able to change this json file):
{"success":true,
"users":{
"1036344647":{"name":"joel", "age":18},
"1036344365":{"name":"klant", "age":24},
"1036344352":{"name":"grabben", "age":23}
}
}
I also have a php code that collect this json data and decodes it.
$data = json_decode(file_get_contents("http://example.com/users/json"));
As you can see in my json each user has a random generated id that will change everytime i collect the data from my json file. Basicly i don't know any of the user ids.
But i still want to be able to print out a users name depending on it's position in the list.
For example: echo $data->users->[THE SECOND ID(the id with a user named "klant")]->name;
So i know this could be done easy if the json file was written as a array, but it is not.
Is there any way i can print out a object in the json depending on it's position in the list?
Try this:
function findUser($id)
{
$data = json_decode(file_get_contents("http://example.com/users/json"));
return isset($data[0]->users->$id) ? $data[0]->users->$id : null;
}
$user = findUser(1036344365);
var_dump($user);
It will output
object(stdClass)#4 (2) { ["name"]=> string(5) "klant" ["age"]=> int(24) }
You can build a 2nd array by "throwing away" those unique IDs with array_values() like so:
$array = json_decode($yourJSON, true);
$justValues = array_values( $array["users"] );
print_r( $justValues );
Output:
Array
(
[0] => Array
(
[name] => joel
[age] => 18
)
[1] => Array
(
[name] => klant
[age] => 24
)
[2] => Array
(
[name] => grabben
[age] => 23
)
)
You can then access each person's data like so:
echo $justValues[1]["name"]; // klant
No, it's not possible. JavaScript objects by design are unaware of "position" of their key-value pairs. Behind the scenes, that's what makes them powerful.
The first step is to rethink why you need to work with the JSON according to position.
I'm working on a comment system, in which one is able to reply to another comment, making it appear below the commented comment. I have a bunch of parent/child pairs, to which end I use recursion to convert this into a tree structure:
private function _create_tree_from_comments($comment_id, $entry_id) {
$node_set = array();
$children = $this->comment_model->get_children($comment_id, $entry_id);
// check if we're a leaf
if($children->num_rows() < 1) {
return $comment_id;
}
foreach($children->result() as $child) {
$node_set[$child->child] = $this->_create_tree_from_comments($child->child, $entry_id);
}
return $node_set;
}
Now this is all fine and well, but the problem is that I need to save information about intermediate nodes. Currently all I can do is save whatever information I need in another array at a leaf, since the intermediate nodes have responsibility to keep information about their children.
So my question is really: How am I able to save information about intermediate nodes without breaking the relationship there is between the parent and its children.
Some sample output of the function:
Array
(
[5] => 5
[2] => 2
[1] => Array
(
[4] => Array
(
[6] => 6
)
[3] => 3
)
)
Each node in your tree could be an array like:
Array (
['comment_id'] => 7
['children'] => Array (...)
['whatever_else'] => 'foo'
)
So your function - instead of returning a comment ID or an array - would always return an array that contains possibly a comment id, children or empty array if no children, and anything else you wish to store
private function _create_tree_from_comments($comment_id, $entry_id) {
$node_set = array();
$children = $this->comment_model->get_children($comment_id, $entry_id);
// check if we're a leaf
if($children->num_rows() < 1) {
$node_set['comment_id'] = $comment_id;
}
$node_set['children'] = array();
foreach($children->result() as $child) {
$node_set['children'][] = $this->_create_tree_from_comments($child->child, $entry_id);
}
$node_set['whatever_else'] = 'foo';
return $node_set;
}