Algorithm to find amount of levels in an array of arrays - php

I'm working on an algorithm to calculate the amount of levels in an array of arrays.
The reason I need this is because I need to fetch a list of categories from the database that belongs to a category parent, and depending of the amount of levels this array has, I need to display an amount of category lists (to select categories).
So it would be a category list for each level of categories, for example
Vehicles
Cars
honda
Red
Blue
Yellow
ford
Red
suzuki
Red
Green
BMW
Motorcycles
bla bla
bla bla
Groceries
Fruits
Berries
Red
Strawberries
So I need a function to check the amount of levels of the selected parent, for example if i pass the ID of vehicles i want it to return 4 or 3 if we count Vehicles as level 0, so I know that if the client selected Vechicles from the first list I will have to display 3 more lists.
So far, what I have that is not working is
function count_children_level($list_of_children, $start_depth = 0){
// if the data being passed is an array
if(is_array($list_of_children)){
// amount of nodes is equal to the
$max = $start_depth;
foreach($list_of_children as $i){
$result = count_children_level($i, $start_depth + 1);
if ($result > $max){
$max = $result;
}
}
return $max;
}
//if is not array
else {
return $start_depth;
}
}
I really need to understand how this works because i have to work with several functions like this one, so please if you can, explain your answer in detail.
Thanks

The depth of a nested array is equal to the depth of the largest array in it + 1.
So for your recursive function, instead of passing the entire array every time, you can make an actual recursive call that only gets the depth of the sub-array. So this function returns 1 for a normal, flat array and 1 extra for each level.
<?php
function array_depth($array) {
// Determine largest sub-array. Start with 0 if there are no arrays at all.
$max = 0;
foreach ($array as $item) {
if (is_array($item)) {
// Make the recursive call, passing not $array, but the sub-array ($item)
// to the function again.
$depth = array_depth($item);
if ($depth > $max)
$max = $depth;
}
}
// Depth of this array is the depth of the largest sub-array + 1.
return $max + 1;
}
I called it like this:
echo array_depth(
array('x' =>
array('y' =>
array('z')))); // Returns 3.

My interpretation of what #GolezTrol said in their answer ("The depth of a nested array is equal to the depth of the largest array in it + 1"):
function array_depth($a)
{
// If $a is not an array or it's an empty array then its depth is 1
if (! is_array($a) || count($a) == 0) {
return 0;
}
// Otherwise, add 1 to the maximum depth of the elements it contains
return 1 + max(array_map('array_depth', $a));
}

Another solution with the RecursiveIteratorIterator class. In this way you don't need a recursive function:
$array = array(
'Vehicles' => array(
'Cars' => array(
'honda' => array(
'Red',
'Blue',
'Yellow',
)
)
)
);
function getTotalDepth($array) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array)
);
$max = 0;
foreach ($iterator as $element) {
if (!$iterator->callHasChildren()) {
$max = max($max, $iterator->getDepth());
}
}
return $max;
}
echo getTotalDepth($array);
Also very helpful if you want to iterate the complete array:
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $element) {
print_r($element);
echo '<br>';
}

Related

PHP Loop through an associative array

