Php recursive array counting - php

I'm trying to write a function which counts array elements recursively.
But result is false.
What could it be problem?
$schema = array(
'div' => array(
'class' => 'lines',
'div' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Soap'
),
'layer' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Ball'
)
)
);
function count_r($array, $i = 0){
foreach($array as $k){
if(is_array($k)){ $i += count_r($k, count($k)); }
else{ $i++; }
}
return $i;
}
echo count_r($schema);

This is the second result on Google so I'm adding this as a reference. There is already a built-in function in PHP that you can use: count().
You can use it like this:
count($schema, COUNT_RECURSIVE);
This function can also detect recursion to avoid infinite loops so it's safer than solutions in other answers.

PHP has a built-in method for this purpose, called count(). If passed without any additional parameters, count() returns the number of array keys on the first level. But, if you pass a second parameter to the count method count( $schema, true ), then the result will be the total number of keys in the multi-dimensional array. The response marked as correct only iterates first and second level arrays, if you have a third child in that array, it will not return the correct answer.
This could be written as a recursive function, if you really want to write your own count() method, though.

Transferred from comment below answer:
Basically, as you're adding the count_r of that array to the current level, you don't need to factor in the count in the second argument - you'd basically be adding it twice. You need the "1" in there, however, to count the array itself. If you'd want to count just the elements and not the arrays, you would just make the "1" a "0".
$schema = array(
'div' => array(
'class' => 'lines',
'div' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Soap'
),
'layer' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Ball'
)
)
);
function count_r($array, $i = 0){
foreach($array as $k){
if(is_array($k)){ $i += count_r($k, 1); }
else{ $i++; }
}
return $i;
}
echo count_r($schema);
This is tested and works correctly.

your function could be simple as
function count_r($array, $i = 0){
foreach($array as $k){
$i++;
if(is_array($k)){
$i += count_r($k);
}
}
return $i;
}

I have altered you code.. Please check it. It is working fine.
$schema = array(
'div' => array(
'class' => 'lines',
'div' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Soap'
),
'layer' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Ball'
)
)
);
function count_r($array){
foreach($array as $k){
if(is_array($k)){
$i += count_r($k);
}
else{
$i++;
}
}
return $i;
}
echo count_r($schema);

If you want to count specific keys/values you can use the built-in array_walk_recursive with a closure function.
$schema = array(
'div' => array(
'class' => 'lines',
'div' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Soap'
),
'layer' => array(
'span' => array(
'key' => 'Product Name'
),
'val' => 'Ball'
)
)
);
$elements = 0;
array_walk_recursive($schema, function($value, $key) use (&$elements) {
if (strstr($value, 'Product')) $elements++;
});
print $elements; // 2
/*
Values
lines
Product Name
Soap
Product Name
Ball
Keys
class
key
val
key
val
*/

count($array, COUNT_RECURSIVE)-count($array)

Related

PHP Write "an array of arrays" dynamically inside an array of arrays, while declaring it [duplicate]

