PHP recursive array_search - php

From the code below I can compare the 2 arrays and find out the $subset elements position range in $array.
$array = [8,2,3,7,4,6,5,1,9];
$subset = [6,3,7];
function get_range($array, $subset)
{
$min = sizeof($array);
$max = 0;
foreach($subset as $value) {
$occurrence = array_search($value, $array);
if( $occurrence < $min ) {
$min = $occurrence;
}
if( $occurrence > $max ) {
$max = $occurrence;
}
}
return [$min, $max];
}
$range = get_range($array, $subset); // result as an array -> [2, 5]
However, I want to do a recursive array_search for my multidimentional array like:
$subset = array (
array(6,3,7),
array(4,2,9),
array(3,5,6),
);
How can I do this? Expecting results -> [2, 5], [1, 8], [2, 6].

You do not need a recursion here, simply add an additional loop in the get_range() function. The following example is based on your code and is a possible solution to your problem:
<?php
$array = [8,2,3,7,4,6,5,1,9];
$subsets = array (
array(6,3,7),
array(4,2,9),
array(3,5,6),
);
function get_range($array, $subsets)
{
$result = array();
foreach ($subsets as $subset) {
$min = sizeof($array);
$max = 0;
foreach($subset as $value) {
$occurrence = array_search($value, $array);
if( $occurrence < $min ) {
$min = $occurrence;
}
if( $occurrence > $max ) {
$max = $occurrence;
}
}
$result[] = [$min, $max];
}
return $result;
}
$range = get_range($array, $subsets);
echo print_r($range, true);
?>
Result:
Array (
[0] => Array ( [0] => 2 [1] => 5 )
[1] => Array ( [0] => 1 [1] => 8 )
[2] => Array ( [0] => 2 [1] => 6 )
)

Related

How to split the string in two arrays in Php

I have one array like this :
$array='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}'
I want first split to two array like this :
$array[0]={b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1}
And
$array[1]={b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}
I change every array with this code
foreach ($b as $k => $m) {
if ($k % 2 == 0) {
$even[]= $m;
}
else {
$odd[] = $m;
}
}
$ff=array_combine($even,$odd);
I want output change like this
Array( Array[0] => ([b_price] => 9500 [b_discount] => 10 [mainPrice] => 95000 [total] => 95000 [title] =>obj1)
Array[1] => ([b_price] => 1500 [b_discount] => 15 [mainPrice] => 15000 [total] => 22500 [title] => obj2))
Two approaches:
-- using explode and array_map functions:
$str = '{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
$result = array_map(function($v){
$r = [];
$arr = explode(',', trim($v, '{}'));
foreach ($arr as $k => $v) {
if (!($k % 2)) $r[$v] = $arr[$k+1];
}
return $r;
}, explode('},{', $str));
print_r($result);
-- using additional preg_match_all and array_combine functions:
$str = '{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
$result = array_map(function($v){
preg_match_all('/([^,]+),([^,]+),?/', trim($v, '{}'), $m);
return array_combine($m[1], $m[2]);
}, explode('},{', $str));
print_r($result);
The output:
Array
(
[0] => Array
(
[b_price] => 9500
[b_discount] => 10
[mainPrice] => 95000
[total] => 95000
[title] => obj1
)
[1] => Array
(
[b_price] => 1500
[b_discount] => 15
[mainPrice] => 15000
[total] => 22500
[title] => obj2
)
)
you should change your needle, in your array string,
i have changed it with semicolon,
$arrayString='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1};{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
echo $arrayString;
echo "<pre>"; print_r (explode(";",$arrayString));
$b=explode(";",$arrayString);
foreach ($b as $k => $m) {
if ($k % 2 == 0) {
$even[]= $m;
}
else {
$odd[] = $m;
}
}
$ff=array_combine($even,$odd);
So, I write this decision. Maybe it can be more clear, but it works.
$array='{b_price,9500,b_discount,10,mainPrice,95000,total,95000,title,obj1},{b_price,1500,b_discount,15,mainPrice,15000,total,22500,title,obj2}';
/*Making array from string*/
$tmp_array = explode("},{", $array);
/*Removing { symbols*/
$tmp_array[0] = substr($tmp_array[0],1);
$tmp_array[1] = substr($tmp_array[1],0,-1);
/*Making arrays from string [0] and [1]*/
$tmp_array[0] = explode(',',$tmp_array[0]);
$tmp_array[1] = explode(',',$tmp_array[1]);
$new_array = [];
/*Creating associative arrays*/
for($a = 0; $a < count($tmp_array); $a++) {
$new_as_array = [];
for($i = 0; $i <= count($tmp_array[0]); $i+=2) {
if($i + 1 <= count($tmp_array[0])) {
$new_as_array[$tmp_array[$a][$i]] = $tmp_array[$a][$i + 1];
}
}
$new_array[] = $new_as_array;
}
print_r($new_array);