Edited to provide clarifications, based on the comments.
I have a dynamic associative array with keys and values that looks like this:
array: ["apples" => 4 "bananas" => 4 "cherries" => 4 "dates" => 3]
I would like to create another n-sized array (with dynamic n) that will loop through the array in series.
Example:
(if n = 6):
apples, cherries, apples
bananas, dates, bananas
cherries, apples, cherries
dates, bananas
apples, cherries
bananas, dates
n range is between 1 and sum of all values
the code I have so far is this:
function makeArray($commonWords){
$n = 6;
$result = array_fill(0,$n, '');
$i = 0;
while (list($key, $value) = each($commonWords)) {
$result[$i] = $result[$i] . $key;
$i++;
}
return $result;
}
Which provides this output:
array:6 [▼
0 => "apples"
1 => "bananas"
2 => "cherries"
3 => "dates"
4 => ""
5 => ""
]
But the 5th line needs to be "apples", the sixth needs to be "bananas".
Then on first line after "apples" needs to have "cherries", and so on, as in the example above.
Hope this provides clarification.
If I understand your question correctly, I think I have a solution.
I added the $n to the arguments list rather than defining it inside the function.
function makeArray($commonWords,$n=6){
$result = array_fill(0,$n,'');
$c = 0;
// while we still have words to print out
while(array_sum($commonWords)){
foreach($commonWords as $word=>$number){
// if this word is still available
if($number > 0){
// put the separator if we have looped through at least once
if($c >= $n){
$result[($c % $n)] .= ", ";
}
$result[($c % $n)] .= $word;
// reduce the number of this word available
$commonWords[$word]--;
$c++;
}
}
}
return $result;
}
It is not really clear what you are trying to accomplish, but you can do the following.
What you are describing here
But the 5th line needs to be "apples", the sixth needs to be
"bananas".
is Circular Linked list. But as the number of iterations cannot exceed the sum values of given array, this is somewhat circular linked list with a limit. You can read about Linked Lists in the wiki.
Luckily PHP has SplDoublyLinkedList class in the Standard PHP Library. But we have to shape it a little to serve our needs:
class CircularLinkedListWithLimit extends SplDoublyLinkedList
{
protected $limit;
public function __construct($limit)
{
$this->limit = $limit;
}
public function next()
{
$this->limit -= 1;
parent::next();
}
public function valid()
{
return $this->limit > 0
? parent::valid() || $this->rewind() || true
: false;
}
}
Having this class we can create our list:
$array = ["apples" => 4, "bananas" => 4, "cherries" => 4, "dates" => 3];
$limit = array_sum($array);
$list = new CircularLinkedListWithLimit($limit);
foreach ($array as $key => $_) {
$list->push($key);
}
Having this circular list we can populate our table (I hard coded $n here for the sake of simplicity, but you can wrap this in function):
$n = 6;
$i = 0;
$j = 0;
$table = array_fill($i, $n, []);
foreach ($list as $item) {
$table[$i][$j] = $item;
$i += 1;
if ($i >= $n) {
$i = 0;
$j += 1;
}
}
Having this table, you can do what you like. As you provided somewhat of expected output, here is code that will print it:
echo implode(PHP_EOL, array_map(function ($row) {
return implode(', ', $row);
}, $table));
This will result in
apples, cherries, apples
bananas, dates, bananas
cherries, apples, cherries
dates, bananas
apples, cherries
bananas, dates
Here is working demo.
As you can see almost all we do is using built-in functionality, bootstrapping it with simple non-nested loops to suit our needs. This is actually the goal of programming on high-level programming languages. Because the less code you write yourself, the fewer bugs you introduce into the system.

Know the count of for-each loop before executing that loop

How may i know that - In php how many times the foreach loop will get run, before that loop get executed..In other words i want to know the count of that particular loop. I want to apply some different css depends upon the count.
Use the function count to get the amount of numbers in your array.
Example:
$array = array('test1', 'test2');
echo count($array); // Echos '2'
Or if you want to be an engineer for-sorts you can set up something like so:
$array = array('test1', 'test2');
$count = 0;
foreach ($array as $a) { $count++; }
And that can count it for you, and the $count variable will hold the count, hope this helped you.
Simply count() the array and use the output as a condition, like:
if (count($array) > 100) {
// This is an array with more than 100 items, go for plan A
$class = 'large';
} else {
// This is an array with less than 100 items, go for plan B
$class = 'small';
}
foreach ($array as $key => $value) {
echo sprintf('<div id="%s" class="%s">%s</div>', $key, $class, $value);
}

append data in multidimensional array php

I want to import my table data into multi-dimensional array in PHP, the table data is continuously updating, so the matrix has to be updated as soon as data is inserted in the table.
How do we create a multi-dimensional array, insert data into it and then append the data dynamically into the array?
Usually, you got a "normal" one-dimensional array out of your table.
To make a multidimensional-array out of it, you have to put the one-dimensional array into a multidimensional array.
To do this, you could use something like:
$i = 0; //initialize i
while ($i < count($array)) { //as long as i is smaller than the array
foreach ($array as $key => $value) { //walk along the array
$newArray[$i][$key] = $value; // set the multidimensional array
}
$i++; // count one up
}
With this, you should get a multidimensional array looking like:
array(
'0' => array(
'0' => 'foo'
'1' => 'bar'
)
'1' => array(
'0' => 'foo'
'1' => 'bar'
)
)
and so on... hope this helps
For what you want to do i think should should look at objects approach because form the following comments
Tom, the scenario is i am maintaining a table for term frequency in the documents, so my column1 will be the different terms in all the documents, and a new column is added for every new document and is inserted with the respective term count, and if it has any new term;
I can identify the following
Team
Document
Rather than make document null only update the required team information
Example
// Porpulate Teams
$teams = array_map(function ($v) {return new Team($v);}, range("A", "Z"));
// Porpulate Documents
$doc = new SplObjectStorage();
for($i = 0; $i < 100; $i ++) {
$doc->attach(new Document($teams[array_rand($teams)]));
}
usort($teams, function($a,$b){ return $b->frequency - $a->frequency;});
//Top 10 Teams
echo "<pre>";
foreach (array_slice($teams, 0,10) as $team ) {
echo "Team ", $team->name, "\t", $team->frequency, PHP_EOL;
}
Output
Team P 9
Team N 8
Team S 7
Team Q 6
Team M 6
Team D 6
Team O 5
Team G 5
Team K 5
Team F 5
Class Used
class Document {
function __construct(Team &$team) {
//Do your metrix
$team->frequency ++;
}
}
class Team {
public $name;
public $frequency = 0;
function __construct($name) {
$this->name = $name;
}
}

