PHP Recursively unset array keys if match - php

I have the following array that I need to recursively loop through and remove any child arrays that have the key 'fields'. I have tried array filter but I am having trouble getting any of it to work.
$myarray = array(
'Item' => array(
'fields' => array('id', 'name'),
'Part' => array(
'fields' => array('part_number', 'part_name')
)
),
'Owner' => array(
'fields' => array('id', 'name', 'active'),
'Company' => array(
'fields' => array('id', 'name',),
'Locations' => array(
'fields' => array('id', 'name', 'address', 'zip'),
'State' => array(
'fields' => array('id', 'name')
)
)
)
)
);
This is how I need it the result to look like:
$myarray = array(
'Item' => array(
'Part' => array(
)
),
'Owner' => array(
'Company' => array(
'Locations' => array(
'State' => array(
)
)
)
)
);

If you want to operate recursively, you need to pass the array as a reference, otherwise you do a lot of unnecessarily copying:
function recursive_unset(&$array, $unwanted_key) {
unset($array[$unwanted_key]);
foreach ($array as &$value) {
if (is_array($value)) {
recursive_unset($value, $unwanted_key);
}
}
}

you want array_walk
function remove_key(&$a) {
if(is_array($a)) {
unset($a['fields']);
array_walk($a, __FUNCTION__);
}
}
remove_key($myarray);

My suggestion:
function removeKey(&$array, $key)
{
if (is_array($array))
{
if (isset($array[$key]))
{
unset($array[$key]);
}
if (count($array) > 0)
{
foreach ($array as $k => $arr)
{
removeKey($array[$k], $key);
}
}
}
}
removeKey($myarray, 'Part');

function recursive_unset(&$array, $unwanted_key) {
if (!is_array($array) || empty($unwanted_key))
return false;
unset($array[$unwanted_key]);
foreach ($array as &$value) {
if (is_array($value)) {
recursive_unset($value, $unwanted_key);
}
}
}

function sanitize($arr) {
if (is_array($arr)) {
$out = array();
foreach ($arr as $key => $val) {
if ($key != 'fields') {
$out[$key] = sanitize($val);
}
}
} else {
return $arr;
}
return $out;
}
$myarray = sanitize($myarray);
Result:
array (
'Item' =>
array (
'Part' =>
array (
),
),
'Owner' =>
array (
'Company' =>
array (
'Locations' =>
array (
'State' =>
array (
),
),
),
),
)

function removeRecursive($haystack,$needle){
if(is_array($haystack)) {
unset($haystack[$needle]);
foreach ($haystack as $k=>$value) {
$haystack[$k] = removeRecursive($value,$needle);
}
}
return $haystack;
}
$new = removeRecursive($old,'key');

Code:
$sweet = array('a' => 'apple', 'b' => 'banana');
$fruits = array('sweet' => $sweet, 'sour' => $sweet);
function recursive_array_except(&$array, $except)
{
foreach($array as $key => $value){
if(in_array($key, $except, true)){
unset($array[$key]);
}else{
if(is_array($value)){
recursive_array_except($array[$key], $except);
}
}
}
return;
}
recursive_array_except($fruits, array('a'));
print_r($fruits);
Input:
Array
(
[sweet] => Array
(
[a] => apple
[b] => banana
)
[sour] => Array
(
[a] => apple
[b] => banana
)
)
Output:
Array
(
[sweet] => Array
(
[b] => banana
)
[sour] => Array
(
[b] => banana
)
)

I come up with a simple function that you can use to delete multiple array element based on multiple keys.
Detail example here.
Just a little change in code.
function removeRecursive($inputArray,$delKey){
if(is_array($inputArray)){
$moreKey = explode(",",$delKey);
foreach($moreKey as $nKey){
unset($inputArray[$nKey]);
foreach($inputArray as $k=>$value) {
$inputArray[$k] = removeRecursive($value,$nKey);
}
}
}
return $inputArray;
}
$inputNew = removeRecursive($input,'keyOne,keyTwo');
print"<pre>";
print_r($inputNew);
print"</pre>";

Give this function a shot. It will remove the keys with 'fields' and leave the rest of the array.
function unsetFields($myarray) {
if (isset($myarray['fields']))
unset($myarray['fields']);
foreach ($myarray as $key => $value)
$myarray[$key] = unsetFields($value);
return $myarray;
}