Retrieve and remove duplicate values from an associative array

I have associative array like below
$arr = [1=>0, 2=>1, 3=>1, 4=>2, 5=>2, 6=>3]
I would like to remove the duplicate values from the initial array and return those duplicates as as a new array of duplicate arrays. So I would end up with something like;
$arr = [1=>0, 6=>3]
$new_arr = [[2=>1, 3=>1],[4=>2, 5=>2]]
Does PHP provide such a function or if not how would I achieve this?
I've tried;
$array = [];
$array[1] = 5;
$array[2] = 5;
$array[3] = 4;
$array[5] = 6;
$array[7] = 7;
$array[8] = 7;
$counts = array_count_values($array);
print_r($counts);
$duplicates = array_filter($array, function ($value) use ($counts) {
return $counts[$value] > 1;
});
print_r($duplicates);
$result = array_diff($array, $duplicates);
print_r($result);
This outputs;
[1] => 5
[2] => 5
[7] => 7
[8] => 7
&
[3] => 4
[5] => 6
which is almost what I want.
Code
The following works for me... Tho I make no promises in regards to complexity and performance, but there's the general idea... Also, I haven't written PHP for many years now, so bear that in mind.
<?php
function nubDups( $arr ) {
$seen = [];
$dups = [];
foreach ( $arr as $k => $v) {
if ( array_key_exists( $v, $seen ) ) {
// duplicate found!
if ( !array_key_exists( $v, $dups ) )
$dups[$v] = [$seen[$v]];
$dups[$v][] = $k;
} else
// First time seen, record!
$seen[$v] = $k;
}
$uniques = [];
foreach ( $seen as $v => $k ) {
if ( !array_key_exists( $v, $dups ) ) $uniques[$k] = $v;
}
return [$uniques, $dups];
}
function nubDups2( $arr ) {
for ( $seen = $dups = []; list( $k, $v ) = each( $arr ); )
if ( key_exists( $v, $dups ) ) $dups[$v][] = $k;
else if ( key_exists( $v, $seen ) ) $dups[$v] = [$seen[$v], $k];
else $seen[$v] = $k;
return [array_flip( array_diff_key( $seen, $dups ) ), $dups];
}
$arr = [0, 1, 4, 1, 2, 2, 3];
print_r( nubDups( $arr ) );
print_r( nubDups2( $arr ) );
Output (for both)
$ php Test.php
Array
(
[0] => 0
[2] => 4
[6] => 3
)
Array
(
[1] => Array
(
[0] => 1
[1] => 3
)
[2] => Array
(
[0] => 4
[1] => 5
)
)
Shortened
removed, specified as [(k, v)]: [(0, 0), (2, 4), (6, 3)]
duplicates, specified as [(v, [k])]: [(1, [1, 3]), (2, [4, 5])]
In Haskell
This version abuses hash tables for fast lookups.
A simpler version that almost does the same but ignores indexes, written in haskell:
-- | 'nubDupsBy': for a given list yields a pair where the fst contains the
-- the list without any duplicates, and snd contains the duplicate elements.
-- This is determined by a user specified binary predicate function.
nubDupsBy :: (a -> a -> Bool) -> [a] -> ([a], [a])
nubDupsBy p = foldl f ([], [])
where f (seen, dups) x | any (p x) seen = (seen, dups ++ [x])
| otherwise = (seen ++ [x], dups)

Multidimensional array sum values