PHP Array shuffle, keeping unique

this is my first php script and problem, I've searched hours with no conclusion other than looping a function" too many laterations". but it doesn't solve my problem I've never studied programming or what ever so I'm hoping that there is an educated person to fill me in on this:
I have an array that contains 120 elements; consists of duplicates eg:
myArray = [0]= item_1, [1] = item _1, [2] = item_2, [3] = item_3 ect..
Briefly I'm trying to make a flash php pokermachine but I need these items in the array to be shuffled BUT I do not want the duplicates to be next to each other after the shuffle but I need the duplicates to be still in the array
I can't do a loop function to check this because it will change the shuffle too many times which will effect the odds of the game: below is what I currently have:
/ * Removed the link here that is no longer available */
you may notice at times it will double up with 2 items in the same reel
Basically I created the virtual reel dynamically with php.ini file
these values are repeatedly pushed into an array($virtualreel) so the value may appear 10 times in the reel and another value will appear 5 times variating the odds. Then after I take a random slice() from the $virtualreel to display 3 vars from this reel and repeat the loop 4 more times for the other reels, also I only can shuffle once as I want the slice() to be from the same reels array order
I only shuffle every new spin not running loop functions to shuffle if I double up on a slice(array,3 items).
hope I've explained what I'm after well enough to give you guys an idea.
You can use this function:
<?php
function shuffleArray($myArray) {
$value_count = array_count_values($myArray);
foreach($value_count as $key=>$value) {
if ($value > count($myArray)/2) {
return false;
}
}
$last_value = $myArray[count($myArray) - 1];
unset($myArray[count($myArray) - 1]);
$shuffle = array();
$last = false;
while (count($myArray) > 0) {
$keys = array_keys($myArray);
$i = round(rand(0, count($keys) - 1));
while ($last === $myArray[$keys[$i]]) {
$i = round(rand(0, count($keys) - 1));
}
$shuffle[] = $myArray[$keys[$i]];
$last = $myArray[$keys[$i]];
unset($myArray[$keys[$i]]);
}
if ($last_value === $last) {
$i = 0;
foreach($shuffle as $key=>$value) {
if ($value !== $last_value) {
$i = $key;
break;
}
}
array_splice($shuffle, $i + 1, 0, $last_value);
} else {
$shuffle[] = $last_value;
}
return $shuffle;
}
print_r(shuffleArray(array(1,5,5,3,7,7)));
Why not just:
Edit :
$shuffled = array();
while(count($to_shuffle) > 0):
$i = rand(0, count($to_shuffle)-1);
$shuffled[] = $to_shuffle[$i];
array_splice($to_shuffle, $i, 1,null);
endwhile;
I think this is what you were expecting, if you don't mind not preserving the association between keys and values.

PHP function to match key in a range of values

I have a array with the lvl=>xp correspondance and I would make a function that return the lvl for a specific xp. like $lvl = getLvlOf(15084); //return 5
$lvl_correspondance = array(
1=>100,
2=>520,
3=>2650,
4=>6588,
5=>12061,
6=>23542,
...
n=>xxxxxx
);
I search the easyest and ressourceless way to do it.
Sorry for my poor english :(
Assuming the level values in the array are kept sorted, e.g. (it's 100,200,300, 400, etc... and not 200,500,100,300,400), then a simple scan will do the trick:
$xp = 15084;
$last_key = null;
foreach($lvl_correspondenance as $key => $val) {
if ($val < $xp) {
$last_key = $key;
} else {
break;
}
}
That'll iterate through the array, and jump out as soon as the XP level in the array is larger than the XP level you're looking for, leaving the key of that "last" level in $last_key
function getLvlOf($lvl, $int){
foreach($lvl as $level=>$exp){
if($exp > $int){
return $level-1;
}
}
}
it's O(n) so no magic there...
It looks like your array can be computed live -
XP = exp( 3 * ln(LVL) + 4 ) * 2
You can do the same in reverse, in O(1):
LVL = exp(ln(XP/2) - 4 / 3)
I rounded the equation, so there may be a +/- 1 issue
Good Luck!
Not good if you have very high level values, but:
$lvl = $lvl_correspondance[array_search(
max(array_intersect(
array_values($lvl_correspondance),
range(0,$points)
)
),
$lvl_correspondance
)];
You can use array_flip if the xp levels are distinct. Then you can simply access the level number using the xp as index:
$levels = array_flip($lvl_correspondance);
$lvl = $levels[15084];
EDIT: But maybe a function would be better to fetch even the xp levels in between:
function getLvlOf($xp) {
// get the levels in descending order
$levels = array_reverse($GLOBALS['lvl_correspondance'], true);
foreach ($levels as $key => $value) {
if ($xp >= $value)
return $key;
}
// no level reached yet
return 0;
}

Categories