This question already has answers here:
PHP is there a way to add elements calling a function from inside of array
(3 answers)
Closed last month.
In the middle of declaring an array of arrays, I want to "write" an array of arrays generated by my function.
I have a working example when I:
simply store my function-generated arrays into a variable and
then call each array from that function by its key,
but I can't find a command to simply call everything at once.
Here is the code which (I hope) explains it:
<?php
// A. (THIS WORKS)
// A1: A function that returns an array of arrays
function my_arrays_building_function() {
$first_array = array(
'id' => 'my_array_1',
'type' => 'some-type',
'title' => 'My title 1',
);
$second_array = array(
'id' => 'my_array_2',
'type' => 'some-type',
'title' => 'My title 2',
);
// ... and so on, many more.
return array(
'first-array' => $first_array,
'second-array' => $second_array,
// ... and so on.
);
// NOTE there are tens or hundreds of returned arrays here.
}
// A2: Store my arrays in a variable
$my_array = my_arrays_building_function();
// A3: Inside an array (of arrays), I simply "write" my arrays INDIVIDUALLY and THAT works
array(
array(
'id' => 'dummy_preexisting_array_1',
'type' => 'some-type',
),
array(
'id' => 'dummy_preexisting_array_2',
'type' => 'some-type',
),
// HERE THERY ARE, INDIVIDUALLY, COMMA SEPARATED
$my_array[ 'first-array' ],
$my_array[ 'second-array' ],
array(
'id' => 'dummy_preexisting_array_n',
'type' => 'some-type',
)
),
/** -------------------- //
THE ISSUE
// -------------------- **/
// B: HOW DO I "write" THEM ALL AT ONCE???
// B1: The same as A1
function my_arrays_building_function() {
$first_array = array(
'id' => 'my_array_1',
'type' => 'some-type',
'title' => 'My title 1',
);
$second_array = array(
'id' => 'my_array_2',
'type' => 'some-type',
'title' => 'My title 2',
);
// NOT SURE I SHOULD RETURN LIKE THIS
return array(
'first-array' => $first_array,
'second-array' => $second_array
);
}
// B2: Same as A3, Inside an array (of arrays), I "write" my arrays BUT NOW I WANT TO "WRITE" THEM ALL AT ONCE
array(
array(
'id' => 'dummy_preexisting_array_1',
'type' => 'some-type',
),
array(
'id' => 'dummy_preexisting_array_2',
'type' => 'some-type',
),
/** >>>> I need my arrays here ALL AT ONCE aka NOT INDIVIDUALLY AS IN EXAMPLE A. <<<< **/
/**
* In other words, while I'm declaring this array,
* I simply need all my arrays from my_arrays_building_function()
* "written" here with a simple command instead of calling hundreds
* of arrays individually as in the first example
*/
array(
'id' => 'dummy_preexisting_array_n',
'type' => 'some-type',
)
), /* this goes on as it's a part of even bigger array */
Although I wouldn't recommend declaring hundreds of array variables inside a function because that's crazy, but for now, you can use get_defined_vars() to get over this issue.
You will also need to filter out the variables which are arrays and has the keys id, type and title as there are could be several other variables defined apart from this.
Snippet:
<?php
array_filter(get_defined_vars(), fn($val) => is_array($val) && isset($val['id'], $val['type'], $val['title']));
Online Demo
Not too sure if I'm understanding this correctly but from what I assume, you just want to return an array with a bunch of others inside it that you define throughout the function?
A simple approach for this would be to define your output variable immediately and add all of your other arrays to it:
function my_arrays_building_function() {
$output = [];
$output[] = [
'id' => 'my_array_1',
'type' => 'some-type',
'title' => 'My title 1',
];
$output[] = [
'id' => 'my_array_2',
'type' => 'some-type',
'title' => 'My title 2',
];
return $output;
}
Thank you to #Rylee for suggesting Array Unpacking.
The final code would look like this:
// C1: A function that returns an array of arrays BUT without keys
function my_arrays_building_function() {
$first_array = array(
'id' => 'my_array_1',
'type' => 'some-type',
'title' => 'My title 1',
);
$second_array = array(
'id' => 'my_array_2',
'type' => 'some-type',
'title' => 'My title 2',
);
// ... and so on, many more.
// Then return but now I don't assign keys, I only list the vars.
return array(
$first_array, $second_array, ... and so on.
);
}
// C2: Inside an array (of arrays), I use Array Unpacking, THAT'S WHAT I WAS LOOKING FOR, UNPACK ARRAY! SEE BELOW
array(
array(
'id' => 'dummy_preexisting_array_1',
'type' => 'some-type',
),
array(
'id' => 'dummy_preexisting_array_2',
'type' => 'some-type',
),
// HERE I UNPACK MY ARRAY BY USING ... THE THREE DOTS ARE THE KEY
... my_arrays_building_function(),
array(
'id' => 'dummy_preexisting_array_n',
'type' => 'some-type',
)
),
Hooray!

How to get a specific value based on adjacent key from a keyless multidimensional array?

