Simple way to check an array for "holes" in the keys - php

I have a simple associative array:
$ar = array( 1=>'foo', 2=>'bar', 5=>'foobar', 8=>'barfoo' )
I need to efficiently find holes in the keys. The keys are guaranteed to be integers.
findHole($ar)
> 0
findHole($ar,1)
> 3
findHole($ar,5)
> 6
what is the easiest way to do this?

Try this:
function findHole($array, $key=0) {
while (array_key_exists($key, $array)) {
$key++;
}
return $key;
}

The desired behavior of your findHole function isn't 100% clear to me, but the following code snippet will give you an array that has all the "missing" indexes.
$ar = array( 1=>'foo', 2=>'bar', 5=>'foobar', 8=>'barfoo' );
$keys = array_keys($ar);
$missing_indexes = array_diff(range(0,max($keys)), $keys);
print_r($missing_indexes);
Depending on your use case this may or may not be less efficient. It's using multiple function calls and arrays are passed around by value by default, but those functions are operating at native code speeds, while solutions using loops are going to be running at PHP speed.
Use case, benchmark, etc.

All holes:
function GetHoles($arr)
{
$holes = array();
$max_value = max(array_keys($arr));
for($i = 0; $i < $max_value; $i++)
{
if(!in_array($i, $keys)) $holes[] = $i;
}
return $holes;
}

if you just want to condense the array, try this:
function FlattenArray( $o )
{
$res = array();
foreach($o as $v)
{
$res = array_merge($res, FlattenArray($v));
}
return $res;
}

Related

Count keys in array

I've written the below function, it takes in a 2 dimensional array and return the number of times a key occurs, BUT it feels like i may be reinventing the wheel here, is there an easy way?
function countKeys($array, $key)
{
$results = array();
foreach($array as $row)
{
if (array_key_exists($row[$key], $results))
{
$results[$row[$key]] += 1;
}
else
{
$results[$row[$key]] = 1;
}
}
return $results;
}
To count the keys in a two dimensional array with a search I would do this -
function countKeys($array,$search){
$key_count = array(); // new array
foreach($array as $record) {
$keys = array_keys($record);
foreach($keys as $key){
if($key == $search){ // check against the search term
array_push($key_count, $key); // add the key to the new array
}
}
}
return count($key_count); // just count the new array
}
echo countKeys($records, 'last_name');
EXAMPLE
array_keys()
count()
For a 2 dimensional array try:
$result = count(array_column($array, $key));
PHP >= 5.5.0 needed for array_column() or use the PHP Implementation of array_column()
Just use count() function like:
count($array);
see: http://php.net/manual/pt_BR/function.count.php
You can use this function to count arrays variable as well.
Hope it helps you.

Sorting PHP array without ksort

