I have a function that returns an array with empty keys from the input array. The problem is that I've been working on an inconsistent data. The data could go to any level of nested array. For example,
$inputArray = [
'a' => 'value a',
'b' => [
1 => [],
2 => 'value b2',
3 => [
'x' => 'value x'
'y' => ''
],
'c' => ''
],
];
I need an output that converts this kind of data to string. So,
$outputArray = [
'empty' => [
'b[1]',
'b[3][y]',
'c'
]
];
Here's what I have so far to get keys with empty values:
$outputArray = [];
foreach ($inputArray as $key => $value) {
if (is_array($value)) {
foreach ($value as $index => $field) {
if (is_array($field)) {
foreach ($field as $index1 => $value1) {
if (empty($value1)) {
array_push($outputArray['empty'], $key . '[' . $index . ']' . '[' . $index1 . ']');
}
}
}
if (empty($field)) {
array_push($outputArray['empty'], $key . '[' . $index . ']');
}
}
}
if (empty($value)) {
array_push($outputArray['empty'], $key);
}
}
return $outputArray;
As I said the input array could be nested to any level. I cannot keep on adding if (is_array) block every time the array is nested one more level. I believe it could be solved using a recursive function, but I cannot seem to figure out how. Please help me with this. Thanks.
You are right about a recursive function but you should also be aware of recursions, whether we are in or out of a recursion. The tricky part is passing current level keys to the recursive function:
function findEmpties($input, $currentLevel = null) {
static $empties = [];
foreach ($input as $key => $value) {
$levelItem = $currentLevel ? "{$currentLevel}[{$key}]" : $key;
if (empty($value)) {
$empties['empty'][] = $levelItem;
} else {
if (is_array($value)) {
findEmpties($value, $levelItem);
}
}
}
return $empties;
}
Live demo
Related
I have a multidimensional array like this:
array (
level1 => array ( level1.1,
level1.2)
level2 => array ( level2.1,
level2.2 => array( level2.2.1 => 'foo',
level2.2.2 => 'bar',
level2.2.3 => 'test')
)
)
As a result I want an array of strings like this
array ("level1/level1.1",
"level1/level1.2",
"level2/level2.1",
"level2/level2.2/level2.2.1",
"level2/level2.2/level2.2.2",
"level2/level2.2/level2.2.3")
Here is the code I tried
function displayArrayRecursively($array, string $path) : array {
if($path == "")
$result_array = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$this->displayArrayRecursively($value, $path . $key . '/');
} else {
$result_array[] = $path . $key; }
}
return $result_array;
}
Any idea how I can achieve this. I could use a reference array to populate, but I want to solve it with return values.
$array = [
'level1' => [
'level1.1',
'level1.2'
],
'level2' => [
'level2.1',
'level2.2' => [
'level2.2.1' => 'foo',
'level2.2.2' => 'bar',
'level2.2.3' => 'test'
]
]
];
function arrayParser(array $array, ?string $path=null) {
$res = [];
foreach($array as $key => $value) {
if(is_array($value)) {
$res[] = arrayParser($value, ($path ? $path.'/' : $path).$key);
}
else {
$res[] = $path.'/'.(!is_numeric($key) ? $key : $value);
}
}
return flatten($res);
}
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
$res = arrayParser($array); // result
I have a json data. Now I want to reform it. In my json data there is a property like person_on_zone I want to make a property named person_info and store them which person under this area.
Here is my data any code. Thanks in advance
My json data
$val = [
{
'city':'xx',
'zone':'yy',
'person_on_zone':'p1'
},
{
'city':'xx',
'zone':'yy',
'person_on_zone':'p2'
},
{
'city':'xx',
'zone':'yy',
'person_on_zone':'p3'
},
{
'city':'xx',
'zone':'ww',
'person_on_zone':'p1'
},
]
My expectation is
[
{
'city':'xx',
'zone':'yy',
'person_info':{
'person_on_zone':'p1',
'person_on_zone':'p2',
'person_on_zone':'p3',
}
},
{
'city':'xx',
'zone':'ww',
'person_info':{
'person_on_zone':'p1'
}
},
]
Here I tried
foreach ($val as $v) {
$new_array['city'] = $v['city'];
$new_array['zone'] = $v['zone'];
foreach ($val as $v2) {
$new_array['person_info'] = $v['person_on_zone'];
}
}
json_encode($new_array);
Try this, use $map to store key city-zone, then transform to array to get the result
<?php
$val = [
[
'city' => 'xx',
'zone' => 'yy',
'person_on_zone' => 'p1'
],
[
'city' => 'xx',
'zone' => 'yy',
'person_on_zone' => 'p2'
],
[
'city' => 'xx',
'zone' => 'yy',
'person_on_zone' => 'p3'
],
[
'city' => 'xx',
'zone' => 'ww',
'person_on_zone' => 'p1'
]
];
$map = [];
foreach ($val as $v) {
$key = $v['city'] . $v['zone'];
if (!isset($map[$key])) {
$map[$key] = [
'city' => $v['city'],
'zone' => $v['zone'],
'person_info' => []
];
}
$map[$key]['person_info'][] = $v['person_on_zone'];
}
print_r(array_values($map));
Correct Code
foreach ($val as $v){
$new_array['city'] = $v['city'];
$new_array['zone'] = $v['zone'];
foreach ($val as $v2){
$new_array['person_info'] = $v2['person_on_zone'];
}
}
json_encode($new_array);
try this:
first decode json array then use foreach loop with key
$val = json_decode($val);
foreach ($val as $v){
$new_array['city'] = $v->city;
$new_array['zone'] = $v->zone;
foreach ($val as $key=>$v2){
$new_array[$key]['person_info'] = $v->person_on_zone;
}
}
print_r($new_array);
I think you make a mistake around composing the json. I tried with your code and i found following is the correct way to do.. Remember single quote(') is not valid in json string that is being used to define key value pair in php
// I think your code has json string and i convert it into array of objects(stdClass)
// and if your code has array then keep you code intact but need to change the notation of
// objects to associative array.
// first thing first decode json string
$values = json_decode('[
{ "city":"xx",
"zone":"yy",
"person_on_zone":"p1"
},
{
"city":"xx",
"zone":"yy",
"person_on_zone":"p2"
},
{
"city":"xx",
"zone":"yy",
"person_on_zone":"p3"
},
{
"city":"xx",
"zone":"ww",
"person_on_zone":"p1"
}]');
// Now declare new values as array
$new_values = [];
// walk through each object
foreach ($values as $v){
// $v becomes an object of stdClass here
// $data is a temporary array
$data['city'] = $v->city;
$data['zone'] = $v->zone;
$data['person_info'] = [];
foreach ($values as $v2){
if(!in_array($data['person_info'],['person_on_zone' => $v2->person_on_zone]))
{
// compare if zones are same or not
if($v->zone == $v2->zone)
{
// push to the array instead of assiging
array_push($data['person_info'], ['person_on_zone' => $v2->person_on_zone]);
}
}
}
// make array unique
$data['person_info'] = array_map("unserialize", array_unique(array_map("serialize", $data['person_info'])));
array_push($new_values, $data);
}
// make array unique in mutidimensional array
$new_values = array_map("unserialize", array_unique(array_map("serialize", $new_values)));
echo '<pre>';
print_r($new_values);
echo '</pre>';
One simple foreach would do,
$result = [];
foreach ($arr as $key => $value) {
//created unique combination of city and zone as key
$result[$value['city'] . $value['zone']]['city'] = $value['city'];
$result[$value['city'] . $value['zone']]['zone'] = $value['zone'];
$result[$value['city'] . $value['zone']]['person_info'][] = ['person_on_zone' => $value['person_on_zone']];
}
echo json_encode(array_values($result));die;
array_values — Return all the values of an array
Working demo.
I have an array that looks like the following:
[
'applicant' => [
'user' => [
'username' => true,
'password' => true,
'data' => [
'value' => true,
'anotherValue' => true
]
]
]
]
What I want to be able to do is convert that array into an array that looks like:
[
'applicant.user.username',
'applicant.user.password',
'applicant.user.data.value',
'applicant.user.data.anotherValue'
]
Basically, I need to somehow loop through the nested array and every time a leaf node is reached, save the entire path to that node as a dot separated string.
Only keys with true as a value are leaf nodes, every other node will always be an array. How would I go about accomplishing this?
edit
This is what I have tried so far, but doesnt give the intended results:
$tree = $this->getTree(); // Returns the above nested array
$crumbs = [];
$recurse = function ($tree, &$currentTree = []) use (&$recurse, &$crumbs)
{
foreach ($tree as $branch => $value)
{
if (is_array($value))
{
$currentTree[] = $branch;
$recurse($value, $currentTree);
}
else
{
$crumbs[] = implode('.', $currentTree);
}
}
};
$recurse($tree);
This function does what you want:
function flattenArray($arr) {
$output = [];
foreach ($arr as $key => $value) {
if (is_array($value)) {
foreach(flattenArray($value) as $flattenKey => $flattenValue) {
$output["${key}.${flattenKey}"] = $flattenValue;
}
} else {
$output[$key] = $value;
}
}
return $output;
}
You can see it running here.
I have an array, $arr, which looks like this:
'sdb5' => [
'filters' => [
(int) 11 => [
'find' => [
(int) 0 => (int) 569
],
'exclude' => [
(int) 0 => (int) 89,
(int) 1 => (int) 573
]
],
(int) 86 => [
'find' => [
(int) 0 => (int) 49,
(int) 1 => (int) 522,
(int) 2 => (int) 803
],
'exclude' => [
(int) 0 => (int) 530,
(int) 1 => (int) 802,
(int) 2 => (int) 511
]
]
]
],
I've read Delete element from multidimensional-array based on value but am struggling to understand how to delete a value in an efficient way.
For example, let's say I want to delete the value 522. I'm doing it like this:
$remove = 522; // value to remove
foreach ($arr as $filters) {
foreach ($filters as $filter) {
foreach ($filter as $single_filter) {
foreach ($single_filter as $key => $value) {
if ($value == $remove) {
unset($key);
}
}
}
}
}
I couldn't work out from the above link how to do this because even though it's a multidimensional array, it doesn't have any sub-arrays like mine.
I also don't know how else to rewrite this without repeating foreach to get to the elements of the array I want. Again, I've read Avoid multiple foreach loops but cannot apply this to my array.
I am using PHP 7.x.
foreach() made copy of elements. Then unseting the key is not enough, because you are destroying a local variable.
You could use references & in your foreach() loops and unset like :
foreach ($arr as &$filters) {
foreach ($filters as &$filter) {
foreach ($filter as &$single_filter) {
foreach ($single_filter as $key => $value) {
if ($value == $remove) {
unset($single_filter[$key]);
}
}
}
}
}
Or using keys ($k1, $k2, ...) :
foreach ($arr as $k1 => $filters) {
foreach ($filters as $k2 => $filter) {
foreach ($filter as $k3 => $single_filter) {
foreach ($single_filter as $key => $value) {
if ($value == $remove) {
unset($arr[$k1][$k2][$k3][$key]);
}
}
}
}
}
You could also write a recursive function, so you don't have to use nested foreach:
function deleteRecursive(&$array, &$value) {
foreach($array as $key => &$subArray) {
if(is_array($subArray)) {
deleteRecursive($subArray, $value);
} elseif($subArray == $value) {
unset($array[$key]);
}
}
}
$valueToDelete = 522;
deleteRecursive($array, $valueToDelete);
function recursiveRemoval(&$array, $val)
{
if(is_array($array))
{
foreach($array as $key=>&$arrayElement)
{
if(is_array($arrayElement))
{
recursiveRemoval($arrayElement, $val);
}
else
{
if($arrayElement == $val)
{
unset($array[$key]);
}
}
}
}
}
Call function
recursiveRemoval($array, $value);
I have an array that looks like the following:
[
'applicant' => [
'user' => [
'username' => true,
'password' => true,
'data' => [
'value' => true,
'anotherValue' => true
]
]
]
]
What I want to be able to do is convert that array into an array that looks like:
[
'applicant.user.username',
'applicant.user.password',
'applicant.user.data.value',
'applicant.user.data.anotherValue'
]
Basically, I need to somehow loop through the nested array and every time a leaf node is reached, save the entire path to that node as a dot separated string.
Only keys with true as a value are leaf nodes, every other node will always be an array. How would I go about accomplishing this?
edit
This is what I have tried so far, but doesnt give the intended results:
$tree = $this->getTree(); // Returns the above nested array
$crumbs = [];
$recurse = function ($tree, &$currentTree = []) use (&$recurse, &$crumbs)
{
foreach ($tree as $branch => $value)
{
if (is_array($value))
{
$currentTree[] = $branch;
$recurse($value, $currentTree);
}
else
{
$crumbs[] = implode('.', $currentTree);
}
}
};
$recurse($tree);
This function does what you want:
function flattenArray($arr) {
$output = [];
foreach ($arr as $key => $value) {
if (is_array($value)) {
foreach(flattenArray($value) as $flattenKey => $flattenValue) {
$output["${key}.${flattenKey}"] = $flattenValue;
}
} else {
$output[$key] = $value;
}
}
return $output;
}
You can see it running here.