Consider the following array:
$serviceNames = array(
0 => array(
'language' => 'en',
'value' => 'something',
'type' => 'name',
),
1 => array(
'language' => 'fi',
'value' => 'jotain',
'type' => 'name',
),
2 => array(
'language' => 'sv',
'value' => 'någonting',
'type' => 'name',
),
);
I need to get the 'value' definitions based on language. The problematic part is that the array $serviceNames does not have a predefined length (comes originally as a JSON file from an API), and the items can come in any order (in my example it goes like en, fi, sv, but it could be de, en, sv, fr... you get it).
If I wanted to get 'value' within the array where 'language' equals to 'en', how could I do that?
My advice is that you make the array associative.
Once that is done you access the value by ["language"]["value"].
$serviceNames = array_column($serviceNames, Null, "language");
echo $serviceNames["fi"]["value"]; //jotain
echo $serviceNames["en"]["value"]; //something
echo $serviceNames["sv"]["value"]; //någonting
https://3v4l.org/ssGQa
You can array_search() and array_column() function. first find the key where "en" is in the array then get the value.
$key = array_search('en', array_column($serviceNames, 'language'));
echo $serviceNames[$key]['value'];
Demo
simple:
$serviceNames = array(
0 => array(
'language' => 'en',
'value' => 'something',
'type' => 'name',
),
1 => array(
'language' => 'fi',
'value' => 'jotain',
'type' => 'name',
),
2 => array(
'language' => 'sv',
'value' => 'någonting',
'type' => 'name',
),
);
function myfunction(array $serviceNames, $field)
{
foreach($serviceNames as $service)
{
if ( $service['language'] === $field )
return $service['value'];
}
return false;
}
echo myfunction($serviceNames, 'en');
Output will : something
you would have to go through each elements of the array, using the foreach statement.
something like:
function getValueForLang($lang, array $arr)
{
foreach ($arr as $desc) {
if ($desc['language'] == $lang) {
return $arr['value'];
}
}
return null;
}
getValueForLang('en', $serviceNames); // gets your value, null if not found
see also:
https://secure.php.net/manual/en/control-structures.foreach.php

PHP: Sort array according to another array

I have a problem in php, i tried searching but i only got more confused. I need to sort an array according to another array using the "priority" key value.
This is the order i need it filtered with.
$custom_order = array(
array('priority' => 0, 'field_id' => 'password'),
array('priority' => 1, 'field_id' => 'username')
);
And this is the array that needs to be filtered
$default_order = array(
'name' => array(
'username' => array(
'type' => 'text',
'priority' => 0
),
'password' => array(
'type' => 'password',
'priority' => 1
),
),
);
And this is the order that i would like to get
$final_order = array(
'name' => array(
'password' => array(
'type' => 'password',
'priority' => 0
),
'username' => array(
'type' => 'text',
'priority' => 1
),
),
);
I'm confused, i'm not sure whether i should use uasort, or array_intersect, by reading other articles about it i got more confused. Can anybody please explain how would i go to sort the array in this way?
Thank you so much.
If priority is the actual sort criteria, then usort would be an easy way:
usort( $array['name'], function($a, $b) {
return ($a->priority < $b->priority) ? -1 : 1;
});
This just compares the value of priority between elements of the array. If not, perhaps you could enhance your example to show how the sort should actually be determined.
EDIT: Based on the clarified question.
Because of the structure of your arrays, I don't believe that you will be able to just use PHP functions.
First, arrange the priorities in a form better for prioritzing:
$priorities = array();
foreach ($custom_order as $item) {
$priorities[$item->field_id] = $item->priority;
}
Then assign updated priorities to the $default items:
$final_order = array();
foreach ($default_order as $item) {
$item->priority = $priorities[$item->type];
}
Then do the sort originally suggested to get them in priority order:
usort( $final_order['name'], function($a, $b) {
return ($a->priority < $b->priority) ? -1 : 1;
});

Conditionally adding item to multidimensional array

