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;
}
Related
on http://php.net/manual/en/function.in-array.php - if you scroll down it gives a function to determine if a string is inside of a query in a multidimensional array. "If you found yourself in need of a multidimensional array in_array like function you can use the one below. Works in a fair amount of time"
Here's original code(working):
function in_multiarray($elem, $array)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom] == $elem)
return true;
else
if(is_array($array[$bottom]))
if(in_multiarray($elem, ($array[$bottom])))
return true;
$bottom++;
}
return false;
}
What I'm trying to do is instead of returning 'true' or 'false' - i'd like to return the ROW #. So my initial thought was to simply replace 'return true' with 'return $bottom; however it isn't returning the record number.
Modified Function (not working);
function in_multiarray($elem, $array)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom] == $elem)
return $bottom;
else
if(is_array($array[$bottom]))
if(in_multiarray($elem, ($array[$bottom])))
return $bottom;
$bottom++;
}
return false;
}
Does anyone have an idea how to modify this function to return the ROW number that contains the match?
Here's a sample of the array...
$sample = array
array ("oldpage1.php","newpage1.php"),
array ("oldpage2.php","newpage2.php"),
array ("oldpage3.php","newpage3.php"),
array ("oldpage4.php","newpage4.php"),
array ("oldpage5.php","newpage5.php")
etc.
);
$row = in_multiarray($input, $sample);
Therefore if we know the row # we can fetch the new page with a simple
$newpage=$sample[$row][1]
Thanks!
It's worth noting that a function like in_array is intended to tell you whether or not a value exists inside of an array. What you're looking for seems to be a lot closer to something like array_search, which is designed to actually provide you with the key that points to a given value in the array.
However, because you're using a multi-dimensional array what you're actually looking for is the key that points to the array that contains the value. Hence we can divide and conquer this problem with two simple steps.
Map
Filter
The first step is to map a function in_array to every element in the first array (which is just another array). This will tell us which elements of the primary array contain an array that contains the value we're searching for.
$result = array_map(function($arr) use($search) {
return in_array($search, $arr, true);
}, $arr, [$searchValue]);
The second step is to then return the keys to those arrays (i.e. filter the result).
$keys = array_keys(array_filter($result));
Now you have all the keys of any matching items. If you want to apply as just one custom filter that specifies exactly where in the subarray to look, you could also do it like this.
$search = "oldpage2.php";
$sample = [
["oldpage1.php","newpage1.php"],
["oldpage2.php","newpage2.php"],
["oldpage3.php","newpage3.php"],
["oldpage4.php","newpage4.php"],
["oldpage5.php","newpage5.php"],
];
$keys = array_keys(array_filter($sample, function($arr) use($search) {
return $arr[0] === $search;
}));
var_dump($keys);
And you get...
array(1) {
[0]=>
int(1)
}
So now you know that "oldpage2.php" is in row 1 in $sample[1][0] which means you can do this to get the results out of the array.
foreach($keys as $key) {
echo "{$sample[$key][0]} maps to {$sample[$key][1]}\n";
}
Giving you
oldpage2.php maps to newpage2.php
If you want to return only the first result you could do that as well with a function like this using similar approach.
function getFirstMatch($search, Array $arr) {
foreach($arr as $key => $value) {
if ($value[0] === $search) {
return $value[1];
}
}
}
echo getFirstMatch("oldpage4.php", $sample); // newpage4.php
The Better Alternative
Of course, the better approach is to actually use the oldpage names as the actual keys of the array rather than do this expensive search through the array, because array lookup by key in PHP is just an O(1) operation, whereas this needle/haystack approach is O(N).
So we turn your $samples array into something like this and the search no longer requires any functions...
$samples = [
"oldpage1.php" => "newpage1.php",
"oldpage2.php" => "newpage2.php",
"oldpage3.php" => "newpage3.php",
"oldpage4.php" => "newpage4.php",
"oldpage5.php" => "newpage5.php",
];
Now you can just do something like $newpage = $samples[$search] and you get exactly what you're looking for. So echo $samples["oldpage2.php"] gives you "newpage2.php" directly without the intermediary step of searching through each array.
You can use the following code to get the full path to the value:
function in_multiarray($elem, $array, &$result)
{
$top = sizeof($array) - 1;
$bottom = 0;
while($bottom <= $top)
{
if($array[$bottom] == $elem) {
array_unshift($result, $bottom);
return true;
}
else {
if(is_array($array[$bottom])) {
if(in_multiarray($elem, $array[$bottom], $result)) {
array_unshift($result, $bottom);
return true;
}
}
}
$bottom++;
}
array_shift($result);
return false;
}
$sample = array(
array ("oldpage1.php","newpage1.php"),
array ("oldpage2.php","newpage2.php"),
array ("oldpage3.php","newpage3.php"),
array ("oldpage4.php","newpage4.php"),
array ("oldpage5.php","newpage5.php")
);
$input = "newpage5.php";
$result = [];
in_multiarray($input, $sample, $result);
print_r($result);
Path is stored in $result;
I am trying to detect if one or more variables contain numbers. I have tried a few different methods, but I have not been entirely successful. Here is what I have tried.
<?php
$one = '1';
$two = '2';
$a1 = '3';
$a2 = '4';
$a3 = '5';
$string_detecting_array = array();
array_push($string_detecting_array, $one,$two,$a1,$a2,$a3);
foreach ($string_detecting_array as $key) {
if (is_numeric($key)) {
echo 'Yes all elements in array are type integer.';
}
else {
echo "Not all elements in array were type integer.";
}
}
?>
I haven't been successful using this method. Any ideas? Thankyou in advance!
First off, your loop logic is wrong: you should process all the items in the array before reaching a verdict. The shortest (although not most obvious) way to do this is with
$allNumbers = $array == array_filter($array, 'is_numeric');
This works because array_filter preserves keys and comparing arrays with == checks element counts, keys, and values (and the values here are primitives, so can be trivially compared).
A more mundane solution would be
$allNumbers = true;
foreach ($array as $item) {
if (!is_numeric_($item)) {
$allNumbers = false;
break;
}
}
// now $allNumbers is either true or false
Regarding the filter function: if you only want to allow the characters 0 to 9, you want to use ctype_digit, with the caveat that this will not allow a minus sign in front.
is_numeric will allow signs, but it will also allow floating point numbers and hexadecimals.
gettype will not work in this case because your array contains numeric strings, not numbers.
You can use gettype if you want to explicitly know if the variable is a number. Using is_numeric will not respect types.
If you are intending to use is_numeric but want to know if all elements are, then proceed as follows:
$all_numeric = true;
foreach ($string_detecting_array as $key) {
if (!(is_numeric($key))) {
$all_numeric = false;
break;
}
}
if ($all_numeric) {
echo 'Yes all elements in array are type integer.';
}
else {
echo "Not all elements in array were type integer.";
}
You can chain array_map with array_product to get a one-liner expression:
if (array_product(array_map('is_numeric', $string_detecting_array))) {
echo "all values are numeric\n";
} else {
echo "not all keys are numeric\n";
}
You can use this:
$set = array(1,2,'a','a1','1');
if(in_array(false, array_map(function($v){return is_numeric($v);}, $set)))
{
echo 'Not all elements in array were type integer.';
}
else
{
echo 'Yes all elements in array are type integer.';
}
You can create own batch testing function. It may be static function on your utility class!
/**
* #param array $array
* #return bool
*/
public static function is_all_numeric(array $array){
foreach($array as $item){
if(!is_numeric($item)) return false;
}
return true;
}
Use gettype()
http://php.net/manual/en/function.gettype.php
You have to set a flag and look at all the items.
$isNumeric = true;
foreach ($string_detecting_array as $key) {
if (!is_numeric($key)) {
$isNumeric = false;
}
}
if ($isNumeric) {
echo 'Yes all elements in array are type integer.';
}
else {
echo "Not all elements in array were type integer.";
}
I'm using code igniter and trying to write tests for the data returned by my models; CI's testing library is very limited in what it will check for, so I've been doing this:
$test = "My Test Name";
$expected = md5(print_r(array(array('val','other'), array('new','old','blue')), 1));
$result = md5(print_r($this->model->method($data), 1));
if($expected == $result) {
echo "$test [pass]";
}
else {
echo "$test [fail]";
}
Are there any pitfalls to testing this way? Is there a preferred, simple library that works with CI that I didn't find? The built-in testing library won't allow you to check results with this fine a grain so I find it not terribly helpful.
Yes, that should work just fine, as if both are identical the hashes should be identical as well.
However, why not just examine the output of print_r directly? Or, for that matter, use a completely separate tool like PhpUnit for testing?
Assuming the two variables you are comparing are both arrays, have a look at the inbuilt array_diff() function (http://php.net/manual/en/function.array-diff.php). If you need to compare the indexes of the arrays also, use array_diff_assoc() instead.
However, if you want a complete check for testing multi-dimentional arrays (as yours is), here's a function that should do that for you:
function compare_arrays($first_array, $second_array, $compare_keys = false){
/* Test if arrays are actually arrays */
if(!is_array($first_array)){
// Elements are not arrays
if(is_array($second_array)){
// Second array is not array
return false;
}
if($first_array == $second_array){
return true;
}
} elseif(!is_array($second_array)){
// Second array is not array (first is)
return false;
}
/* Test same number of elements */
if(count($first_array) != count($second_array)){
return false;
}
$size = count($first_array);
if($compare_keys){
// Load keys to compare
$first_keys = array_keys($first_array);
$second_keys = array_keys($second_array);
}
for($i = 0; $i < $size; $i++){
if($compare_keys && $first_keys[$i] != $second_keys[$i]){
// Keys do not match
return false;
}
if(is_array($first_array[$i])){
// Element is array
if(!is_array($second_array[$i])){
// Matching element in other array is not array
return false;
}
$result = compare_arrays($first_array[$i], $second_array[$i], $compare_keys);
if(!$result){
// Sub-arrays do not match
return false;
}
// Match - skip iteration
continue;
} elseif(is_array($second_array[$i])){
// Second array element is array (first is not)
return false;
}
// Elements are not arrays
if($first_array[$i] != $second_array[$i]){
return false;
}
}
// Match
return true;
}
Usage:
$br = '<br />';
$array_one = array(4, 5, array(2, 5), 'hello', 'element');
if(compare_arrays($array_one, $array_one)){
echo 'Match 1'.$br;
} else {
echo 'No Match 1'.$br;
}
$array_two = array(5, 5, array(2, 5), 'hello', 'element'); // Slightly different
if(compare_arrays($array_one, $array_two)){
echo 'Match 2'.$br;
} else {
echo 'No Match 2'.$br;
}
/* Output:
Match 1
No Match 2
*/
if($array1 === $array2) {
# the arrays are matched with identical keys and values
# no matter how deeply nested
}
No hashing required.
Without having to change the function signature, I'd like a PHP function to behave differently if given an associated array instead of a regular array.
Note: You can assume arrays are homogenous. E.g., array(1,2,"foo" => "bar") is not accepted and can be ignored.
function my_func(Array $foo){
if (…) {
echo "Found associated array";
}
else {
echo "Found regular array";
}
}
my_func(array("foo" => "bar", "hello" => "world"));
# => "Found associated array"
my_func(array(1,2,3,4));
# => "Found regular array"
Is this possible with PHP?
Just check the type of any key:
function is_associative(array $a) {
return is_string(key($a));
}
$a = array(1 => 0);
$a2 = array("a" => 0);
var_dump(is_associative($a)); //false
var_dump(is_associative($a2)); //true
You COULD use a check with array_values if your arrays are small and you don't care about the overhead (if they are large, this will be quite expensive as it requires copying the entire array just for the check, then disposing of it):
if ($array === array_values($array)) {}
If you care about memory, you could do:
function isAssociative(array $array) {
$c = count($array);
for ($i = 0; $i < $c; $i++) {
if (!isset($array[$i])) {
return true;
}
}
return false;
}
Note that this will be fairly slow, since it involves iteration, but it should be much more memory efficient since it doesn't require any copying of the array.
Edit: Considering your homogenious requirement, you can simply do this:
if (isset($array[0])) {
// Non-Associative
} else {
// Associative
}
But note that numerics are valid keys for an associative array. I assume you're talking about an associative array with string keys (which is what the above if will handle)...
Assuming $foo is homogeneous, just check the type of one key and that's it.
<?php
function my_func(array $foo) {
if (!is_int(key($foo))) {
echo 'Found associative array';
} else {
echo 'Found indexed array';
}
}
?>
In the light of your comment Assume arrays are homogenous; no mixtures.:
Just check if first (or last, or random) key is an integer or a string.
This would be one way of doing it, by checking if there's any keys consisting of non-numeric values:
function my_func($arr) {
$keys = array_keys($arr); // pull out all the keys into a new array
$non_numeric = preg_grep('/\D/', $keys); // find any keys containing non-digits
if (count($non_numeric) > 0) {
return TRUE; // at least one non-numeric key, so it's not a "straight" array
} else {
return FALSE: // all keys are numeric, so most likely a straight array
}
}
function is_associative($array) {
return count(array_keys($array)) != array_filter(array_keys($array), 'is_numeric');
}
What is the most efficient way to check if an array is a flat array
of primitive values or if it is a multidimensional array?
Is there any way to do this without actually looping through an
array and running is_array() on each of its elements?
Use count() twice; one time in default mode and one time in recursive mode. If the values match, the array is not multidimensional, as a multidimensional array would have a higher recursive count.
if (count($array) == count($array, COUNT_RECURSIVE))
{
echo 'array is not multidimensional';
}
else
{
echo 'array is multidimensional';
}
This option second value mode was added in PHP 4.2.0. From the PHP Docs:
If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count() will recursively count the array. This is particularly useful for counting all the elements of a multidimensional array. count() does not detect infinite recursion.
However this method does not detect array(array()).
The short answer is no you can't do it without at least looping implicitly if the 'second dimension' could be anywhere. If it has to be in the first item, you'd just do
is_array($arr[0]);
But, the most efficient general way I could find is to use a foreach loop on the array, shortcircuiting whenever a hit is found (at least the implicit loop is better than the straight for()):
$ more multi.php
<?php
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
$c = array(1 => 'a',2 => 'b','foo' => array(1,array(2)));
function is_multi($a) {
$rv = array_filter($a,'is_array');
if(count($rv)>0) return true;
return false;
}
function is_multi2($a) {
foreach ($a as $v) {
if (is_array($v)) return true;
}
return false;
}
function is_multi3($a) {
$c = count($a);
for ($i=0;$i<$c;$i++) {
if (is_array($a[$i])) return true;
}
return false;
}
$iters = 500000;
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
is_multi($a);
is_multi($b);
is_multi($c);
}
$end = microtime(true);
echo "is_multi took ".($end-$time)." seconds in $iters times\n";
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
is_multi2($a);
is_multi2($b);
is_multi2($c);
}
$end = microtime(true);
echo "is_multi2 took ".($end-$time)." seconds in $iters times\n";
$time = microtime(true);
for ($i = 0; $i < $iters; $i++) {
is_multi3($a);
is_multi3($b);
is_multi3($c);
}
$end = microtime(true);
echo "is_multi3 took ".($end-$time)." seconds in $iters times\n";
?>
$ php multi.php
is_multi took 7.53565130424 seconds in 500000 times
is_multi2 took 4.56964588165 seconds in 500000 times
is_multi3 took 9.01706600189 seconds in 500000 times
Implicit looping, but we can't shortcircuit as soon as a match is found...
$ more multi.php
<?php
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
function is_multi($a) {
$rv = array_filter($a,'is_array');
if(count($rv)>0) return true;
return false;
}
var_dump(is_multi($a));
var_dump(is_multi($b));
?>
$ php multi.php
bool(true)
bool(false)
For PHP 4.2.0 or newer:
function is_multi($array) {
return (count($array) != count($array, 1));
}
I think this is the most straight forward way and it's state-of-the-art:
function is_multidimensional(array $array) {
return count($array) !== count($array, COUNT_RECURSIVE);
}
After PHP 7 you could simply do:
public function is_multi(array $array):bool
{
return is_array($array[array_key_first($array)]);
}
You could look check is_array() on the first element, under the assumption that if the first element of an array is an array, then the rest of them are too.
I think you will find that this function is the simplest, most efficient, and fastest way.
function isMultiArray($a){
foreach($a as $v) if(is_array($v)) return TRUE;
return FALSE;
}
You can test it like this:
$a = array(1 => 'a',2 => 'b',3 => array(1,2,3));
$b = array(1 => 'a',2 => 'b');
echo isMultiArray($a) ? 'is multi':'is not multi';
echo '<br />';
echo isMultiArray($b) ? 'is multi':'is not multi';
Don't use COUNT_RECURSIVE
click this site for know why
use rsort and then use isset
function is_multi_array( $arr ) {
rsort( $arr );
return isset( $arr[0] ) && is_array( $arr[0] );
}
//Usage
var_dump( is_multi_array( $some_array ) );
Even this works
is_array(current($array));
If false its a single dimension array if true its a multi dimension array.
current will give you the first element of your array and check if the first element is an array or not by is_array function.
You can also do a simple check like this:
$array = array('yo'=>'dream', 'mydear'=> array('anotherYo'=>'dream'));
$array1 = array('yo'=>'dream', 'mydear'=> 'not_array');
function is_multi_dimensional($array){
$flag = 0;
while(list($k,$value)=each($array)){
if(is_array($value))
$flag = 1;
}
return $flag;
}
echo is_multi_dimensional($array); // returns 1
echo is_multi_dimensional($array1); // returns 0
I think this one is classy (props to another user I don't know his username):
static public function isMulti($array)
{
$result = array_unique(array_map("gettype",$array));
return count($result) == 1 && array_shift($result) == "array";
}
In my case. I stuck in vary strange condition.
1st case = array("data"=> "name");
2nd case = array("data"=> array("name"=>"username","fname"=>"fname"));
But if data has array instead of value then sizeof() or count() function not work for this condition. Then i create custom function to check.
If first index of array have value then it return "only value"
But if index have array instead of value then it return "has array"
I use this way
function is_multi($a) {
foreach ($a as $v) {
if (is_array($v))
{
return "has array";
break;
}
break;
}
return 'only value';
}
Special thanks to Vinko Vrsalovic
Its as simple as
$isMulti = !empty(array_filter($array, function($e) {
return is_array($e);
}));
This function will return int number of array dimensions (stolen from here).
function countdim($array)
{
if (is_array(reset($array)))
$return = countdim(reset($array)) + 1;
else
$return = 1;
return $return;
}
Try as follows
if (count($arrayList) != count($arrayList, COUNT_RECURSIVE))
{
echo 'arrayList is multidimensional';
}else{
echo 'arrayList is no multidimensional';
}
$is_multi_array = array_reduce(array_keys($arr), function ($carry, $key) use ($arr) { return $carry && is_array($arr[$key]); }, true);
Here is a nice one liner. It iterates over every key to check if the value at that key is an array. This will ensure true