How to specifically count arrays inside of an array?
For example:
$array = [ 10, 'hello', [1, 2, 3, ['hi', 7]], [15, 67], 12 ]
Output should be 3.
You can use a recursive function for that. Let me give you an example:
<?php
$array = [ 10, 'hello', [1, 2, 3, ['hi', 7]], [15, 67], 12 ];
$arrayCount = 0; # this variable will hold count of all arrays found
# Call a recursive function that starts with taking $array as an input
# Recursive function will call itself from within the function
countItemsInArray($array);
function countItemsInArray($subArray) {
# access $arrayCount that is declared outside this function
global $arrayCount;
# loop through the array. Skip if the item is NOT an array
# if it is an array, increase counter by 1
# then, call this same function by passing the found array
# that process will continue no matter how many arrays are nested within arrays
foreach ($subArray as $x) {
if ( ! is_array($x) ) continue;
$arrayCount++;
countItemsInArray($x);
}
}
echo "Found $arrayCount arrays\n";
Example
https://rextester.com/SOV87180
Explanation
Here's how the function would operate.
[ 10, 'hello', [1, 2, 3, ['hi', 7]], [15, 67], 12 ] is sent to countItemsInArray function
This function looks at each item in the array
10 is reviewed. It's not an array, so the function goes to the next item
'hello' is reviewed. It's not an array, so the function goes to the next item
[1, 2, 3, ['hi', 7]] is evaluated. It is an array, so arrayCount is increased to 1 and the same function is called but the input to the function now is [1, 2, 3, ['hi', 7].
Remember, this same function called with the entire array is not dead. It's just waiting for this countItemsInArray([1, 2, 3, ['hi', 7]]) to be done
1 is evaluated. It's not an array, so the next item is evaluated
2 is evaluated ...
3 is evaluated ...
['hi', 7] is evaluated. It is an array. So, array count is increased to 2
countItemsInArray(['hi', 7]) is called.
Remember that countItemsInArray with the full array as the parameter AND countItemsInArray([1, 2, 3, ['hi', 7]]) are now waiting for countItemsInArray(['hi', 7]) to be done
hi is evaluated. That's not an array, so the function tests the next item
7 is evaluated. That's not an array either. So, the that function completes, giving control back to countItemsInArray([1, 2, 3, ['hi', 7]])
countItemsInArray([1, 2, 3, ['hi', 7]]) recognizes that it has nothing more to evaluate. Control is given back to countItemsInArray($array).
[15, 67] is evaluated. It is an array, so array count is increased to 3
countItemsInArray([15, 67]) is called, which evaluates 15 and 16 and determines that none of them are an array, so it gives control back to countItemsInArray($array)
countItemsInArray($array) evaluates 12 and determines that's not an array either. So, it ends its work.
Then, echo echoes out that array count is 3.
Recursively iterate your input array and restart the tally variable upon entering a new level in the array. Every time an array is encountered, add one and recurse. Pass up the tallies from the deeper levels and when the levels are completely traversed, return the top level's cumulative tally.
Code: (Demo)
$array = [10, [[[]],[]], 'hello', [1, 2, 3, ['hi', 7]], [15, 67], 12];
function array_tally_recursive(array $array): int {
$tally = 0;
foreach ($array as $item) {
if (is_array($item)) {
$tally += 1 + array_tally_recursive($item);
}
}
return $tally;
}
echo array_tally_recursive($array);
The documentation for min() shows the following example:
// Multiple arrays of the same length are compared from left to right
// so in our example: 2 == 2, but 4 < 5
$val = min(array(2, 4, 8), array(2, 5, 1)); // array(2, 4, 8)
Given the following code:
$input = [
[3, 6],
[2, 9],
];
var_dump(min(...$input)); // returns [2, 9] as expected
If you make the same array associative, it fails and always seems to just return the first array:
$input = [
["three" => 3, "six" => 6],
["two" => 2, "nine" => 9],
];
var_dump(min(...$input)); // returns ["three" => 3, "six" => 6]
Why?
Here's an example
According to the documentation values are compared using the standard comparison rules.
In the table of "comparison with various types" there, it states that if both operands are arrays and a key in operand 1 is not present in operand 2, then the arrays are not comparable. This is why min simply returns whatever is the first value in your array.
Specifically, arrays are compared as follows:
If one array has fewer values than the other, it is smaller.
Otherwise if any key in operand 1 is not in operand 2, the arrays are uncomparable.
For each value in operand 1 in turn, compare with the value with the same key in operand 2.
If the same, continue comparing values.
Otherwise the smaller array is the one with the smaller value.
Because they are not comparable, min is simply returning the first array in the list. If you swap the order, the other array will be returned. You can see this if you sort the array with sort($input). Every time you sort it the array is reversed.
To get the behaviour you desire, sort the arrays based on their values and then fetch the first element. But be aware that this will depend which key you defined first, so ["three" => 3, "six" => 6] is not the same as ["six" => 6, "three" => 3].
usort($input, function($a, $b) { return array_values($a) <=> array_values($b); });
var_dump($input[0]);
Just convert them to simple arrays and then return the associative array associated to the result of min.
<?php
function array_min_assoc(){
$args = func_get_args();
$not_assoc = array_map('array_values',$args);
$min = min(...$not_assoc);
$key = array_search($min, $not_assoc);
if ($key !== false){
return $args[$key];
}
}
$input = [
["three" => 3, "six" => 6],
["two" => 2, "nine" => 9],
];
var_dump(array_min_assoc(...$input));
/* returns
array(2) {
["two"] => int(2)
["nine"]=> int(9)
}
*/
I have the following array:
$foo = ["hello", "hi", [5, 10]];
I want to convert this to an associative array like so:
$foo = [
"0" => "hello",
"1" => "hi",
"2" => [5, 10],
];
How can I do this?
UPDATE
The reason I want to do this is because when I execute the shuffle method, I want to know what the original index was.
For example shuffle($foo) might return:
$foo = [
"1" => "hi",
"2" => [5, 10],
"0" => "hello",
];
This is your array:
$foo = ["hello", "hi", [5, 10]];
It's already associative. PHP adds keys for you, so it's the same as doing:
$foo = [0 => "hello", 1 => "hi", 2 => [5, 10]];
If you use string keys that are numbers, PHP converts them to numbers for you.
Your array doesn't need to be "associative" to make shuffle behave as you want. All PHP arrays are associative. You are looking for the wrong solution to your problem.
(See Andrea's answer, this is incorrect)
The only transformation from the previous state is that the keys are strings, rather than integers.
$keys = array_map('strval', array_keys($foo));
$values = array_values($foo);
$foo = array_combine($keys, $values);
There is no need to do something like this
The values are already "indexed". If you run
$foo = ["hello", "hi", [5, 10]];
echo $foo[0];
You will get hello (starting from 0)
Don't know what you're trying to achive, but as I see you want to convert your array indexes into strings:
$_foo = [
"0" => "hello",
"1" => "hi",
"2" => [5, 10],
];
$foo = [];
foreach($_foo as $key => $value) {
$foo[(string) $key] = $value;
}
var_dump($foo);
But I think that's just redicilous, to convert array indexes into strings. I don't know if you're aware, but you already can access your array with indexes: 0, 1 or 2.
var_dump($foo[1]); // hi
This question already has answers here:
Populate array of integers from a comma-separated string of numbers and hyphenated number ranges
(8 answers)
Closed 6 months ago.
I have a text box that a user can paste a string of comma separated numbers and it puts that into an array. However, in some cases that string may contain numbers separated by a dash and I need to fill in the missing numbers where that dash appears.
Example:
1, 4, 7, 20-25, 31, 46, 100
Needs to become:
1, 4, 7, 20, 21, 22, 23, 24, 25, 31, 46, 100
How might I go about doing this? The gap wont always be 5 numbers and there could be more than one set of dashed numbers in the string that is input.
Here is one way, without a regex:
It is:
Parsing the input string with str_getcsv() to separate it into its individual elements, then trimming whitespace around each element with trim() and array_map().
Iterating over each element, searching for a dash. If it finds a dash, it found a range of numbers, so it separates them into the start and end indexes by explode()ing on the dash, and forms the correct range between $start and $end with range(). It then merges that newly formed range with what has already been formed as the result using array_merge().
If, during iteration, it didn't find a dash, it assumes you have a literal number, and adds just that number to the resulting array.
That's it!
$input = '1, 4, 7, 20-25, 31, 46, 100';
$entries = array_map( 'trim', str_getcsv( $input));
$result = array();
foreach( $entries as $entry) {
if( strpos( $entry, '-') !== false) {
list( $start, $end) = explode( '-', $entry);
$result = array_merge( $result, range( trim($start), trim($end)));
} else {
$result[] = $entry;
}
}
You can see from this demo that it produces:
Array (
[0] => 1
[1] => 4
[2] => 7
[3] => 20
[4] => 21
[5] => 22
[6] => 23
[7] => 24
[8] => 25
[9] => 31
[10] => 46
[11] => 100 )
Edit: To form the resulting string, instead of the array, you just need a call to implode(), like so:
echo implode( ', ', $result);
This will produce:
1, 4, 7, 20, 21, 22, 23, 24, 25, 31, 46, 100
$data = '1, 4, 7, 20-25, 31, 46, 100';
$elts = explode(',', $data);
$res = array();
foreach ($elts as $elt) {
$elt = trim($elt);
if (preg_match('/(\d+)-(\d+)/', $elt, $matches) === 1) {
$res = array_merge($res, range($matches[1], $matches[2]));
} else {
$res[] = intval($elt);
}
}
var_dump($res);
My solution :DEMO HERE
First delete spaces using str_replace()
explode() string using , to array of integers.
explode() every element using - to check if it's a range or just a single integer.
if it's a range , loop using for() or range() and add $i to $newArray , else add the element to $newArray.
implode() using , and print the result.
PHP code :
<?php
$str=str_replace(' ', '', '1, 4, 7, 20-25, 31, 46, 100');
$arr = explode(',',$str);
foreach($arr as $elem){
$values=explode('-',$elem);
if(count($values)!=1) for($i=$values[0];$i<=$values[1];$i++) $newArr[]=$i;
else $newArr[]=$elem;
}
echo implode(',',$newArr);
?>
OUTPUT:
1,4,7,20,21,22,23,24,25,31,46,100
Simple code :
split string by delimiter , with explode();
remove space from the beginning and end of a string with trim;
function array_map is used so you don't need to do foreach loop just to apply trim() to every element;
create empty array where you will store new elements;
iterate over each number or range with foreach;
do points 1., 2. and 3. again on every iteration;
split string by delimiter - with explode() even if string doesn't have delimiter - in it; result will be array with one or multi elements;
if point 6. has array with multiple elements, that means that there is range involved, so use function range() to create an array containing a range of elements;
store new numbers to point 4. array with array_merge() function;
when point 4. array is full generated; sort values with sort(), so you don't have output like 5, 3, 1, 4 etc. (delete this function if you don't need this functionality);
remove duplicates with array_unique(), so you don't have output like 4, 4, 4, 4, 5 etc. (delete this function if you don't need this functionality)
to convert array back to string like you inputed, use implode() function.
|
function fillGaps($s) {
$s = array_map('trim', explode(',', $s));
$ss = [];
foreach ($s as $n) {
$n = array_map('trim', explode('-', $n));
if (count($n) > 1) $n = range($n[0], end($n));
$ss = array_merge($ss, $n);
}
sort($ss); # remove if you don't need sorting
$ss = array_unique($ss); # remove if duplicates are allowed
return implode(', ', $ss);
}
Example :
Your example :
echo fillGaps('1, 4, 7, 20 - 25, 31, 46, 100');
# 1, 4, 7, 20, 21, 22, 23, 24, 25, 31, 46, 100
This code will also work for multiple gaps, like :
echo fillGaps('100-105-110');
# 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110
You can even go reverse :
echo fillGaps('10-5');
# 5, 6, 7, 8, 9, 10
# or remove function sort() and output will be 10, 9, 8, 7, 6, 5
Remove duplicates :
echo fillGaps('2-4, 4, 4, 5');
# 2, 3, 4, 5
This can be done without the using a regular expression.
Get the user input, explode with , as the delimiter, and you'll get an array of all numbers.
Now use an if statement to check if the string contains a dash.
If it does, explode it again with the delimiter -. Now you'll get two numbers
Use range() to create an array of numbers within that range
Use implode() to join them by a comma.
As a useful function:
Code:
function fill_string_gaps($input)
{
$parts = explode(',', $input);
$resultArray = array();
foreach ($parts as $part) {
if(strpos(trim($part), '-')) {
list($num1, $num2) = explode('-', $part);
$expanded_num_array = range($num1, $num2);
$resultArray = array_merge($resultArray, $expanded_num_array);
} else {
$resultArray[] = trim($part);
}
}
$comma_separated = implode(', ', $resultArray);
return $comma_separated;
}
Usage:
$input = '1, 4, 7, 20-25, 31, 46, 100';
echo fill_string_gaps($input;)
Test cases:
echo fill_string_gaps('1-5');
echo fill_string_gaps('1-5, 12, 24');
echo fill_string_gaps('2, 2, 4, 5-8');
Output:
1, 2, 3, 4, 5
1, 2, 3, 4, 5, 12, 24
2, 2, 4, 5, 6, 7, 8
See it in action!