Recursively walk the array (by reference) and unset the relevant keys.
clear_fields($myarray);
print_r($myarray);
function clear_fields(&$parent) {
unset($parent['fields']);
foreach ($parent as $k => &$v) {
if (is_array($v)) {
clear_fields($v);
}
}
}

I needed to have a little more granularity in unsetting arrays and I came up with this - with the evil eval and other dirty tricks.
$post = array(); //some huge array
function array_unset(&$arr,$path){
$str = 'unset($arr[\''.implode('\'][\'',explode('/', $path)).'\']);';
eval($str);
}
$junk = array();
$junk[] = 'property_meta/_edit_lock';
$junk[] = 'property_terms/post_tag';
$junk[] = 'property_terms/property-type/0/term_id';
foreach($junk as $path){
array_unset($post,$path);
}
// unset($arr['property_meta']['_edit_lock']);
// unset($arr['property_terms']['post_tag']);
// unset($arr['property_terms']['property-type']['0']['term_id']);

Related

Search values on multidimensional array then display the result

I'm trying to retrieve the values on multidimensional arrays using a search like function.
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
array(
'Name'=>'Dune',
'Year'=>'1984',
'Rating'=>'6'
),
array(
'Name'=>'Superbabies: Baby Geniuses 2',
'Year'=>'2004',
'Rating'=>'1'
)
);
So for example, if you want to search the array with a value of Name with 'City of God' and Year with '1963' the expected output should be like this
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
);
Currently, the function I am using is this
function multiSearch(array $array, array $pairs)
{
$found = array();
foreach ($array as $aKey => $aVal) {
$coincidences = 0;
foreach ($pairs as $pKey => $pVal) {
if (array_key_exists($pKey, $aVal) && $aVal[$pKey] == $pVal) {
$coincidences++;
}
}
if ($coincidences == count($pairs)) {
$found[$aKey] = $aVal;
}
}
return $found;
}
However, using this function only capable of retrieving data of the same array key. So for example if I search the value of Name of 'City of God'
$x = multiSearch($rows, array('Name' => 'City of God')
This will display the correct output like this
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
);
Unfortunately, if you try to use, 'Name' => 'City of God' and 'Year' => '1963'
It will not display any output. I'm stuck on figuring out on displaying the correct output, any ideas would be appreciated
Try this :
$datas = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
array(
'Name'=>'Dune',
'Year'=>'1984',
'Rating'=>'6'
),
array(
'Name'=>'Superbabies: Baby Geniuses 2',
'Year'=>'2004',
'Rating'=>'1'
)
);
$search = array(
'Name' => 'Dune',
'Year' => '2004'
);
$output = array();
foreach ($datas as $key1 => $row) {
foreach ($row as $key2 => $value) {
if($search[$key2] == $value) {
// if(stristr($value, $search[$key2]) !== FALSE) { // if you want to search
$output[] = $datas[$key1];
break;
}
}
}
echo "<pre>"; print_r($output); exit;
Output:
Array
(
[0] => Array
(
[Name] => Dune
[Year] => 1984
[Rating] => 6
)
[1] => Array
(
[Name] => Superbabies: Baby Geniuses 2
[Year] => 2004
[Rating] => 1
)
)
If you need OR, as you mentioned in your question, you then need to see if at least one coincidence happened:
if ($coincidences > 0) {
$found[$aKey] = $aVal;
}
This way, both entries:
$rows = array(
array(
'Name'=>'City of God',
'Year'=>'2002',
'Rating'=>'10'
),
array(
'Name'=>'The Great Escape',
'Year'=>'1963',
'Rating'=>'9'
),
);
will be matched.
Modify the search function to this - works for both the cases mentioned:
function multiSearch($rows, $value) {
$newArr = array();
foreach ($rows as $row) {
foreach ($value as $key => $val) {
if (isset($row[$key]) && $row[$key] == $val) {
$newArr[] = $row;
}
}
}
return $newArr;
}
Calling with one or two values produces the required output -
$x = multiSearch($rows, array('Name' => 'City of God'));
$x = multiSearch($rows, array('Name' => 'City of God', 'Year' => '1963'));
//returns $rows containing this pair
Take a look on my suggestion
function search ($storage, $criteria)
{
return array_filter(
$storage,
function ($row) use ($criteria) {
foreach ($criteria as $key => $value) {
if ($row[$key] === $value) {
return true;
}
}
}
);
}
$result = search($datas, $search);
/**
* $result = [
* 2 => [
* 'Name' => 'Dune'
* 'Year' => '1984'
* 'Rating' => '6'
* ],
* 3 => [
* 'Name' => 'Superbabies: Baby Geniuses 2'
* 'Year' => '2004'
* 'Rating' => '1'
* ]
* ];
*/
what about a recursive call?
function recursiveInArray ($needle, $array) {
$status = false;
foreach($array as $value) {
if(is_array($value)) {
recursiveInArray($needle, $value);
} else {
$status = ($needle == $value);
}
}
return $status;
}
i know its abstract, but should help you working with multi dimensional searching