I have following function to sum multidimensional array values.
// summing values of multidimensional array
function getSum($array, $path = array()){
// process second argument:
foreach ($path as $key) {
if (!is_array($array) || !isset($array[$key])) {
return 0; // key does not exist, return 0
}
$array = $array[$key];
}
if(is_array($array)) {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$sum = 0;
foreach ($iterator as $key => $value) {
$sum += $value;
}
} else{
$sum = $array;
}
return $sum;
}
I'm using the function like this:
$array = array();
$array['one']['green'][20] = 20;
$array['one']['blue'][20] = 5;
$array['one']['blue'][30] = 10;
getSum($array,['one','green']); // 20
getSum($array,['one','blue',20]); // 5
Now I have a problem if I don't want to for example set any spesific color because I want that script sums all values from category 20 from all colours.
So it should be working like this:
getSum($array,['one','*',20]); // 25
Thanks for your help!
Here is example of my array:
Array (
[1] => Array (
[AREA I] => Array (
[20] => 1
[25] => 0
[30] => 0 )
[AREA II] => Array (
[20] => 0
[30] => 0 )
[AREA III] => Array (
[20] => 2
[30] => 0 )
[AREA IV] => Array (
[20] => 0
[30] => 3 )
[AREA V] => Array (
[20] => 4
[25] => 0
[30] => 3 )
)
[2] => Array (
[AREA I] => Array (
[20] => 0
[30] => 0 )
[AREA II] => Array (
[20] => 0
[30] => 0 )
)
)
And here is example of my getSum call:
getSum($visitsandinfosact,['*','*',20]); // should print 7
Recursive Function
I was not sure if ['one','*'] should give 45 but if it should just return 0 you just have to remove the else if (empty($filterList) && is_array($value) && $first == "*")condition. All values which are not arrays are just converted to int via intval and added to the sum. If you wanna use float then use floatval instead of intval
function getSum($array, $filterList = array('*')) {
$sum = 0;
$first = array_shift($filterList);
foreach ($array as $key => $value) {
if ($key == $first || $first == "*") {
if (is_array($value) && !empty($filterList)) {
$sum += getSum($value, $filterList);
} else if (empty($filterList) && is_array($value)) {
$sum += getSum($value, array("*"));
} else if (empty($filterList)) {
$sum += intval($value);
}
}
}
return $sum;
}
echo getSum($array,['one','*',20], 10) . "\n"; // 25
echo getSum($array,['one','*','*',20]) . "\n"; // 10
echo getSum($array,['one','*']) . "\n"; // 45
echo getSum($array) . "\n"; // 45
Input Array
$array = array();
$array['one'] = array();
$array['one']['green'] = array();
$array['one']['green'][20] = 20;
$array['one']['blue'] = array();
$array['one']['blue'][20] = 5;
$array['one']['blue'][30] = 10;
$array['one']['orange']['red'][20] = 10;
Output
Only the numbers are outputted but just added the input params for better understanding.
25 // (['one','*',20])
10 // (['one','*','*',20])
45 // (['one','*'])
45 // no filterList
In short, you need a recursive function to add in wildcard "endpoints". You might as well use the same recursive nature to cover the addition as well.
The following should do what you're wanting:
// summing values of multidimensional array
function getSum(&$array, $path = array()){
$sum = 0;
if(is_int($array) and empty($path)) // return value if int
$sum = $array;
else if(is_array($array)){ // else add recurred values
if(empty($path)){
foreach($array as $value)
$sum += getSum($value);
} else {
$key = array_shift($path);
if($key=='*'){
foreach($array as $value)
$sum += getSum($value, $path);
} else {
if(isset($array[$key]))
$sum += getSum($array[$key], $path);
}
}
}
return $sum;
}
Test:
$array['one'] = array();
$array['one']['green'] = array();
$array['one']['green'][20] = 20;
$array['one']['blue'] = array();
$array['one']['blue'][20] = 5;
$array['one']['blue'][30] = 10;
$array['one']['orange']['red'][20] = 10;
echo getSum($array,['one','*',20]); // 25
echo getSum($array,['one','*','*',20]); // 10
echo getSum($array,['one','*']); // 45
Happy coding

Count the number of numeric keys in an array

