How to check if NULL value exists in multidimesional array? - php

I have seen some methods to determine if some value exists in php multidimensional array but I am stuck at simple problem.
I have a following array.
$data = [
['name' => 'sagar', 'address' => 'kathmandu'],
['name' => 'sagar', 'address' => null ]
];
I want to check if null value exists anywhere inside 2 dimensional array and keys can be anything. Is there any builtin function or we have made our own or some methodology to check ?
If you have any idea, it would be extremely helpful.

$hasNull = false;
foreach ($data as $set) {
if ($hasNull = in_array(null, $set, true)) {
break;
}
}

Related

Get value of dynamic sub-array, in multidimensional array

Not sure if my question is clear, but here's what I'm trying to achieve. Let’s say I have a multidimensional array like so:
$arr['client1']**['dog']['Jack']**
$arr['client2']['cat']['Stacy']
How can I get the second portion of the array (between **), knowing it can be anything. For client 3, it could be a crocodile. For Client 4, it could be a car.
So I'm looking to "build" the structure of the array, dynamically. Something like so:
$arr['client1']{partBetweenThe**InTheExemple}
{partBetweenThe**InTheExemple} would be constructed "on the fly" (hence, the dynamically).
EDIT: Hopefully some clarifications...
The array changes every time. Basically, I'm building an addon to poll any API on the web. The data structure I'm getting can be anything. So what I need to do is build the key combination "on the fly", with variables.
In the exemple above, my variable would be something like $query = ['dog']['Jack'] and to get the value, I would poll it like so (from a logistic perspective, I know this doesn't work):
$arr['client1'][$query] or $arr['client1']$query or $arr['client1']{$query}
You can define the query as an array with each level as an element. Then we can iterate through that and check if we find a matching key in the response:
function findInArray(array $query, array $data)
{
foreach ($query as $key) {
if (!array_key_exists($key, $data)) {
// The key was not found, abort and return null
return null;
}
// Since the key was found, move to next level
$data =& $data[$key];
}
return $data;
}
// Example response
$response = [
'client1' => [
'dog' => [
'Jack' => 'Some value',
],
]
];
// Define the query as an array
$query = ['dog', 'Jack'];
$result = findInArray($query, $response['client1']);
Demo: https://3v4l.org/WjXTn
Edit:
So since the array's structure can't be changed this will return the client if the structure remains ['client']['animal']['name'].
$clients = [
'client1' => [
'dog' => [
'Jack' => []
]
],
'client2' => [
'cat' => [
'Stacy' => []
]
]
];
$animal = 'dog';
$name = 'Jack';
foreach ($clients as $client => $options) {
if (
array_key_exists($animal, $options) &&
array_key_exists($name, $options[$animal])
) {
echo $client;
break;
}
}

PHP Access a Nested Object Property Using Variable

I am using PHP7.
I have an Object I am trying to parse:
$RECORD = {
'name' => 'Stephen Brad Taylor',
'address' => '432 Cranberry Hills, Pittsburg',
'phone' => '708 865 456',
'Account' => (Object Vendor/Entity/User) {
'email' => 'INeedThisEmail#pleaseHelp.com' // I want to access this property.
'id' => 34,
'accessible' => ['email', 'id]
}
}
I have an array which I am using to select certain fields from RECORD:
$fieldnames = [
'name',
'address',
'phone',
'Account["email"]'
];
I am trying to parse the fieldnames from RECORD as follows:
$data[]
foreach($fieldnames as $k => $fieldname) {
$data[k] = $RECORD->$fieldname
}
The method above works for the first-level attributes: name, address, and phone. However, email returns null.
I have tried the following below and none have worked:
$data[k] = RECORD->${$fieldname}
$propertyName = '$RECORD->$fieldname'
$data[k] = ${$propertyName}
Does anyone know a way to access an object's properties using a string from an object reference?
Much gratitude <3
You can't use Account["email"] directly as a property accessor, it won't be split up to find the nested property. You need to parse it yourself.
foreach($fieldnames as $k => $fieldname) {
if (preg_match('/^(.*)\["(.*)"\]$/', $fieldname, $match) {
$data[$k] = $RECORD->{$match[1]}->{$match[2]};
} else {
$data[$k] = $RECORD->$fieldname;
}
}
Also, you need $ in $k.
This code only works for 1 level deep. If you need to deal with arbitrary levels, you'll have to write a recursive procedure. See How to access and manipulate multi-dimensional array by key names / path? for examples of how to code this.
I use anonymous functions for such cases. (PHP 7.4)
$index = fn($item) => $item->dotaItem->prop_def_index;
if (get_class($collection) === Collection::class) {
$index = fn($item) => $item->prop_def_index;
}

how to make an array follow a strict form? [duplicate]

Is there a function out there to make sure that any given array conforms to a particular structure? What I mean is that is has particular key names, perhaps particular types for values, and whatever nested structure.
Right now I have a place where I want to make sure that the array getting past has certain keys, a couple holding a certain data type, and one sub-array with particular key names. I've done a lot of run-around because I was passing malformed arrays to it, and finally I'm at the point where I have a bunch of
if ( ! isset($arr['key1']) ) { .... }
if ( ! isset($arr['key2']) ) { .... }
if ( ! isset($arr['key3']) ) { .... }
I would have saved a lot of time and consternation if I could have checked that the array conformed to a particular structure beforehand. Ideally something like
$arrModel = array(
'key1' => NULL ,
'key2' => int ,
'key3' => array(
'key1' => NULL ,
'key2' => NULL ,
),
);
if ( ! validate_array( $arrModel, $arrCandidate ) ) { ... }
So, the question I'm asking is, does this already exists, or do I write this myself?
Convert array to JSON:
http://us.php.net/manual/en/function.json-encode.php
Then check against a JSON Schema:
http://json-schema.org/
http://jsonschemaphpv.sourceforge.net/
It doesn't exist built in.
Maybe try something like (untested):
array_diff(array_merge_recursive($arrCandidate, $arrModel), $arrModel)
You can use the Symfony Validation component
Installation:
composer require symfony/validator doctrine/annotations
Usage:
$myArray = [
'name' => ['first_name' => 'foo', 'last_name' => 'bar'],
'email' => 'foo#email.com'
];
$constraints = new Assert\Collection([
'name' => new Assert\Collection([
'first_name' => new Assert\Length(['min' => 101]),
'last_name' => new Assert\Length(['min' => 1]),
]),
'email' => new Assert\Email(),
]);
$validator = Validation::createValidator();
$violations = $validator->validate($myArray, $constraints);
For more details, see How to Validate Raw Values (Scalar Values and Arrays)
accepted answer make diff based on values, since it's about array structure you dont want to diff values. Insted you should use [array_diff_key()][1]
Function alone is not recursive. It will not work out of box on sample array from question.
Edit after years: Got into similar trouble whit recursive array functions, so there is mine array_diff_recursive, this does not solve original question because it compare values as well, but i believe it can be easily modified and someone can find it useful .
function array_diff_recursive(array $array1, array $array2){
return array_replace_recursive(
array_diff_recursive_one_way($array1, $array2),
array_diff_recursive_one_way($array2, $array1)
);
}//end array_diff_recursive()
function array_diff_recursive_one_way(array $array1, array $array2)
{
$ret = array();
foreach ($array1 as $key => $value) {
if (array_key_exists($key, $array2) === TRUE) {
if (is_array($value) === TRUE) {
$recurse = array_diff_recursive_one_way($value, $array2[$key]);
if (count($recurse) > 0) {
$ret[$key] = $recurse;
}
} elseif (in_array($value, $array2) === FALSE) {
$ret[$key] = $value;
}
} elseif (in_array($value, $array2) === FALSE) {
$ret[$key] = $value;
}
}
return $ret;
}//end array_diff_recursive_one_way()```
[1]: http://php.net/manual/en/function.array-diff-key.php
I know this is sort of an old post, sorry if my answer is not approppriate.
I'm in the process of writing a php package that does exactly what you are asking for, it's called Structure.
What you can do with the package is something like:
$arrayCheck = new \Structure\ArrayS();
$arrayCheck->setFormat(array("profile"=>"array"));
if ($arrayCheck->check($myArray)) {
//...
}
You can check it out here: http://github.com/3nr1c/structure
I came across a tool called Matchmaker on GitHub, which looks very comprehensive and has composer support and unit tests:
https://github.com/ptrofimov/matchmaker
You can include it into your project with
composer require ptrofimov/matchmaker.

Remove deeply nested element from multi-dimensional array?

I need to remove an element form a deeply nested array of unknown structure (i.e. I do not know what the key sequence would be to address the element in order to unset it). The element I am removing however does have a consistent structure (stdObject), so I can search the entire multidimensional array to find it, but then it must be removed. Thoughts on how to accomplish this?
EDIT: This is the function I have right now trying to achieve this.
function _subqueue_filter_reference(&$where)
{
foreach ($where as $key => $value) {
if (is_array($value))
{
foreach ($value as $filter_key => $filter)
{
if (isset($filter['field']) && is_string($filter['field']) && $filter['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
unset($value[$filter_key]);
return TRUE;
}
}
return _subqueue_filter_reference($value);
}
}
return FALSE;
}
EDIT #2: Snipped of array structure from var_dump.
array (size=1)
1 =>
array (size=3)
'conditions' =>
array (size=5)
0 =>
array (size=3)
...
1 =>
array (size=3)
...
2 =>
array (size=3)
...
3 =>
array (size=3)
...
4 =>
array (size=3)
...
'args' =>
array (size=0)
empty
'type' => string 'AND' (length=3)
...so assuming that this entire structure is assigned to $array, the element I need to remove is $array[1]['conditions'][4] where that target is an array with three fields:
field
value
operator
...all of which are string values.
This is just a cursor problem.
function recursive_unset(&$array)
{
foreach ($array as $key => &$value) # See the added & here.
{
if(is_array($value))
{
if(isset($value['field']) && $value['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
unset($array[$key]);
}
recursive_unset($value);
}
}
}
Notes : you don't need to use is_string here, you can just make the comparison as you're comparing to a string and the value exists.
Don't use return unless you're sure there is only one occurrence of your value.
Edit :
Here is a complete example with an array similar to what you showed :
$test = array (
1 => array (
'conditions' =>
array (
0 => array ('field' => 'dont_care1', 'value' => 'test', 'operator' => 'whatever'),
1 => array ('field' => 'dont_care2', 'value' => 'test', 'operator' => 'whatever'),
2 => array ('field' => 'nodequeue_nodes_node__nodequeue_subqueue.reference', 'value' => 'test', 'operator' => 'whatever'),
3 => array ('field' => 'dont_care3', 'value' => 'test', 'operator' => 'whatever')
),
'args' => array (),
'type' => 'AND'
));
var_dump($test);
function recursive_unset(&$array)
{
foreach ($array as $key => &$value)
{
if(is_array($value))
{
if(isset($value['field']) && $value['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
unset($array[$key]);
}
recursive_unset($value);
}
}
}
recursive_unset($test);
var_dump($test);
One way to solve this was to extend your recursive function with a second parameter:
function _subqueue_filter_reference(&$where, $keyPath = array())
You'd still do the initial call the same way, but the internal call to itself would be this:
return _subqueue_filter_reference($value, array_merge($keyPath, array($key)));
This would provide you with the full path of keys to reach the current part of the array in the $keyPath variable. You can then use this in your unset. If you're feeling really dirty, you might even use eval for this as a valid shortcut, since the source of the input you'd give it would be fully within your control.
Edit: On another note, it may not be a good idea to delete items from the array while you're looping over it. I'm not sure how a foreach compiles but if you get weird errors you may want to separate your finding logic from the deleting logic.
I have arrived at a solution that is a spin-off of the function found at http://www.php.net/manual/en/function.array-search.php#79535 (array_search documentation).
Code:
function _subqueue_filter_reference($haystack,&$tree=array(),$index="")
{
// dpm($haystack);
if (is_array($haystack))
{
$result = array();
if (count($tree)==0)
{
$tree = array() + $haystack;
}
foreach($haystack as $k=>$current)
{
if (is_array($current))
{
if (isset($current['field']) && is_string($current['field']) && $current['field'] == 'nodequeue_nodes_node__nodequeue_subqueue.reference')
{
eval("unset(\$tree{$index}[{$k}]);"); // unset all elements = empty array
}
_subqueue_filter_reference($current,$tree,$index."[$k]");
}
}
}
return $tree;
}
I hate having to use eval as it SCREAMS of a giant, gaping security hole, but it's pretty secure and the values being called in eval are generated explicitly by Drupal core and Views. I'm okay with using it for now.
Anyway, when I return the tree I simply replace the old array with the newly returned tree array. Works like a charm.

How can I validate the structure of my PHP arrays?

Is there a function out there to make sure that any given array conforms to a particular structure? What I mean is that is has particular key names, perhaps particular types for values, and whatever nested structure.
Right now I have a place where I want to make sure that the array getting past has certain keys, a couple holding a certain data type, and one sub-array with particular key names. I've done a lot of run-around because I was passing malformed arrays to it, and finally I'm at the point where I have a bunch of
if ( ! isset($arr['key1']) ) { .... }
if ( ! isset($arr['key2']) ) { .... }
if ( ! isset($arr['key3']) ) { .... }
I would have saved a lot of time and consternation if I could have checked that the array conformed to a particular structure beforehand. Ideally something like
$arrModel = array(
'key1' => NULL ,
'key2' => int ,
'key3' => array(
'key1' => NULL ,
'key2' => NULL ,
),
);
if ( ! validate_array( $arrModel, $arrCandidate ) ) { ... }
So, the question I'm asking is, does this already exists, or do I write this myself?
Convert array to JSON:
http://us.php.net/manual/en/function.json-encode.php
Then check against a JSON Schema:
http://json-schema.org/
http://jsonschemaphpv.sourceforge.net/
It doesn't exist built in.
Maybe try something like (untested):
array_diff(array_merge_recursive($arrCandidate, $arrModel), $arrModel)
You can use the Symfony Validation component
Installation:
composer require symfony/validator doctrine/annotations
Usage:
$myArray = [
'name' => ['first_name' => 'foo', 'last_name' => 'bar'],
'email' => 'foo#email.com'
];
$constraints = new Assert\Collection([
'name' => new Assert\Collection([
'first_name' => new Assert\Length(['min' => 101]),
'last_name' => new Assert\Length(['min' => 1]),
]),
'email' => new Assert\Email(),
]);
$validator = Validation::createValidator();
$violations = $validator->validate($myArray, $constraints);
For more details, see How to Validate Raw Values (Scalar Values and Arrays)
accepted answer make diff based on values, since it's about array structure you dont want to diff values. Insted you should use [array_diff_key()][1]
Function alone is not recursive. It will not work out of box on sample array from question.
Edit after years: Got into similar trouble whit recursive array functions, so there is mine array_diff_recursive, this does not solve original question because it compare values as well, but i believe it can be easily modified and someone can find it useful .
function array_diff_recursive(array $array1, array $array2){
return array_replace_recursive(
array_diff_recursive_one_way($array1, $array2),
array_diff_recursive_one_way($array2, $array1)
);
}//end array_diff_recursive()
function array_diff_recursive_one_way(array $array1, array $array2)
{
$ret = array();
foreach ($array1 as $key => $value) {
if (array_key_exists($key, $array2) === TRUE) {
if (is_array($value) === TRUE) {
$recurse = array_diff_recursive_one_way($value, $array2[$key]);
if (count($recurse) > 0) {
$ret[$key] = $recurse;
}
} elseif (in_array($value, $array2) === FALSE) {
$ret[$key] = $value;
}
} elseif (in_array($value, $array2) === FALSE) {
$ret[$key] = $value;
}
}
return $ret;
}//end array_diff_recursive_one_way()```
[1]: http://php.net/manual/en/function.array-diff-key.php
I know this is sort of an old post, sorry if my answer is not approppriate.
I'm in the process of writing a php package that does exactly what you are asking for, it's called Structure.
What you can do with the package is something like:
$arrayCheck = new \Structure\ArrayS();
$arrayCheck->setFormat(array("profile"=>"array"));
if ($arrayCheck->check($myArray)) {
//...
}
You can check it out here: http://github.com/3nr1c/structure
I came across a tool called Matchmaker on GitHub, which looks very comprehensive and has composer support and unit tests:
https://github.com/ptrofimov/matchmaker
You can include it into your project with
composer require ptrofimov/matchmaker.

Categories