PHP - replace array keys recursively and change values to array including old key

I've spent a while trying to get what I need from old answers but haven't quite got it (have got close though!).
I have this;
[January] => Array
(
[Tuesday] => Array
(
[foo] => Array
(
[82] => 47731
[125] => 19894
)
[bar] => Array
(
[82] => 29911
[125] => 10686
)
)
}
...and I want this;
[0] => Array
(
'key' => 'January'
'children' => Array
[0] => Array
{
'key' => 'Tuesday'
'children' => Array
[0] => Array
{
'key' => 'foo'
'values' => Array
{
[82] => 47731
[125] => 19894
}
[1] => Array
{
'key' => 'bar'
'values' => Array
{
[82] => 29911
[125] => 10686
}
}
)
}
I've got fairly close by adapting the first answer from Recursively change keys in array but only the bottom layer of my result is correct - the nodes with keys 'Tuesday', 'foo' and 'bar' just look the same as in the source array.
Here's what I've got so far;
public function transform_hierarchical_output(&$var)
{
if (is_array($var))
{
$final = [];
$i = 0;
foreach ($var as $k => &$v)
{
$new_node = [
'key' => $k,
'children' => $v
];
$k = $i;
$this->transform_hierarchical_output($v);
$final[$k] = $new_node;
$i++;
}
$var = $final;
}
elseif (is_string($var))
{
}
}
This needs to work with a source array of any length and depth.
Thanks in advance.
Geoff
<?php
$array = [
'January' => [
'Tuesday' => [
'foo' => [
82 => 47731,
125 => 19894,
],
'bar' => [
82 => 47731,
125 => 19894,
]
]
]
];
function transform(array $input)
{
$output = [];
foreach ($input as $key => $val) {
if (is_array(array_values($val)[0])) { // if next depth is an array
$output[] = [
'key' => $key,
'children' => transform($val)
];
} else {
$output[] = [
'key' => $key,
'values' => $val
];
}
}
return $output;
}
print_r(transform($array));
try below:
function t($arr)
{
$a = [];
$num = 0;
foreach($arr as $k => $v) {
if (is_array($v))
{
$a[$num] = [
'key' => $k,
];
$a[$num][is_array(array_values($v)[0]) ? 'children' : 'values'] = t($v);
$num ++;
} else {
$a[$k] = $v;
}
}
return $a;
}
First thing I should note is that, though there are many recursions in your example, but technically it is not a recursive loop in terms of the need for a self-executing function. Because your loop's scheme fluctuates a bit in its depth, it does not follow a regular pattern down to is last node; however it is a logical scheme, but is not repeated down to is last node.
The following function might work for you:
function doArray($array)
{
$keys = array_keys($array);
$arr_count = count($keys);
$new_array;
for($i = 0; $i < $arr_count; $i++)
{
$new_array[$i]["key"] = $keys[$i];
$new_keys = array_keys($array[$keys[$i]]);
for($w = 0; $w < count($new_keys); $w++)
{
$new_array[$i]["children"][$w]["keys"] = $new_keys[$i];
$new_array[$i]["children"][$w]["children"] = array();
for($w = 0; $w < count($new_keys); $w++)
{
$new_new_keys = array_keys($array[$keys[$i]][$new_keys[$w]]);
for($q = 0; $q < count($new_new_keys); $q++)
{
$new_array[$i]["children"][$w]["children"][$q]["key"] = $new_new_keys[$q];
//$new_array[$i]["children"][$w]["children"][$q]["children"] = $array[$keys[$i]][$new_keys[$w]][$new_new_keys[$q]];
$last_new_keys = array_keys($array[$keys[$i]][$new_keys[$w]][$new_new_keys[$q]]);
for($s = 0; $s < count($last_new_keys); $s++)
{
$new_array[$i]["children"][$w]["children"][$q]["values"][$last_new_keys[$s]] = $array[$keys[$i]][$new_keys[$w]][$new_new_keys[$q]][$last_new_keys[$s]];
}
}
}
}
}
return $new_array;
}

Retrieve all parent keys of a given child key in Array

I've been breaking my head over this one but can't seem to find a solution. I need a function that retrieves all parent keys of a given child key. So for example if I have an array like this:
array(
'apples' => array(
'bananas' => array(
'strawberries' => array(
'fruit' => array()
)
)
)
)
I would call the function like 'key_get_parents($key, $array)', and it would return an array with all the parent keys. In this example that would be array('apples', 'bananas', 'strawberries').
$array = array(
'apples' => array(
'bananas' => array(
'strawberries' => array(
'fruit' => array()
)
)
)
);
function key_get_parents($subject, $array)
{
foreach ($array as $key => $value)
{
if (is_array($value))
{
if (in_array($subject, array_keys($value)))
return array($key);
else
{
$chain = key_get_parents($subject, $value);
if (!is_null($chain))
return array_merge(array($key), $chain);
}
}
}
return null;
}
// Prints "Array ( [0] => apples [1] => bananas )"
print_r(key_get_parents('strawberries', $array));

How do I transform this array into a multi-dimensional array via recursion?

So my example inputs are
$example_1 = Array (
0 => Array (
'category' => 'body',
'sub-category' => 'intro',
'id' => 'header',
'copy' => 'Hello',
),
1 => Array (
'category' => 'body',
'sub-category' => 'intro',
'id' => 'footer',
'copy' => 'Bye',
),
);
$example_2 = Array (
0 => Array (
'category' => 'body',
'sub-category' => 'intro',
'sub-sub-category' => 'header',
'sub-sub-child-category' => 'left',
'id' => 'title',
'copy' => 'Hello',
),
1 => Array (
'category' => 'body',
'sub-category' => 'intro',
'sub-sub-category' => 'footer',
'sub-sub-child-category' => 'right',
'id' => 'title',
'copy' => 'Bye',
),
);
I want to transform it into
$example_output_1 = Array (
'body' => Array (
'intro' => Array (
'header' => Array (
'title' => 'Hello',
),
'footer' => Array (
'title' => 'Bye',
),
),
),
);
$example_output_2 = Array (
'body' => Array (
'intro' => Array (
'header' => Array (
'left' => Array (
'title' => 'Hello',
),
),
'footer' => Array (
'right' => Array (
'title' => 'Bye',
)
),
),
),
);
Note the depth of the array is dynamic (it is not set - only by the time it hits 'copy' does it indicate the depth of the array).
I am having problems trying to get the recursion correctly. The basic but very rough algorithm I had was to
- Loop through the Row
- Loop through the contents of the Row
- When the index is "copy" then the final value is current value.
- Then build the array
I managed to get it to process for ONLY one row of the array but it was very messy and kinda patchy, so I got a feeling I really need to start from scratch.
Updated: Attached embarrassing Code as requested (don't scream! ;p)
function buildArray($row, $start = true) {
if ($start) {
$result = array();
}
if ( ! is_array($row) ) {
return $row;
}
// Get the first element of the array include its index
$cellValue = null;
$colId = null;
foreach($row AS $index => $value) {
$cellValue = $value;
$colId = $index;
break;
}
// Reduce the array by one
$tempRow = $row;
$temp = array_shift($tempRow);
if ($colId == 'copy') {
$result[$cell] = buildArray($cellValue, $locale, false);
} else {
$result[$cell] = buildArray($tempRow, $locale, false);
}
return $result;
}
Any help will be greatly appreciated.
Should be pretty straightforward:
$originalArray = array(); // <-- should contain your values
$newArray = array();
foreach( $originalArray as $item )
{
$category = $item['category'];
$subcategory = $item['sub-category'];
if ( empty( $newArray[$category] ) )
{
$newArray[$category] = array();
}
if ( empty( $newArray[$category][$subcategory] ) )
{
$newArray[$category][$subcategory] = array();
}
$newArray[$category][$subcategory][$item['id']] = $item['copy'];
}
See it here in action: http://codepad.viper-7.com/9bDiLP
Update: Now that you've specified that you need unlimited recursion, here's a shot at that:
$originalArray = array(); // <-- Your values go here
$newArray = array();
foreach ( $originalArray as $item )
{
$inception = &$newArray; // http://www.imdb.com/title/tt1375666/
foreach ( $item as $key => $val )
{
if ( $key != 'id' )
{
if ( empty($inception[$val]) )
{
$inception[$val] = array();
}
$inception = &$inception[$val];
}
else
{
$inception[ $val ] = $item['copy'];
break;
}
}
}
...and here's the demo: http://codepad.viper-7.com/F9hY7h
This can be solved iteratively, because the recursion would only happen at the tail end of your function. The following code is the simplification. It builds a new layered array while it iterates over the old.
After transforming each each entry it gets merged using array_merge_recursive.
function transform($a)
{
// create new array and keep a reference to it
$b = array(); $cur = &$b;
foreach ($a as $key => $value) {
if ('id' === $key) {
// we're done, add value to the array built so far using id and copy
$cur[$value] = $a['copy'];
break;
} else {
// create one more level
$cur[$value] = array();
// and update the reference
$cur = &$cur[$value];
}
}
// all done
return $b;
}
// $example_2 is your multi-dimensional array
$merged = call_user_func_array('array_merge_recursive',
array_map('transform', $example_2)
);

recursive function php

I have an array which looks like this
$dataArray = array (
0 =>
array (
'UserId' => '804023',
'ProjectCode' => 'RA1234',
'Role' => 'PI',
),
1 =>
array (
'UserId' => '804023',
'ProjectCode' => 'RA1234',
'Role' => 'PM',
),
2 =>
array (
'UserId' => '804023',
'ProjectCode' => 'A90123',
'Role' => 'CI',
),
3 =>
array (
'UserId' => '804023',
'ProjectCode' => 'A20022',
'Role' => 'PM',
),
)
I need it to look like this
$expected = array (
804023 =>
array (
'RA1234' =>
array (
0 => 'PI',
1 => 'PM',
),
'A90123' =>
array (
0 => 'PI',
),
'A20022' =>
array (
0 => 'CI',
),
),
)
I think this could be achieved generically using recursion as this is a scenario I am likely to come across many times
I have got this far passing in an array of keys that form the nested array keys i.e.
$keys=array("UserId","projectCode","Role");
but am just not seeing where to go from here any pointers?
public function structureData(array $data, array $keys)
{
//$structuredData = array();
foreach ($data as $key => $value)
{
$keyForData = array_slice($keys,0,1);
$remainingKeys = $keys;
array_shift($remainingKeys);
if (!array_key_exists($value[$keyForData[0]], $structuredData))
{
$count=count($remainingKeys);
$structuredData[$value[$keyForData[0]]] =array();
// this returns as expected array(804023 =>array ()); but subsequent recursive calls with the remaining data fail
}
}
return $structuredData);
}
You don't need recursion, just a loop:
foreach ($dataArray as $da) {
$expected[$da['UserId']][$da['ProjectCode']][] = $da['Role'];
}
var_export($expected);
/* output:
array (
804023 =>
array (
'RA1234' =>
array (
0 => 'PI',
1 => 'PM',
),
'A90123' =>
array (
0 => 'CI',
),
'A20022' =>
array (
0 => 'PM',
),
),
)
*/
A crude but functioning solution.
function structureData($data, $keys){
$out = array();
foreach($data as $row){
$subout = &$out;
foreach(array_slice($keys, 0, -1) as $key){
$value = $row[$key];
$subout = &$subout[$value];
}
$subout[] = $row[$keys[count($keys) - 1]];
}
return $out;
}
print_r(structureData($dataArray, array('UserId', 'ProjectCode', 'Role')));
Recursion? Nah. Try this:
function add_role($dataArray, $userid, $project_code, $role)
{
$dataArray[$userid][$project_code][] = $role;
}
Functional solution:
$t = array_gather_key($dataArray, function ($e) { return $e['UserId']; } );
$t = array_map(
function ($e) {
return array_gather_key($e,
function ($e) { return $e['ProjectCode']; },
function ($e) { return $e['Role']; } );
},
$t
);
With this higher-order function:
function array_gather_key($array, $func, $transf = null) {
$res = array();
foreach ($array as $elem) {
$key = $func($elem);
if (!array_key_exists($key, $res))
$res[$key] = array();
if ($transf === null)
$res[$key][] = $elem;
else
$res[$key][] = $transf($elem);
}
return $res;
}
This gives:
array(1) {
[804023]=>
array(3) {
["RA1234"]=>
array(2) {
[0]=>
string(2) "PI"
[1]=>
string(2) "PM"
}
["A90123"]=>
array(1) {
[0]=>
string(2) "CI"
}
["A20022"]=>
array(1) {
[0]=>
string(2) "PM"
}
}
}

Categories