Is there any more efficient,cleaner and shorter form for following:
if($x==null or $y==null or $z==null or $w==null) return true;
In general how can I also get name of null variables?
function any_null(array $a){
foreach($a as $v) if ($v==null) return //variable name;
return false;
}
var_dump(any_null(array($x,$y,$z,$w)));
isset can take multiple arguments.
If multiple parameters are supplied then isset() will return TRUE only
if all of the parameters are set.
Since this is the opposite of what you want a simple negation is needed:
!isset($x, $y, $z, $w)
An additional bonus is that isset is not a regular function and will not raise any notices on undefined variables.
It's harder to get the name from the variables. You will need to check every one of them in some way. I think the best and most scalable solution is to use an associative array with the name of the variable as key:
function any_null(array $a){
foreach($a as $k => $v) if ($v === null) return $k;
return false;
}
any_null(array('x' => $x, 'y' => $y));
Note the strict comparison (===). Otherwise false, "", 0 and array() would be counted as "null" as well.
Another solution is to only pass the name of your variables and make use of the $GLOBALS array. Here I've coupled it with a call to func_get_args to get a little less verbose calling convention:
function any_null(){
$a = func_get_args();
foreach($a as $k) if (!isset($GLOBALS[$k])) return $k;
return false;
}
any_null('x', 'y', 'z');
This last solution have a lot of shortcomings though. You can only test variables in the global scope, and you can only test standard variables (not elements in an array, not objects and so on). The syntax for those would be very clunky.
Related
This is the code I used to have to check if $A doesn't match $B
if($A!=$B) {
$set = array();
echo $val= str_replace('\\/', '/', json_encode($set));
//echo print_r($_SERVER);
exit;
}
Now I need the opposite of this condition: ($A need to match one of these $B,$C or $D)
A simple shortcut to seeing if a value matches one of multiple values you can put the values to be compared against ($B, $C, and $D) into an array and then use in_array() to see if the original value ($A) matches any of them.
if (in_array($A, [$B, $C, $D])) {
// ...
}
If you don't want it to match any of $B, $C, or $D just use !:
if (!in_array($A, [$B, $C, $D])) {
// ...
}
You can use array_search
$B = 'B';
$C = 'C';
$D = 'D';
//match B
$A = 'B';
$options = [$B, $C, $D];
if (false !== ($index = array_search($A, $options ))) {
echo "Match: {$index} '{$options[$index]}'";
}
Output
Match: 0 'B'
Sandbox
The nice thing here is you can set the $index and use that to tell which one matched later.
Note you have to use false !== because array search returns the index where the match happened at, so it can happen on the first array element which is index 0. As we know PHP can treat 0 as false (in this case the condition would fail when it should pass). However, when we use the strict type check PHP also compares the type and INT 0 is not BOOL false (which passes the condition).
for reference.
http://php.net/manual/en/function.array-search.php
Another probably the most efficient way is to use isset, and use keys instead of values:
$options = [$B=>1,$C=>1,$D=>1]; //values don't matter
if(!isset($options[$A])){
//....
}
I am talking about the second "return -1;" on the 12th line of the code. This gets reached only if two sets of numbers are exactly the same, like when comparing '192.167.11' to '192.167.11'. I will also add that using range(0,2) would be a better option for this piece of code (range(0,3) produces errors if two elements happen to be the same; I did not change that as this is the original code example from PHP Array Exercise #21 from w3resource.com).
<?php
function sort_subnets($x, $y){
$x_arr = explode('.', $x);
$y_arr = explode('.', $y);
foreach (range(0, 3) as $i) {
if ($x_arr[$i] < $y_arr[$i]) {
return -1;
} elseif ($x_arr[$i] > $y_arr[$i]) {
return 1;
}
}
return -1;
}
$subnet_list =
array('192.169.12',
'192.167.11',
'192.169.14',
'192.168.13',
'192.167.12',
'122.169.15',
'192.167.16'
);
usort($subnet_list, 'sort_subnets');
print_r($subnet_list);
?>
Returning "-1" would move the second element (the same as the first in the current $x and $y pair) towards the higher index of the array (down the array). Why not return "0" and keep everything as is if the two elements are exactly the same? Is there any reason for returning the "-1" maybe based on how the usort() works (or any other factor of this)?
Thanks.
EDIT:
I think that this is Insertion Sort (array size 6-15 elements; normally it would be Quicksort).
If the two elements are the same, there's no difference between swapping the order and keeping the order the same. So it doesn't make a difference what it returns in that case.
You're right that 0 is more appropriate. This would be more important if usort were "stable". But the documentation says
Note:
If two members compare as equal, their relative order in the sorted array is undefined.
To illustrate the excellent point of #Don'tPanic:
<?php
function sort_subnets($x, $y){
$x_arr = explode('.', $x);
$y_arr = explode('.', $y);
return $x_arr <=> $y_arr;
}
$subnet_list =
array('192.169.12',
'192.167.11',
'192.169.14',
'192.168.13',
'192.167.12',
'122.169.15',
'192.167.16'
);
usort($subnet_list, 'sort_subnets');
print_r($subnet_list);
See live code
Note the use of the "spaceship" operator, namely <=> which offers a conciseness that spares one from having to write code like the following in a function:
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
Lastly, note that the user-defined callback for usort() makes use of ternary logic because sometimes as in the case of sorting bivalent logic is insufficient. Yet, usort() itself utilizes two-part logic, returning TRUE on success and FALSE on failure.
function foo($a)
{
$b = ...;
$c = ...;
return (both b and c);
}
and so I could get $b value to $first and $c value to $second
I know you can return more than 1 variable by return array($b,$c) but then it should be $var[0] and $var[1] and then I need to type $first = $var[0] and $second = $var[1] and so I'm creating more useless variables
So is it possible to do so without array?
Fundamentally, functions only have one return value. You could return a class with member variables first and second, or an associative array with keys "first" and "second", but you'll still only be returning a single object.*
Alternatively, you could references to $first and $second into your function:
function foo($a, &$b, &$c)
{
$b = ...;
$c = ...;
}
foo(42, $first, $second);
I'm not a big fan of this approach, though, because it's not immediately clear from the call-site that $first and $second are going to be modified.
* Note that if you return an array, you can always use the short-hand list($first,$second) = foo(42);.
No, it can't.
But you can still return an array from function, but use "list" to accept the result for convenient:
list ($first, $second) = foo ($a);
No, you cannot to that. Function returns only one result.
What you can do, if possible in you case, is pass a variable by reference.
function foo($a, &$b, &$c){
$b = ...;
$c = ...;
}
The following will make changes to $b and $c visible outside of the function scope.
The only alternative to avoid returning an array is to return an object, or serialized data, but you don't "win" something from that.
I have a variable $v that can be either single string or array of stringsand I have a code:
$a = array();
if (is_array($v)) {
$a = $v;
} else {
$a[] = $v;
}
How it can be done in more elegant way? (in other words, how to cast a variable to array)
You can cast a variable to an array by using:
$var = (array)$arr;
$a = (array) $v;
is the answer.
I would write your could snippet like this (short and you read it and know exactly what is happening):
$a = is_array($v) ? $v : array($v);
Alternatively you could use settype:
settype($a, "array");
For expliciting the variable type. It's exactly the same as what happens with a typecast behind the scenes. (More useful for group-wise typecasting e.g. in loops.)
As others have said, casting a scalar value to an array will produce a singleton array (i.e. an array with the scalar as its only element). However, as still others have pointed out, take care to only do this if you know the value is going to be a scalar and not a class instance.
From the PHP docs:
For any of the types integer, float, string, boolean and resource,
converting a value to an array results in an array with a single
element with index zero and the value of the scalar which was
converted. In other words, (array)$scalarValue is exactly the same as
array($scalarValue).
If an object is converted to an array, the result is an array whose
elements are the object's properties. The keys are the member variable
names, with a few notable exceptions: integer properties are
unaccessible; private variables have the class name prepended to the
variable name; protected variables have a '*' prepended to the
variable name. These prepended values have null bytes on either side.
If $v is a scalar (Boolean, String, Number) you can use:
a) $v = (array)$v;
If $v is an object, you have to use:
b) $v = is_array($v) ? $v : array($v);
Method (b) works in every case (with scalars too).
If you are looking to convert an object to a single count array you can use the follow code:
$list = array([0] => $obj);
The other provided answers won't work when trying to convert an object, it will simply convert the fields of that object into an associative array (unless that is what you are trying to do).
$var = (array)$arr;
Actually if you want to cast to an array and not have to worry about what you put into it, the answer is
$var = (is_object($var)) ? array($var) : (array) $var;
You can test this with the following code
function toArray($var) {
return (is_object($var)) ? array($var) : (array) $var;
}
$object = new stdClass;
$resource = fopen('php://stdout', 'w');
$closure = function () {};
$tests = array(
array(toArray(true), array(true), 'boolean true'),
array(toArray(false), array(false), 'boolean false'),
array(toArray(null), array(), 'null'),
array(toArray(1), array(1), 'positive integer'),
array(toArray(0), array(0), 'zero integer'),
array(toArray(-1), array(-1), 'negative integer'),
array(toArray(1.5), array(1.5), 'positive float'),
array(toArray(0.0), array(0.0), 'zero float'),
array(toArray(-1.5), array(-1.5), 'negative float'),
array(toArray(''), array(''), 'empty string'),
array(toArray('foo'), array('foo'), 'string'),
array(toArray(array()), array(), 'array'),
array(toArray($object), array($object), 'object'),
array(toArray($resource), array($resource), 'resource'),
array(toArray($closure), array($closure), 'closure'),
);
foreach ($tests as $test) {
ob_start();
var_dump($test[0]);
$a = ob_get_clean();
ob_start();
var_dump($test[1]);
$b = ob_get_clean();
assert($a === $b, "{$test[2]} is not the same");
}
I want to know how to array_intersect for object array.
You can use array_uintersect in conjunction with spl_object_hash, see an example:
array_uintersect($a, $b, function($a, $b) {
return strcmp(spl_object_hash($a), spl_object_hash($b));
});
where '$a' and '$b' are arrays of some objects that you want to intersect.
nice toString function is already implemented and is called serialize ;) so
array_map(
'unserialize',
array_intersect(
array_map(
'serialize',
$obj1
),
array_map(
'serialize',
$obj2
)
)
);
will do the work, example mentioned higher don't work 'cause array_intersect work's only with strings as someone mentioned too
array_intersect() returns an array containing all the values of array1 that are present in all the arguments.
Then what mean present in this context (exacly this function), i found on php.net my answer:
Two elements are considered equal if
and only if (string) $elem1 ===
(string) $elem2. In words: when the
string representation is the
same.
Then you can't use it on array of objects if your objects not implements unique conversion to string.
Had a similar problem a few days ago, while these are answers are on the right path; I used them to work out the following:
From Artefacto's answer return $obj1 == $obj2 didn't really work, so I wrote a simple comparative function (basically gets the md5 of the serialised object and compares that):
function object_compare($obj1, $obj2){
$md5 = function($obj){
return md5(serialize($obj));
};
return strcmp($md5($obj1), $md5($obj2));
}
Then it’s jut a matter of calling array_uintersect with our comparative function to get the intersection:
# $array1 / $array2 are the array of objects we want to compare
return array_uintersect($array1, $array2, 'object_compare');
In my case, I had an unknown / dynamic array of objects, so I took it a step further so I don't have to declare array_uintersect($array1, $array2, ...) specifically - but just be able to pass in an array of arrays (of objects):
# $multiarray_of_objects is our array of arrays
$multiarray_of_objects[] = 'object_compare';
return call_user_func_array('array_uintersect', $multiarray_of_objects);
Just gotta remember to pass in the reference to our callback / comparative function as the last string in the array. Works like a charm!
The correct way to check whether two objects are equal is to use ==. Therefore:
array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 == $a2; });
Just for completeness: Implement __toString() method in your object returning a unique value. For database entities this might be as easy as returning the fully qualified class name postfixed with the ID of the record. But it can also be arbitrarily complex by doing some hashing or even worse things.
In my opinion, it's the class's duty to serialize itself or create something unique to compare its objects by. Using anything outside of a class to serialize an object might result in strange behaviour (including comparing objects of different classes, which must never result in equality).
I use array_udiff to implement array_intersect for an object array.
function diff($a, $b) {
if($a === $b) {
return 0;
} else {
return 1;}
}
$array_1 = array('a', 'b', 'c');
$array_2 = array('c', 'd','e');
$array = array_udiff($array_1, array_udiff($array_1, $array_2, 'diff'),'diff');
var_dump($array);
return array(1) { [2]=> string(1) "c" }
You can have your own diff function for any scheme.
The correct solution would be:
array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 != $a2; });
Note the != in the callback function as opposed to the answer from #Artefacto. Based on the documentation of array_uintersect, the callback function has to return 0 (false) if array items are equal.