I am trying to manually sort a PHP array without making use of ksort.
This is how my code looks at the moment:
function my_ksort(&$arg){
foreach($arg as $key1 => $value1){
foreach($arg as $key2 => $value2){
if($key1 > $key2){
$aux = $value2;
$arg[$key2] = $value1;
$arg[$key1] = $aux;
}
}
}
}
It doesn't sort, I can't figure out how to make it sort.
You could try this:
function my_ksort(&$arg)
{
$keys=array_keys($arg);
sort($keys);
foreach($keys as $key)
{
$val=$arg[$key];
unset($arg[$key]);
$arg[$key]=$val;
}
}
I'm sorting the keys separately and then deleting the elements one-by-one and appending them to the end, in ascending order.
I'm using another sorting function (sort()), but if you want to eliminate all available sorting functions from your emulation, sort() is much easier to emulate. In fact, #crypticous's algorithm does just that!
This function return array in ASC. Take in consideration that I'm using goto which is supported in (PHP 5 >= 5.3.0)
function ascending_array($array){
if (!is_array($array)){
$array = explode(",", $array);
}
$new = array();
$flag = true;
iter:
$array = array_values($array); // recount array values with new offsets
(isset($min["max"])) ? $min["value"] = $min["max"] : $min["value"] = $array[0];
$min["offset"] = 0;
for ($i=0;$i<count($array);$i++){
if ($array[$i] < $min["value"]){ // redefine min values each time if statement executed
$min["value"] = $array[$i];
$min["offset"] = $i;
}
if ($flag){ // execute only first time
if ($array[$i] > $min["value"]){ // define max value from array
$min["max"] = $array[$i];
}
$flag = false;
}
if ($i === (count($array)-1)){ // last array element
array_push($new,$min["value"]);
unset($array[$min["offset"]]);
}
}
if (count($array)!=0){
goto iter;
}
print_r($new);
}
$arr = array(50,25,98,45);
ascending_array($arr); // 25 45 50 98
PS. When I was studying php, I wrote this function and now remembered that I had it (that's why I really don't remember what I am doing in it, though fact is it's working properly and hopefully there are comments too), hope you'll enjoy :)
DEMO
I was checking some issue related to this post and i wanted to give my insight about it ! here's what i would have done to implement php's sort :
$array_res = array();
$array = array(50,25,98,45);
$i=0;
$temp = $array[0];
$key = array_search($temp, $array);
while ($i<count($array)-1){
$temp = $array[0];
for($n=0;$n<count($array) ;$n++)
{
if($array[$n]< $temp && $array[$n] != -1 )
{
$temp = $array[$n];
}
else{continue;}
}
//get the index for later deletion
$key = array_search($temp, $array);
array_push($array_res, $temp);
/// flag on those which were ordered
$array[$key] =-1;
$i++;
}
// lastly append the highest number
for($n=0;$n<count($array) ;$n++)
{
if ($array[$n] != -1)
array_push($array_res, $array[$n]);
}
// display the results
print_r($array_res);
This code will display : Array
(
[0] => 25
[1] => 45
[2] => 50
[3] => 98
)
Short and sweet
function custom_ksort($arg)
{
$keys = array_keys($arg);
sort($keys);
foreach($keys as $newV)
{
$newArr[$newV] = $arg[$newV];
}
return $newArr;
}
It looks like your issue is that you're changing "temporary" characters $key1 and $key2 but not the actual arrays. You have to change $arg, not just $key1 and $key2.
Try something like:
$arr = Array(3=>"a",7=>"b");
print_r( $arr );
foreach( $arr as $k=>$v ){
unset($arr[$k]);
$arr[$k+1] = $v;
}
print_r($arr);

why this is not the array I need?

I have an array $num_arr ,so I want get a new array that It's sum is smaller than 10,so i write the code like this,
$num_arr=array(1,3,6,5,4,2,7,9,5,3,6,2,4,7);
$sum=0;
for($i=0;$i<=count($num_arr);$i++){
$sum+=$num_arr[$i];
$k++;
if($sum>=10){
$need_arr[]=array_slice($num_arr,0,$k);
array_splice($num_arr,0, $k);
$k=0;
$sum=0;
}
}
The result $need_arr is not right,that is why and how can get the right array like this: array(array(1,3,6),array(5,4),array(2,7),array(9),...)?
Implemented a "oneliner" just for fun:
$num_arr=array(1,3,6,5,4,2,7,9,5,3,6,2,4,7);
$result = array_reduce($num_arr, function($result, $curr) {
if (!count($result)) {
$result[] = array();
}
$last =& $result[count($result) - 1];
if (array_sum($last) + $curr > 10) {
$result[] = array($curr);
} else {
$last[] = $curr;
}
return $result;
}, array());
var_dump($result);
Online demo: http://ideone.com/aFVmkp
Among other things, you are changing the length of the array when you use array_splice, but you aren't adjusting $i in any way.
In fact, you can remove that array_splice line entirely, since you're continuing to iterate over the array.
Also, you're only starting a new array if you're over 10. You should change your condition to this:
if(!isset($num_arr[$i+1]) || $sum+$num_arr[$i+1] >= 10)

Test if one array is a subset of another

