I have a class which allows to retrieve an array according to the syntax "key.key2.key3".
For now, no problem. Problems occur when one of the keys is an object.
Because I have to check that the key is an array or an object
My script bug when I try to determine if type of key[key2] == array or if type of key->key2 == object. One of the two conditions may be false. (If script tests that key[key2] == array, and that key2 is an object, for example).
Is there a way to verify that key2 is an array or an object without doing key[key2] or key->key2 ?
Thanks.
You need to perform the test on the parent, before accessing the key.
Here is an example:
function findValue($a, $s) {
$keys = explode(".", $s);
foreach ($keys as $key) {
if (is_object($a)) {
$a = $a->$key;
} else if (is_array($a)) {
$a = $a[$key];
} else { // unexpected
return null;
}
}
return $a;
}
// Sample data: mix of object and array:
$arr = [
"key1" => (object) [
"key2" => [
"key3" => 42
]
]
];
echo findValue($arr, "key1.key2.key3"); // 42
What about is_array()?
Docs here
Related
I'm trying to find a key in an array that doesn't start with zero.
This is my not so elegant solution:
private $imagetypes = [
1 => [
'name' => 'banner_big',
'path' => 'campaigns/big/'
],
2 => [
'name' => 'banner_small',
'path' => 'campaigns/small/'
],
// ...
If i use $key = array_search('banner_big', array_column($this->imagetypes, 'name')); the result is 0
I came up with this solution but I feel like I needlessly complicated the code:
/**
* #param string $name
* #return int
*/
public function getImagetypeFromName($name)
{
$keys = array_keys($this->imagetypes);
$key = array_search($name, array_column($this->imagetypes, 'name'));
if ($key !== false && array_key_exists($key, $keys)) {
return $keys[$key];
}
return -1;
}
Is there a better solution then this.
I can't change the keys in.
Just save indexes
$key = array_search('banner_big',
array_combine(array_keys($imagetypes),
array_column($imagetypes, 'name')));
demo on eval.in
The problem is array_column will return a new array (without the existing indexes)
So in your example.
$key = array_search('banner_big', array_column($this->imagetypes, 'name'));
var_dump($key);
//$key is 0 as 0 is the key for the first element in the array returned by array_column.
You can mitigate against this by creating a new array with the existing keys.
That's because array_column() generates another array (starting at
index zero), as you may have imagined. An idea to solve this would be to
transform the array with array_map(), reducing it to key and image
name (which is what you're searching for). The keys will be the same,
and this can be achieved with a simple callback:
function($e) {
return $e['name'];
}
So, a full implementation for your case:
public function
getImagetypeFromName($name)
{
$key = array_search($name, array_map(function($e) {
return $e['name'];
}, $this->imagetypes));
return $key ?: -1;
}
I have tried out with the google and tried myself to get done for the below functionality. I need a function that will validate each array element whether it is scalar or not. So i wrote a simple function that will iterate each element of the array and checks for scalar or not.
But the real requirement, the array could be a multi dimentional array. So i have modified the array and called the function recursively as below, But it will not go-through all elements in the array.
function validate_scalar($params)
{
foreach ($params as $key => $arg)
{
if (is_array($arg))
{
validate_scalar($arg);
}
else
{
if (!is_scalar($arg))
{
// throwing an exception here if not scalar.
}
}
}
return true;
}
Is there any method to achieve this functionality? Please help me on this.
array_walk_recursive
You could use something like this:
<?php
$array = array(
'kalle' => 'asdf',
'anka' => array(
123,
54324,
new stdClass()
)
);
array_walk_recursive($array, function ($item, $key) {
if (!is_scalar($item)) {
echo $key . " => : Is not scalar\n";
return false;
}
echo $key . " => : Is scalar\n";
return true;
});
array_walk_recursive ignores values that are arrays
output:
kalle => : Is scalar
0 => : Is scalar
1 => : Is scalar
2 => : Is not scalar
I think I could do this with a foreach loop like this:
foreach ($haystack as $item)
if (isset($item->$needle_field) && $item->$needle_field == $needle)
return true;
}
but i was wandering if it could be done without a loop?
something like:
if(in_array($item->$needle_field == $needle,$haystack)
return true;
Yes, in modern PHP you can determine if a specific object property contains a specific value without a classic loop by combining the forces of array_column() (which has evolved to also handle arrays of objects) and in_array().
Code: (Demo)
$objects = [
(object)['cats' => 2],
(object)['dogs' => 2],
(object)['fish' => 10],
(object)['birds' => 1],
];
$needleField = 'cats';
$needleValue = 2;
var_export(
in_array($needleValue, array_column($objects, $needleField))
);
// output: true
The advantage of this technique is the obviously concise syntax. This is a perfectly acceptable approach for relatively small volumes of data.
A possible disadvantage to this technique is that array_column() will be generating a new array of all of values that relate to the $needleField.
In my above demo, array_column() will only generate a single-element array because there is only one cats property in all of the objects. If we were processing a relatively large volume of data, then it would be inefficient to bother collecting all of the qualifying cats values and then run in_array() when only one match is necessary to return true.
For "large" volumes of data where performance is a primary criterion for script design, a classic foreach loop would be a better choice and as soon as an object satisfies the rules, then the loop should be halted via return or break.
Code: (Demo)
function hasPropertyValue(array $objects, $property, $value): bool {
foreach ($objects as $object) {
if (property_exists($object, $property) && $object->{$property} === $value) {
return true;
}
}
return false;
}
var_export(
hasPropertyValue($objects, $needleField, $needleValue)
);
It's possible, but it's not any better:
<?php
function make_subject($count, $success) {
$ret = array();
for ($i = 0; $i < $count; $i++) {
$object = new stdClass();
$object->foo = $success ? $i : null;
$ret[] = $object;
}
return $ret;
}
// Change true to false for failed test.
$subject = make_subject(10, true);
if (sizeof(array_filter($subject, function($value) {
return $value->foo === 3;
}))) {
echo 'Value 3 was found!';
} else {
echo 'Value 3 was not found.';
}
Outputs Value 3 was found!.
I advise you remain with the for loop: it's legible, unlike any tricks to save a line that you might find.
This will not work if the array you are searching is out of your control. But, if you are the one building the array of objects to be searched, you can structure it using the needle as array keys to be used with array_key_exists when you are searching.
For example, instead of making your $haystack array like this:
[
{
'needle_field' => $needle
},
...
]
Make it like this:
[
$needle => {
'needle_field' => $needle
},
...
]
And search like this:
if (array_key_exists($needle, $haystack)) {
return true;
}
Finally, if you need to, you can convert back to an integer indexed array by using array_values
$haystack = array_values($haystack);
This may not work in all situations but it worked great for me.
Maybe with array_key_exists:
if (array_key_exists($needle_field, $haystack) {
if ($haystack[$needle_field] == $needle) {
echo "$needle exists";
}
}
All my AJAX requests are in json format which are being parsed in javascript.
How can i prevent null values being displayed in an HTML page without needing to write an if-statement in javascript for each json value?
Or should i write a custom PHP function to encode an array to json instead of using json_encode() so it doesn't display 'null' ?
In server side with PHP,
You can use array_filter before json_encode.
array_filter without second argument removes null elements of entry array, example :
$object= array(
0 => 'foo',
1 => false,
2 => -1,
3 => null,
4 => ''
);
$object = (object) array_filter((array) $object);
$result = json_encode($object);
The $result contains:
{"0":"foo","2":-1}
As you see, the null elements are removed.
I'm going to add to the accepted answer because that will only work if you have a 1 dimensional object or array. If there is any array or object nesting then in order to get the accepted solution to work, you must create some sort of recursive array filter. Not ideal.
The best solution my colleague and I came up with was to actually perform a regular expression on the JSON string before it was returned from the server.
$json = json_encode($complexObject);
echo preg_replace('/,\s*"[^"]+":null|"[^"]+":null,?/', '', $json);
The regular expression will remove all places in the string of the form ,"key":null including any whitespace between the leading comma and the start of the key. It will also match "key":null, afterwards to make sure that no null values were found at the beginning of a JSON object.
This isn't an ideal solution but it's far better than creating a recursive array filter given an object could be several dimensions deep. A better solution would be if json_encode had a flag that let you specify if you wanted null entries to remain in the output string.
To remove only NULL, but keep FALSE, '', and 0:
function is_not_null($var)
{
return !is_null($var);
}
echo json_encode(array_filter((array) $object, 'is_not_null'));
public function __toString() {
$obj = clone $this;
$keys = get_object_vars($obj);
foreach ($keys as $key => $value) {
if (!$value) {
unset($obj->{$key});
}
}
return json_encode($obj);
}
What about using the native JSON.stringify method on the javascript side?
You can set, a second parameter, a function to remove keys with a null value.
If you return undefined, the property is not included in the output JSON string (check the documentation for "the replacer parameter" at https://developer.mozilla.org/en-US/docs/Using_native_JSON#The_replacer_parameter).
function removeNulls(obj) {
var str = JSON.stringify(obj, function(key, val) {
if (val == null) return undefined;
return val;
});
return JSON.parse(str);
}
Then you can have a "normalized" JSON object by calling:
var obj = removeNulls(obj);
echo json_encode(array_filter((array) $object, function($val) {
return !empty($val);
}));
class Foo implements JsonSerializable
{
public $param1;
public $param2;
public function jsonSerialize()
{
return array_filter((array) $this, function ($var) {
return !is_null($var);
});
}
}
public function testJsonSerialization()
{
$expectedJson = '{"param1":true}';
$foo = new Foo();
$foo->param1 = true;
$foo->param2 = null;
$this->assertEquals(
$expectedJson,
json_encode($foo)
);
}`
of course you can create Abstract class with custom jsonSerialize method and extend all your DTOs from this
A version that works with multi-dimensional arrays.
$withoutNull = function($a) use (&$withoutNull) {
return array_filter(
array_map(
fn($p) => is_array($p) ? $withoutNull($p) : $p, $a
)
);
};
Example:
$a = [
"name" => "nathan",
"data" => [
"age" => 27,
"something" => null
],
"another" => null
];
$b = $withoutNull($a);
print_r($b);
Output:
Array
(
[name] => nathan
[data] => Array
(
[age] => 27
)
)
I want to take an array and use that array's values to populate an object's properties using the array's keynames. Like so:
$a=array('property1' => 1, 'property2' => 2);
$o=new Obj();
$o->populate($a);
class Obj
{
function Populate($array)
{
//??
}
}
After this, I now have:
$o->property1==1
$o->property2==2
How would I go about doing this?
foreach ($a as $key => $value) {
$o->$key = $value;
}
However, the syntax you are using to declare your array is not valid. You need to do something like this:
$a = array('property1' => 1, 'property2' => 2);
If you don't care about the class of the object, you could just do this (giving you an instance of stdClass):
$o = (Object) $a;
Hm. What about having something like
class Obj
{
var properties = array();
function Populate($array)
{
this->properties = $array;
}
}
Then you can say:
$o->properties['property1'] == 1
...