I have been looking how to do this and am a bit stumped.
My array is as follows:
$returndata->setup_array = array(
'General' => array(
'Main Details' => 'setup/maindets',
'Directories' => 'directories',
'Extension Allocation' => 'xtnallo',
'List Holidays' => 'setup/holidays',
'List Group VM' => 'groupvm',
'Conference Rooms' => 'confroom'
),
'Offices' => array(
'List Offices' => 'iptoffices'
),
'Users' => array(
'List Users' => 'iptusers'
),
'Phones' => array(
'List Phones' => 'iptphones'
),
);
However I have 1 item that on a certain condition(triggered by the users session) that needs to be added to the listin the general array. The section being 'View Details => setup/viewdetails'. I have tried array push (probably incorrectly) but this adds the item as another array at the end under the main array.
I want/need it to work like this:
$returndata->setup_array = array(
'General' => array(
$viewdets
'Main Details' => 'setup/maindets',
'Directories' => 'directories',
'Extension Allocation' => 'xtnallo',
'List Holidays' => 'setup/holidays',
'List Group VM' => 'groupvm',
'Conference Rooms' => 'confroom'
),
'Offices' => array(
'List Offices' => 'iptoffices'
),
'Users' => array(
'List Users' => 'iptusers'
),
'Phones' => array(
'List Phones' => 'iptphones'
),
);
$viewdets = "'View Details' => 'setup/viewdetails'";
and still be interpreted as a functioning array for use as a menu.
$returndata->setup_array['General']['View Details'] = 'setup/viewdetails'
Cheers Rick!
You can use ArrayObject to have the array as a reference:
$a = new ArrayObject();
$b = array(
"a" => $a
);
$a[] = "foo";
print_r($b);
What did you try calling array_push() on? Have you tried
array_push($returndata->setup_array['General'], $viewdets);
You would need to add the variable to the specific depth of the array you wanted it to be present. check out array_push here, there's also a short language syntax that avoids the function call:
$returndata->setup_array['General'][] = $viewdets;

Adding to Array where Key equals Variable

I always have trouble wrapping my head around these foreach array things, and SO has been an incredibly value resource in getting me there, so I hope you guys can help with this one.
public function progress_bar()
{
$items = array(
array(
'step-name' => 'Setup',
'url' => '/projects/new/setup/',
),
array(
'step-name' => 'Artwork',
'url' => '/projects/new/artwork/',
),
array(
'step-name' => 'Review',
'url' => '/projects/new/review/',
),
array(
'step-name' => 'Shipping',
'url' => '/projects/new/shipping-info/',
),
array(
'step-name' => 'Billing',
'url' => '/projects/new/billing/',
),
array(
'step-name' => 'Receipt',
'url' => '/projects/new/receipt/',
),
);
// Status can be active, editing, or complete.
foreach ($this->progress as $step => $status)
{
foreach ($items as $item)
{
$item['step-name'] == ucfirst($step) ? $item['status'] = $status : '';
}
}
return $items;
}
$this->progress contains an array of statuses ('setup' => 'active', 'artwork' => 'editing')
I want to add to the $items array the status of each matching item in $this->progress
$items = array(
array(
'step-name' => 'Setup',
'url' => '/projects/new/setup',
'status' => 'active',
),
etc...
);
If I understand your question correctly, the problem is that you are trying to add an array element to $items, but what you're actually doing is adding the element to a temporary variable ($item), which does not reference the original $items variable.
I'd suggest approaching it like this:
foreach ($this->progress as $step => $status)
{
// Having the $key here allows us to reference the
// original $items variable.
foreach ($items as $key => $item)
{
if ($item['step-name'] == ucfirst($step) )
{
$items[$key]['status'] = $status;
}
}
}
return $items;
Are you locked into using an array to store $items? If so, you're going to be stuck doing a nested loop ("For each element in $this->progress, check each element in $items. If match, update $items" or something like that). If you have some flexibility, I would use a hash for $items (associative array in php-speak), where the index is the step name. So $items['Setup'] would contain 'url' => ... and 'status' => ... etc. Does that make sense? Then your algorithm breaks down to "For each element in $this->progress, get the element in $items by name ($items[$step_name]) and update it's info."
I would change how you have the $items array keyed and do it like this. Stops you from having the nested loops.
public function progress_bar()
{
$items = array(
'Setup' => array(
'url' => '/projects/new/setup/',
),
'Artwork' => array(
'url' => '/projects/new/artwork/',
),
'Review' => array(
'url' => '/projects/new/review/',
),
'Shipping' => array(
'url' => '/projects/new/shipping-info/',
),
'Billing' => array(
'url' => '/projects/new/billing/',
),
'Receipt' => array(
'url' => '/projects/new/receipt/',
)
);
// Status can be active, editing, or complete.
foreach ($this->progress as $step => $status)
{
$item[ucfirst($step)]['status'] = $status;
}
return $items;
}

Categories