How can I determine if one array is a subset of another (all elements in the first are present in the second)?
$s1 = "string1>string2>string3>string4>string5>string6>";
$arr1 = explode(">", $s1);
$s2 = "string1>string4>string5";
$arr2 = explode(">", $s2);
$isSubset = /* ??? */
if (array_intersect($array1, $array2) == $array1) {
// $array1 is a subset of $array2
}
Simple: use array subtraction.
On array subtraction, you will know whether or not one array is a subset of the other.
Example:
if (!array_diff($array1, $array2)) {
// $array1 is a subset of $array2
}
Reference: array_diff
You can use array_intersect also.
Try that
If you start from strings, you could check strstr($fullString,$subsetStr);. But that'll only work when all chars have the same order: 'abcd','cd' will work, but 'abcd','ad' won't.
But instead of writing your own, custom, function you should know that PHP has TONS of array functions, so its neigh on impossible that there isn't a std function that can do what you need it to do. In this case, I'd suggest array_diff:
$srcString = explode('>','string1>string2>string3>string4>string5');
$subset = explode('>','string3>string2>string5');
$isSubset = array_diff($subset,$srcString);
//if (empty($isSubset)) --> cf comments: somewhat safer branch:
if (!$isSubset)
{
echo 'Subset';
return true;
}
else
{
echo 'Nope, substrings: '.implode(', ',$isSubset).' Didn\'t match';
return false;
}
I would create an associated array of the larger array, then iterate through the smaller array, looking for a non collision, if you find one, return false.
function isSubset($arr1,$arr2){
$map = Array();
for ($i=0;$i<count($arr1);$i++){
$map[$arr[$i]]=true;
}
for ($i=0;$i<count($arr2);$i++){
if (!isset($map[$arr2[$i]])){
return false;
}
}
return true;
$s1 = "1>2>3>4>5>6>7";
$arr1 = explode(">",$s1);
$s2 = "1>2>3";
$arr2 = explode(">",$s2);
if(isSub($arr1,$arr2)){
echo 'true';
}else{
echo 'false';
}
function isSub($a1,$a2){
$num2 = count($a2);
$sub = $num2;
for($i = 0;$i < $num2 ;$i++){
if(in_array($a2[$i],$a1)){
$sub--;
}
}
return ($sub==0)? true:false;
}
Simple function which will return true if array is exact subset otherwise false. Solution is applicable for two dimensional array as well.
function is_array_subset($superArr, $subArr) {
foreach ($subArr as $key => $value) {
//check if keys not set in super array OR values are unequal in both array.
if (!isset($superArr[$key]) || $superArr[$key] != $value) {
return false;
}
}
return true;
}

PHP array as variable name

How to send an indexes name for php array vairable.
the array is
$array = array('Somthing'=>array('More'=>array('id'=> 34)));
and now I want to display this thing but with a variable name I don't know how to explain so I write what I want to have.
$index_name = '[Something][More][id]';
$array{$index_name};
Is it possible in any way ?
Not in one go like that. Here's how you'd do it:
$array['Something']['More']['id']
If you particularly wanted access multidimensional arrays with a single string, then you could build a function to do that:
function array_multi(Array $arr, $path) {
$parts = explode(".", $path);
$curr =& $arr;
for ($i = 0, $l = count($parts); $i < $l; ++$i) {
if (!isset($curr[$parts[$i]])) {
// path doesn't exist
return null;
} else if (($i < $l - 1) && !is_array($curr[$parts[$i]]) {
// path doesn't exist
return null;
}
$curr =& $curr[$parts[$i]];
}
return $curr;
}
// usage:
echo array_multi($array, "Something.More.id"); // 34
echo array_multi($array, "Something.More"); // array("id" => 34)
Recursive version supporting your syntax with square brackets:
$array = array('Something'=>array('More'=>array('id'=> 34)));
$string = '[Something][More][id]';
echo scan_array($string, $array);
function scan_array($string, $array) {
list($key, $rest) = preg_split('/[[\]]/', $string, 2, PREG_SPLIT_NO_EMPTY);
if ( $key && $rest ) {
return scan_array($rest, $array[$key]);
} elseif ( $key ) {
return $array[$key];
} else {
return FALSE;
}
}
Ok, I know this is how people get shot. But c'mon, eval() is not always the wrong answer.
$array = array('Something'=>array('More'=>array('id'=> 34)));
$index_name = '[Something][More][id]';
eval('$val = $array'.$index_name.';'); // Wrap in a function or something
You could do this with eval():
<?php
$array = array('Somthing'=>array('More'=>array('id'=> 34)));
$index_name = "['Somthing']['More']['id']";
$stmt='echo $array'.$index_name.';';
eval($stmt);
?>
UPDATE:
It seems some SO users are uncomfortable with the idea of using eval(). I think it makes sense to read this thread which discusses the pros and cons before deciding whether to use this in your own code.
If you've cornered yourself into needing to do something like this, there's a pretty good chance you've done something else in a poor way. There's valid reasons to do this, but not very often.
function key_path($arr, $keys) {
return $keys ? key_path($arr[array_shift($keys)], $keys) : $arr;
}
$arr['Something']['More']['id'] = 34;
$keys = array('Something', 'More', 'id');
var_dump( key_path($arr, $keys));

Categories