$array = ["farm"=>
[
"horse"=>
[
"rabbit"=>
[
"fred1"=> "fred1",
"fred2"=> "fred2",
"fred3"=> "fred3",
"fred4"=> "fred4"
],
"raccoon"=>
["frida"=> "frida"]
]
]
];
I want to create an array from my for each loop:
$keySearch = "o";
function createList($array, $keySearch, $path) {
foreach ($array as $key => $item) {
$basePath = $path === null ? $key : $path. "/" . $key;
if(is_array($item)){
if (stripos($key, $keySearch) !== false){
$a['key'] = $key ;
$b['basePath'] = $basePath;
$result[] = array_merge_recursive ($a, $b);
}
createList($item, $keySearch, $basePath);
}
}
print_r($result);
}
createList($array, $keySearch, '');
My result is:
Array
(
[0] => Array
(
[key] => horse
[basePath] => farm/horse
)
)
Array
(
[0] => Array
(
[key] => raccoon
[basePath] => farm/horse/raccoon
)
)
What I actually expect is:
Array
(
[0] => Array
(
[key] => horse
[basePath] => farm/horse
)
[1] => Array
(
[key] => raccoon
[basePath] => farm/horse/raccoon
)
)
https://eval.in/571065
i improved your code:
function createList($array, $keySearch, $path=null) {
$result = [];
foreach ($array as $key => $item) {
$basePath = $path === null ? $key : $path. "/" . $key;
if(is_array($item)){
if (stripos($key, $keySearch) !== false) {
$result[] = ['key' => $key, 'basePath' => $basePath];
}
$result = array_merge($result, createList($item, $keySearch, $basePath));
}
}
return $result;
}
$keySearch = 'o';
$res = createList($array, $keySearch);
print_r($res);
demo
UPD: if you find all keys, not only those which points array, change code so:
function createList($array, $keySearch, $path=null) {
$result = [];
foreach ($array as $key => $item) {
$basePath = $path === null ? $key : $path. "/" . $key;
if (stripos($key, $keySearch) !== false)
$result[] = ['key' => $key, 'basePath' => $basePath];
if(is_array($item))
$result = array_merge($result, createList($item, $keySearch, $basePath));
}
return $result;
}
$keySearch = 'fr';
$res = createList($array, $keySearch);
print_r($res);
demo
You can use your same function with addition ref attribute, and append array into that attribute.
$array = ["farm"=>
[
"horse"=>
[
"rabbit"=>
[
"fred1"=> "fred1",
"fred2"=> "fred2",
"fred3"=> "fred3",
"fred4"=> "fred4"
],
"raccoon"=>
["frida"=> "frida"]
]
]
];
function createList($array, $keySearch, $path, &$out) {
foreach ($array as $key => $item) {
$basePath = $path === null ? $key : $path. "/" . $key;
if(is_array($item)){
if (stripos($key, $keySearch) !== false){
$a['key'] = $key ;
$b['basePath'] = $basePath;
$out[] = array_merge_recursive ($a, $b);
}
createList($item, $keySearch, $basePath, $out);
}
}
}
$keySearch = "o";
createList($array, $keySearch, '', $result);
print_r($result);
Demo: https://eval.in/571224
RECURSIVE ALGORITHM SOLUTION:
<?php
$array = ["farm"=>
[
"horse"=>
[
"rabbit"=>
[
"fred1"=> "fred1",
"fred2"=> "fred2",
"fred3"=> "fred3",
"fred4"=> "fred4"
],
"raccoon"=>
["john"=> "john"]
]
]
];
$jl = array();
$root = "";
function walkJarLarsData($ar, $search, $base="base-path", $pKey=""){
global $jl, $root;
if(!stristr($root, $base)){
$root .= $base;
}
foreach($ar as $key=>$val){
$pKey = $pKey?"{$pKey}":"";
if (preg_match("#" . preg_quote($search) . "#", $key)) {
$jl[] = array(
"key" => $key,
"basePath" => $root . "/{$pKey}/{$key}",
);
}
if(is_array($val)){
walkJarLarsData($val, $search, $base, $key);
}
}
return $jl;
}
var_dump(walkJarLarsData($array, "o"));
assuredly, this is the solution you seek:
<?php
$arBase = array();
$kern = "";
function arrayRecurse($ar, $search, $mainPath="base-path", $cue=""){
global $arBase, $kern;
$kern = !(stristr($kern, $mainPath))? $kern.= $mainPath : $kern;
foreach($ar as $key=>$val){
$cue = $cue?"{$cue}":"";
if (preg_match("#" . preg_quote($search) . "#", $key)) {
$arBase[] = array(
"key" => $key,
"basePath" => $kern . "/{$cue}/{$key}",
);
}
if(is_array($val)){
arrayRecurse($val, $search, $mainPath, $key);
}
}
return $arBase;
}
var_dump(arrayRecurse($array, "fr"));
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 am trying to created nested array from flat based on its keys.
Also format of keys in original array can be changed if it will simplify task.
From :
$arr = [
'player.name' => 'Joe',
'player.lastName' => 'Snow',
'team.name' => 'Stars',
'team.picture.name' => 'Joe Snow Profile',
'team.picture.file' => 'xxx.jpg'
];
To:
$arr = [
'player' => [
'name' => 'Joe'
, 'lastName' => 'Snow'
]
,'team' => [
'name'=> 'Stars'
,'picture' => [
'name' => 'Joe Snow Profile'
, 'file' =>'xxx.jpg'
]
],
];
Here is my take on it.
It should be able to handle arbitrary depth
function unflatten($arr) {
$result = array();
foreach($arr as $key => $value) {
$keys = explode(".", $key); //potentially other separator
$lastKey = array_pop($keys);
$node = &$result;
foreach($keys as $k) {
if (!array_key_exists($k, $node))
$node[$k] = array();
$node = &$node[$k];
}
$node[$lastKey] = $value;
}
return $result;
}
Combination of iteration and recursion. Could be simplified to just iterative.
$array = [
'player.name' => 'Joe',
'player.lastName' => 'Snow',
'team.name' => 'Stars',
'team.picture.name' => 'Joe Snow Profile',
'team.picture.file' => 'xxx.jpg'
];
$newArray = array ();
foreach($array as $key=> $value) {
$temp = array ();
$keys = array_reverse (explode('.', $key));
$temp[$keys[0]] = $value;
for ($i = 1; $i < count($keys); $i++) {
$temp[$keys[$i]] = $temp;
unset ($temp [$keys [$i -1]]);
}
$newArray = array_merge_recursive($newArray,$temp);
}
var_dump($newArray );
I received this question as a test, this is my answer:
<?php
function buildArray(&$newArray, $keys, $value)
{
if (sizeof($keys) == 0) {
return $value;
} else {
$key = array_shift($keys);
if (isset($newArray[$key])) {
$value = buildArray($newArray[$key], $keys, $value);
if (is_array($value)) {
$newArray[$key] += $value;
} else {
$newArray[$key] = $value;
}
$arr = $newArray;
} else {
$arr[$key] = buildArray($newArray, $keys, $value);
}
}
return $arr;
}
$arr = [
'player.name' => 'Joe',
'player.lastName' => 'Snow',
'team.name' => 'Stars',
'team.picture.name' => 'Joe Snow Profile',
'team.picture.file' => 'xxx.jpg',
];
$newArray = [];
foreach ($arr as $key => $value) {
$explodedKey = explode(".", $key);
$temp = buildArray($newArray, $explodedKey, $value);
$newArray = array_merge($temp, $newArray);
}
print_r($newArray);
?>
You could do it like this
$newArr = [];
for ($arr as $key => $val) {
$tmp = explode ('.', $key);
if (!array_key_exists ($tmp [0], $newArray){
$newArray [$tmp [0]] = [];
}
$newArray [$tmp [0]][$tmp [1]] = $val;
}
edit:
Damn didn't saw the third level in team.
Not very generic but should work for third level ;)
$newArr = [];
for ($arr as $key => $val) {
$tmp = explode ('.', $key);
if (!array_key_exists ($tmp [0], $newArray){
$newArray [$tmp [0]] = [];
}
if (count($tmp) > 2){
if (!array_key_exists ($tmp [1], $newArray[$tmp [0]]){
$newArray [$tmp [0]][$tmp[1]] = [];
}
$newArray [$tmp [0]][$tmp [1]][$tmp [2]] = $val;
} else {
$newArray [$tmp [0]][$tmp [1]] = $val;
}
}
I think you can use something like this, for converting 2d array to nested tree. But You'll have to play with parent_id
https://github.com/denis-cto/flat-array-to-nested-tree
I have a multi-dimensional array:
$categories = array(
array(
'CategoryID' => 14308,
'CategoryLevel' => 1,
'CategoryName' => 'Alcohol & Food',
'CategoryParentID' => 14308
),
// CHILD CATEGORIES
array(
array(
'CategoryID' => 179836,
'CategoryLevel' => 2,
'CategoryName' => 'Alcohol & Alcohol Mixes',
'CategoryParentID' => 14308
),
array(
array(
'CategoryID' => 172528,
'CategoryLevel' => 2,
'CategoryName' => 'Antipasto, Savoury',
'CategoryParentID' => 14308
)
)
)
);
I need to get the exact location of the index, and since array_search doesn't work on multi-dimensional arrays, I'm using one of the functions provided on the PHP manual page.
function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
.. but it also returns the key of the first array only:
echo recursive_array_search(172528, $categories); // outputs 1
I'm expecting:
[1][1][0]
You can change your recursive function like this, which should give you the solution:
function recursive_array_search($needle, $haystack, $currentKey = '') {
foreach($haystack as $key=>$value) {
if (is_array($value)) {
$nextKey = recursive_array_search($needle,$value, $currentKey . '[' . $key . ']');
if ($nextKey) {
return $nextKey;
}
}
else if($value==$needle) {
return is_numeric($key) ? $currentKey . '[' .$key . ']' : $currentKey . '["' .$key . '"]';
}
}
return false;
}
This will result in
[1][1][0]["CategoryID"]
Since CategoryID is also a key in your multidimensional array.
If you don't want this, you can adapt the function to
function recursive_array_search($needle, $haystack, $currentKey = '') {
foreach($haystack as $key=>$value) {
if (is_array($value)) {
$nextKey = recursive_array_search($needle,$value, $currentKey . '[' . $key . ']');
if ($nextKey) {
return $nextKey;
}
}
else if($value==$needle) {
return is_numeric($key) ? $currentKey . '[' .$key . ']' : $currentKey;
}
}
return false;
}
You are ignoring the returned value of your inner call to recursive_array_search. Don't do that.
/*
* Searches for $needle in the multidimensional array $haystack.
*
* #param mixed $needle The item to search for
* #param array $haystack The array to search
* #return array|bool The indices of $needle in $haystack across the
* various dimensions. FALSE if $needle was not found.
*/
function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
if($needle===$value) {
return array($key);
} else if (is_array($value) && $subkey = recursive_array_search($needle,$value)) {
array_unshift($subkey, $key);
return $subkey;
}
}
}
public static function unique(array $data, $key){
$rez = [];
foreach($data as $val){
if(!isset($val[$key]) && is_array($val)){
return self::unique($val, $key);
}elseif( isset($val[$key]) ){
$rez[] = $val[$key];
}
}
return array_unique($rez);
}
function array_search_recursive( $search, $values = array(), $i = 0) {
$match = false;
var_dump($i, $search);
$i++;
foreach ( $values as $keyState => $val ) {
var_dump($val == $search, 'expression');
if ( $val == $search ) {
return $keyState;
}
if ( is_array( $val ) ) {
$match = array_search_recursive($search, $val, $i);
}
if ( $match !== false ) {
return $keyState;
}
}
return false;
}
echo array_search_recursive($search, $canada)
Edit:
This will return the first key, tested for $canada = array( 'Brazilia' => 'test1', "Alberta" => [ "Airdrie", "Brooks", "Camrose" ], "British Columbia" => [ "Abbotsford" => [ 'a', 'b', 'c' ], "Armstrong", "Castlegar" ], "Manitoba" => [ "Brandon", "Selkirk" ], 'Olanda' => 'test2' ); $search = "Selkirk";
I've realized I need to stop banging my head and ask for help...
I have the following array:
$permissionTypes = array(
'system' => array(
'view' => 'View system settings.',
'manage' => 'Manage system settings.'
),
'users' => array(
'all' => array(
'view' => 'View all users.',
'manage' => 'Manage all users.'
)
),
'associations' => array(
'generalInformation' => array(
'all' => array(
'view' => 'View general information of all associations.',
'manage' => 'Manage general information of all associations.'
),
'own' => array(
'view' => 'View general information of the association the user is a member of.',
'manage' => 'Manage general information of the association the user is a member of.'
)
)
));
I'm trying to collapse / cascade the keys into a one-dimension array like so:
array(
'system_view',
'system_manage',
'users_all_view',
'users_all_manage',
'associations_generalInformation_all_view',
'associations_generalInformation_all_manage',
'associations_generalInformation_own_view',
'associations_generalInformation_own_manage'
)
I could use nested loops, but the array will be an undefined number of dimensions.
This is the closest I've gotten:
public function iterateKeys(array $array, $joiner, $prepend = NULL) {
if (!isset($formattedArray)) { $formattedArray = array(); }
foreach ($array as $key => $value) {
if(is_array($value)) {
array_push($formattedArray, $joiner . $this->iterateKeys($value, $joiner, $key));
} else {
$formattedArray = $prepend . $joiner . $key;
}
}
return $formattedArray;
}
Any ideas?
I think this should do it:
public function iterateKeys(array $array, $joiner, $prepend = NULL) {
if (!isset($formattedArray)) {
$formattedArray = array();
}
foreach ($array as $key => $value) {
if(is_array($value)) {
$formattedArray = array_merge($formattedArray, $this->iterateKeys($value, $joiner, $prepend . $joiner . $key));
} else {
$formattedArray[] = $prepend . $joiner . $key;
}
}
return $formattedArray;
}
Since the recursive call returns an array, you need to use array_merge to combine it with what you currently have. And for the non-array case, you need to push the new string onto the array, not replace the array with a string.
try this:
function flattern(&$inputArray, $tmp = null, $name = '')
{
if ($tmp === null) {
$tmp = $inputArray;
}
foreach($tmp as $index => $value) {
if (is_array($value)) {
flattern($inputArray, $value, $name.'_'.$index);
if (isset($inputArray[$index])) {
unset($inputArray[$index]);
}
} else {
$inputArray[$name.'_'.$index] = $value;
}
}
return $inputArray;
}
var_dump(flattern($permissionTypes));
function flattenWithKeys(array $array, array $path = []) {
$result = [];
foreach ($array as $key => $value) {
$currentPath = array_merge($path, [$key]);
if (is_array($value)) {
$result = array_merge($result, flattenWithKeys($value, $currentPath));
} else {
$result[join('_', $currentPath)] = $value;
}
}
return $result;
}
$flattened = flattenWithKeys($permissionTypes);
Working fine for me.
function array_key_append($source_array, $return_array = array(), $last_key = '', $append_with = "#")
{
if(is_array($source_array))
{
foreach($source_array as $k => $v)
{
$new_key = $k;
if(!empty($last_key))
{
$new_key = $last_key . $append_with . $k;
}
if(is_array($v))
{
$return_array = array_key_append($v, $return_array, $new_key, $append_with);
} else {
$return_array[$new_key] = $v;
}
}
}
return $return_array;
}
$StandardContactRequestDataforALL = array_key_append($StandardContactRequestDataforALL, $return_array = array(), $last_key = '', $append_with = "#");
$result = array_merge($arr1,$arr2);
I want to exclude numerical values of $arr2,is there an option for this?
Edit after comment:
$arr1 = array('key' => 1);
$arr2 = array('test',1 => 'test', 'key2' => 2);
after processing I need the result to be:
array('key' => 1,'key2' => 2);
Excluding numerical keys
It seems that you want to array_filter your $arr2's keys, first:
function not_numeric( $object ) {
return !is_numeric( $object );
}
$no_numeric_keys = array_filter( array_keys( $arr2 ), not_numeric );
$no_numeric_array = array_intersect_keys( $arr2, $no_numeric_keys );
$result = array_merge( $arr1, $no_numeric_array );
I'm guessing that this would work, after using $result = array_merge($arr1,$arr2);:
foreach ($result as $key => $value) {
if (is_numeric($key)) {
unset($result[$key]);
}
}
Edit:
In as few lines as possible (1) – as requested in the new title:
foreach ($result as $key => $value) { if (is_numeric($key)) { unset($result[$key]); } }
Just loop through each array and test if keys are strings:
$output = array();
foreach($arr1 as $key => $value) {
if(is_string($key)) {
$output[$key] = $value;
}
}
foreach($arr2 as $key => $value) {
if(is_string($key)) {
$output[$key] = $value;
}
}
Edit:
Since you said elegant...
function merge_arrays_string_keys()
{
$output = array();
foreach(func_get_args() as $arr)
{
if(is_array($arr))
{
foreach($arr as $key => $value) {
if(is_string($key) {
$output[$key] = $value;
}
}
}
}
return $output;
}
elegant, huh?
$test = array('test', 1 => 'test', 'key2' => 2, 33, 3 => 33, 'foo' => 'bar');
$test_non_num = array_intersect_key(
$test,
array_flip(
array_diff(
array_keys($test),
array_filter(array_keys($test), 'is_int'))));
print_r($test_non_num); // key2=>2, foo=>bar
Use this code , it will also do the require thing.
<?php
$result = array ( 1,"pavunkumar","bks", 123 , "3" ) ;
array_walk($result,'test_print');
print_r ( $result ) ;
function test_print( $val , $key )
{
global $result;
if ( gettype ( $val ) == 'integer' )
{
unset ( $result[$key] ) ;
}
}
array_diff_ukey($m=$arr2+$arr1,$m,function($k){return is_string($k);})