I'm using PHP 5.6, and I have the following array:
Array (
[0] => Array (
[id] => 1
[name] => James
)
[1] => Array (
[id] => 2
[name] => Tim
[children] => Array (
[0] => Array (
[id] => 4
[name] => Sam
)
[1] => Array (
[id] => 5
[name] => Florence
)
)
)
[2] => Array (
[id] => 3
[name] => Stephen
)
)
I'm trying to find a neat and fast way to count the number of people, which is the same as counting the number of numeric keys, which should be 5.
echo count($myarray); // 3 (only first level)
echo count($myarray, COUNT_RECURSIVE); // 16 (every key/value)
Is there a good way to do this with built-in PHP functions, or do I need to traverse the whole multidimensional array and count them manually..?
EDIT My array could end up being 1,000+ people (or more), with many many levels (an unknown number of levels).
It is important to note that, even if there were a PHP built-in (such as count($myarray, COUNT_RECURSIVE_NUMERIC);) internally, it would still be traversing the whole array, recursively. If you are worried about Out Of Memory errors, try pass-by-reference, which will not copy the array or the array items:
define('COUNT_RECURSIVE', 1);
function count_numeric_keys(&$array, $flags = 0) {
$count = 0;
foreach ($array as $key => $value) {
$count += (int) (is_numeric($key));
if ($flags & COUNT_RECURSIVE && is_array($value)) {
$count += count_numeric_keys($value, $flags);
}
}
return (int) $count;
}
$count = count_numeric_keys($array, COUNT_RECURSIVE);
Mayhaps?
Comparison with non-pass-by-reference, type-hint, and small benchmark:
define('COUNT_RECURSIVE', 1);
function count_numeric_keys(Array &$array, $flags = 0) {
$count = 0;
foreach ($array as $key => $value) {
$count += (int) (is_numeric($key));
if ($flags & COUNT_RECURSIVE && is_array($value)) {
$count += count_numeric_keys($value, $flags);
}
}
return (int) $count;
}
function count_numeric_keys_np(Array $array, $flags = 0) {
$count = 0;
foreach ($array as $key => $value) {
$count += (int) (is_numeric($key));
if ($flags & COUNT_RECURSIVE && is_array($value)) {
$count += count_numeric_keys_np($value, $flags);
}
}
return (int) $count;
}
$tpl_array = array(
1=>"one",
"two"=>"two",
3=>array(
1=>1,
"two"=>2
)
);
// Fill the array with both numeric and non-numeric
$array = array();
for($i = 1000; $i > 0; $i--) {
$array[] = $tpl_array;
}
for($i = 1000; $i > 0; $i--) {
$array["not a number $i"] = $tpl_array;
}
echo "Pre Memory: ".memory_get_usage(TRUE).PHP_EOL;
echo "Count: ".count_numeric_keys($array, COUNT_RECURSIVE).PHP_EOL;
echo "Reference Memory: ".memory_get_usage(TRUE)." current, ".memory_get_peak_usage(TRUE)." peak.\n";
count_numeric_keys_np($array, COUNT_RECURSIVE);
echo "No-Reference Memory: ".memory_get_usage(TRUE)." current, ".memory_get_peak_usage(TRUE)." peak.\n";
View it on IDEONE here.
ODDLY, having a reference on $value, like foreach($array as $key => &value) {} actually increased memory usage. Bizarre...
I just created this recursive function to do this for ya :P
function countNumericKeys($array)
{
$count = 0;
foreach ($array as $key => $value)
{
if (is_numeric($key))
{
$count ++;
}
if (is_array($value))
{
$count += countNumericKeys($value);
}
}
return $count;
}
// Test it!
$array = [
1=>"one",
"two"=>"two",
3=>[
1=>1,
"two"=>2
]
];
print countNumericKeys($array); // Output: 3, correct (in this case at least)!
Shortened the code so it uses ternary operators instead of the ifs that it was :P
function simpleCountNumericKeys($array)
{
$count = 0;
foreach ($array as $key => $value)
{
$count += is_numeric($key) ? 1 : 0;
$count += is_array($value) ? simpleCountNumericKeys($value) : 0;
}
return $count;
}
TEST USING array_keys -- only gets the top level keys of the array
function countArrayKeysNumeric($array)
{
$count = 0;
$keys = array_keys($array);
foreach ($keys as $key) $count += is_numeric($key) ? 1 :0;
return $count;
}
$array = [
1=>"one",
"two"=>"two",
3=>[
1=>1,
"two"=>2
]
];
print countArrayKeysNumeric($array);
Prints 2... :(

php numbers script array loop

I try to write a script and a problem. Can you let me know if you know how i can do this or ask someone if they know how can this be possibe.
Max numbers which can be selected 1 to 20 numbers. it can loop and select any number between 1-20
ignore these numbers e.g. 1,2,4,6,9,12 this will be array which can change.
each array line can have upto 4 numbers
Each array line needs to be unique
5.I need to have around 10 arrays unique
Max 2 numbers can match previous numbers see below.
How can i go about doing this. Any help would be great.
e.g.
Array(
[0] => Array
(
[0] => 3
[1] => 16
[2] => 22
[3] => 24
)
[1] => Array
(
[0] => 3
[1] => 16
[2] => 7
[3] => 13
)
[2] => Array
(
[0] => 20
[1] => 17
[2] => 10
[3] => 18
)
)
This not allow as some array match each other
Array(
[0] => Array
(
[0] => 3
[1] => 16
[2] => 22
[3] => 24
)
[1] => Array - cant have this as 3 of the numbers matchs the previous array.only two numbers can match.
(
[0] => 3
[1] => 16
[2] => 22
[3] => 13
)
[2] => Array
(
[0] => 20
[1] => 17
[2] => 10
[3] => 18
)
)
Thank you.
This seems to satisfy your conditions: http://codepad.viper-7.com/WHkQeD
<?php
$num_arrays = 10; $num_elements = 4;
$min = 1; $max = 20;
$exclude_numbers = array( 1, 4, 6); // Add numbers here to exclude
$answer = array();
for( $i = 0; $i < $num_arrays; $i++)
{
$answer[$i] = array();
for( $j = 0; $j < $num_elements; $j++)
{
do
{
$current = rand( $min, $max);
// If the previous array exists and there are more than two common elements when we add the $current element, continue
if( isset( $answer[$i-1]) && count( array_intersect( $answer[$i-1], array_merge( $answer[$i], array( $current))) > 2)
{
continue;
}
} while( in_array( $current, $exclude_numbers) || in_array( $current, $answer[$i]));
$answer[$i][$j] = $current;
}
}
var_dump( $answer);
Edit: Here is a complete solution that satisfies all of your criteria.
Demo
<?php
$num_arrays = 10; $num_elements = 4;
$min = 1; $max = 20;
$exclude_numbers = array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
$answer = array();
for( $i = 0; $i < $num_arrays; $i++)
{
$answer[$i] = array();
for( $j = 0; $j < $num_elements; $j++)
{
do
{
// Get a random element
$current = rand( $min, $max);
$new_array = array_merge( $answer[$i], array( $current));
// If the previous array has more than two common elements (because of the added $current), get a new $current
if( isset( $answer[$i-1]) && count( array_intersect( $answer[$i-1], $new_array)) > 2)
{
$answer[$i] = array_diff( $new_array, $answer[$i-1]);
$j = count( $answer[$i]) - 1;
continue;
}
} while( in_array( $current, $exclude_numbers) || in_array( $current, $answer[$i]));
$answer[$i][$j] = $current;
// If the array is complete, we need to check for unique arrays
if( count( $answer[$i]) == $num_elements)
{
$k = $i - 1;
while( $k >= 0)
{
if( count( array_diff( $answer[$k], $answer[$i])) == 0)
{
// This array is the same as a previous one, start over
$answer[$i] = array();
$j = -1;
break;
}
$k--;
}
// Optionally sort each array
sort( $answer[$i]);
}
}
}
var_dump( $answer);
somthing like:
function generate_list($max, $forbidden)
{
$list = array();
while(count($list) < 4)
{
$new = rand(1, $max);
if(in_array($new, $forbidden))
{
continue;
}
$list[] = $new;
$forbidden[] = $new;
}
return $list;
}
function count_max_same($new_list, $old_lists)
{
$max_same = 0;
foreach($old_lists as $current_list)
{
$max_same = max($max_same, count(array_intersect($new_list, $old_lists)));
}
return $max_same;
}
function generate_unique_lists($count_of_lists, $max, $forbidden, $max_same = 2, $max_tries = 1000)
{
$lists = array();
while($max_tries-- AND count($lists) < $count_of_lists)
{
$new_list = generate_list($max, $forbidden);
if(count_max_same($new_list, $lists) <= $max_same)
{
$lists[] = $new_list;
}
}
return $lists;
}

Categories