Say for example I have the following array:
$h = array (
"app" => array (
"level1" => array (
"level2" => array (
"level3" =>3
),
"level4" => array (
"level5" => 2
)
)
)
);
What I wish to do is to create a string for every single sub-array found in here. For example, using the array above, the output would be:
Array
(
[0] => Array
(
[app.level1.level2.level3] => 3
)
[1] => Array
(
[app.level1.level4.level5] => 2
)
)
As you can see, each sub-array is concatenated with a '.' to represent the fact there is a child array with a value assigned given by the last node. Of course the only thing I can think of is to create a recursive function that could handle this, though this is where I'm having some trouble here. Here's what I started working on:
public static function buildString($array, $string ="") {
foreach($array as $h => $k) {
if(is_array($k)) {
$string .= $h.".";
return self::buildString($k, $string);
} else {
$string .= $h;
$j[] = array (
$string => $k
);
return $j;
}
}
}
Inputting the array given above within this method, I successfully get the first iteration:
Array
(
[0] => Array
(
[app.level1.level2.level3] => 3
)
)
And this is where I am at the moment and cannot seem to figure out how to do the rest of the array, or any size array for that matter.
Any hints//remarks would be appreciated.
You cannot return inside the foreach loop, you need to aggregate all of the recursive/non-recursive results and bubble them up. Something like this:
public static function buildString($array, $string ="") {
$j = array();
foreach($array as $h => $k) {
if(is_array($k)) {
$string .= $h.".";
$j = array_merge($j, self::buildString($k, $string));
} else {
$string .= $h;
$j[] = array (
$string => $k
);
}
}
return $j;
}
you can use array_walk() as a method:
$h = array (
"app" => array (
"level1" => array (
"level2" => array (
"level3" =>3
),
"level4" => array (
"level5" => 2
)
)
)
);
$results = array();
function get_strings($item, $key, $old_key = null) {
global $results;
if(is_array($item)) {
array_walk($item, 'get_strings', $old_key . $key . ".");
} else {
$results[$old_key . $key] = $item;
}
}
array_walk($h, 'get_strings');
print_r($results); //returns Array ( [app.level1.level2.level3] => 3 [app.level1.level4.level5] => 2 )
array_walk() documentation: http://php.net/manual/en/function.array-walk.php
Although I must give credit to #kennypu, I've made some slight modifications to the answer in order to contain the code in one function without the use of global or any other variables within the class (keeping it all in one method).
public static function buildString($array, $delimeter = '.') {
$results = array();
$func = function($item, $key, $old_key = NULL) use (&$func, &$results, $delimeter) {
if(is_array($item)) {
array_walk($item, $func, $old_key . $key . $delimeter);
} else {
$results[$old_key . $key] = $item;
}
};
array_walk($array, $func);
return $results;
}
In essence I created an anonymous function that uses parameters from the parent with the use of the key word use. Although not much documentation can be found for use some examples are shown here: Anonymous Functions.
Related
I have a Laravel installed with Moloquent (Mongo). Mongo ins't necessarily the problem, when the model loads the "JSON" record, it becomes a PHP associative array.
I need to be able to create a function in a model that returns an array element by a string.
for example:
$search1 = 'folder1/folder2/folder3/item';
//would look like: $array['folder1'][folder2'][folder3']['item']
$search2 = 'folder1/picture1/picture';
//would look like: $array['folder1'][picture1']['picture']
echo getRecord($search1);
echo getRecord($search2);
function getRecord($str='') {
//this function take path as string and return array
return $result;
}
I guess I could use the ?? operator, but I have to form an array "check" meaning:
How would I form the $array['1']['2']['3'] if I have 3 elements deep or 1 ($array['1']), or 5 ($array['1']['2']['3']['4']['5']).
I am making an api to add an item or folder to Mongo.
Input : "f1/f2/item"
This function I have:
echo print_r($j->_arrayBuilder('f1/f2/item'), true);
public function _arrayBuilder($folderPath)
{
$ret = array();
$arr = explode('/', $folderPath);
Log::info("Path Array:\n" . print_r($arr, true));
$x = count($arr) - 1;
Log::info("Count: " . $x);
for ($i = 0; $i <= $x; $i++) {
Log::info("Element of arr: " . $arr[$i]);
$ret = array($arr[$i] => $ret);
}
return $ret;
}
Current output:
Array
(
[item] => Array
(
[f2] => Array
(
[f1] => Array
(
)
)
)
)
Desire output:
Array
(
[f1] => Array
(
[f2] => Array
(
[item] => Array
(
)
)
)
)
Note: I have tried PHP's array_reverse and it does not work on this.. Multidimensional and non-numeric..
Thank you.
If I understand correctly, You want to take input string f1/f2/f3/f4/f5/item and create array("f1" => array("f2" => array("f3" => array("f4" => array("f5" => array("item" => array()))))))
In order to do that you can use function close to what you tried as:
function buildArr($path) {
$path = array_reverse(explode("/", $path)); // getting the path and reverse it
$ret = array();
foreach($path as $key)
$ret = array($key => $ret);
return $ret;
}
For input of print_r(buildArr("f1/f2/item")); it prints:
Array
(
[f1] => Array
(
[f2] => Array
(
[item] => Array
(
)
)
)
)
Hope that what you meant. If not feel free to comment
I'm getting below output as an array.
$array =
Array
(
[12] => Array
(
[1] => Array
(
[14] => Array
(
[0] => Array
(
[name] => Avaya Implementation Services
[service_id] => 14
[ser_type_id] => 1
[service_desc] =>Avaya Implementation Servic
)
)
)
)
);
I want to print only service_desc array value. and I don't want call like $array[12][1][14][0]['service_desc'];
How can I call particular service_desc array value of the array?
As you mentioned that you don't want to call it as $array[12][1][14][0]['service_desc'] you can use extract function which will create variables from your array,
extract($array[12][1][14][0]);
echo $service_desc;
And then you can use your particular key such as service_desc as variable.
You can try this function: (Please optimize as per your requirements)
$arr ="<YOUR ARRAY>";
$val = "service_desc";
echo removekey($arr, $val);
function removekey($arr, $val) {
$return = array();
foreach ($arr as $k => $v) {
if (is_array($v) && $k !== $val) {
removekey($v, $val);
continue;
}
if ($k == $val) {
echo ($arr[$k]);
die;
}
$return[$k] = $v;
}
return $return;
}
You can use array_walk_rescursive to frame a single dimensional array of matching keys:
DEMO
<?php
$array[12][1][14][0]['service_desc'] = 'Avaya Implementation Servic';
$array[12][1][14][0]['service'] = 'dsfasf';
$array[12][1][114][0]['service_desc'] = 'Avaya Implementation Servicasdfdsf';
$searchKey = 'service_desc';
$desiredValues = [];
array_walk_recursive($array, function ($v, $k) use ($searchKey, &$desiredValues) {
if ($k === $searchKey) {
$desiredValues[] = $v;
}
});
print_r($desiredValues);
So this will yield:
Array
(
[0] => Avaya Implementation Servic
[1] => Avaya Implementation Servicasdfdsf
)
You might use the array_walk_recursive function.
array_walk_recursive($array, function ($val, $key) {
if ($key == 'service_desc') print_r($val);
} );
Instead of the print_r statement, you can collect your data into another structure, which you convey using the use statement, or with the $userdata additional parameter (see http://www.php.net/manual/en/function.array-walk-recursive.php ).
$results = array();
array_walk_recursive($array, function ($val, $key) use (&$results) {
if ($key == 'service_desc') {
$results []= $val;
}
} );
Pay extra care to the & in front of the use (&$results) otherwise your array of results will be considered immutable inside the callback (i.e. all changes discarded).
Convert multidimensional array to single array using iterator_to_array
REF: http://php.net/manual/en/function.iterator-to-array.php
$service_desc= iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator($your_array)), 0);
print_r($service_desc);
Result:
Array
(
[name] => Avaya Implementation Services
[service_id] => 14
[ser_type_id] => 1
[service_desc] =>Avaya Implementation Servic
)
I want to concat values of array with same key
Example:
[0] => Array
(
[0] => A
[1] => XYZ
)
[1] => Array
(
[0] => B
[1] => ABC
)
[2] => Array
(
[0] => A
[1] => LMN
)
[3] => Array
(
[0] => B
[1] => PQR
)
)
Expected output:
[0] => Array
(
[0] => A
[1] => XYZ,LMN
)
[1] => Array
(
[0] => B
[1] => ABC,PQR
)
)
A simple solution uses the PHP function array_reduce():
// The input array you posted in the question
$input = array(
array('A', 'XYZ'),
array('B', 'ABC'),
array('A', 'LMN'),
array('B', 'PQR'),
);
// Reduce the array to a new array that contains the data aggregated as you need
$output = array_reduce(
// Process each $item from $input using a callback function
$input,
// The callback function processes $item; the partial result is $carry
function (array $carry, array $item) {
// Extract the key into a variable
$key = $item[0];
// If the key was encountered before
// then a partial entry already exists in $carry
if (isset($carry[$key])) {
// Append the desired value to the existing entry
$carry[$key][1] .= ','.$item[1];
} else {
// Create a new entry in $carry (copy $item to key $key for quick retrieval)
$carry[$key] = $item;
}
// Return the updated $carry
return $carry;
},
// Start with an empty array (it is known as $carry in the callback function)
array()
);
// $output contains the array you need
Try this:
$final = array();
foreach ($array_items as $item)
{
$key = $item[0];
$found_index = -1;
for ($i=0; $i<count($final); $i++)
{
if ($key == $final[$i][0])
{
$found_index = $i;
break;
}
}
if ($found_index == -1)
{
$final_item = array();
$final_item[0] = $key;
$final_item[1] = $item[1];
$final[] = $final_item;
}
else
{
$final[$found_index][1] .= ",".$item[1];
}
}
We create a new array $final, and loop through your old array $array_items. For each item, we see if there is already an item in $final that has the same [0] index. If it doesn't exist, we create it and add the initial string to the [1] index. If it does exist, we just have to add the string onto the end of the [1] index.
Try it, substituting $array_items for whatever your array is called, let me know if it works.
Check my solution. It should work fine. I hope it will help you much.
$result = $passed_keys = $extended_arr = [];
foreach ($arr as $k => $value) {
for($i = $k + 1; $i < count($arr); $i++){
if ( $value[0] == $arr[$i][0] ){ // compare each array with rest subsequent arrays
$key_name = $value[0];
if (!array_key_exists($key_name, $result)){
$result[$key_name] = $value[1] .",". $arr[$i][1];
} else {
if (!in_array($i, $passed_keys[$key_name])) {
$result[$key_name] .= ",". $arr[$i][1];
}
}
$passed_keys[$key_name][] = $i; // memorizing keys that were passed
}
}
}
array_walk($result, function($v, $k) use(&$extended_arr){
$extended_arr[] = [$k, $v];
});
The result is in $extended_arr
My solution, creates a custom key which makes identifying the letter much easier. This removes the need to continuously iterate through each array, which can become a major resources hog.
<?php
$inital_array = array(
array('A','XYZ'),
array('B','ABC'),
array('A','LMN'),
array('B','PQR')
);
$concat_array = array();
foreach($inital_array as $a){
$key = $a[0];
if( !isset($concat_array[$key]) ){
$concat_array[$key] = array($key,'');
}
$concat_array[$key][1] .= (empty($concat_array[$key][1]) ? '' : ',').$a[1];
}
$concat_array = array_values($concat_array);
echo '<pre>',print_r($concat_array),'</pre>';
This question already has answers here:
How to Flatten a Multidimensional Array?
(31 answers)
Closed 5 months ago.
Is there any fast way to flatten an array and select subkeys ('key'&'value' in this case) without running a foreach loop, or is the foreach always the fastest way?
Array
(
[0] => Array
(
[key] => string
[value] => a simple string
[cas] => 0
)
[1] => Array
(
[key] => int
[value] => 99
[cas] => 0
)
[2] => Array
(
[key] => array
[value] => Array
(
[0] => 11
[1] => 12
)
[cas] => 0
)
)
To:
Array
(
[int] => 99
[string] => a simple string
[array] => Array
(
[0] => 11
[1] => 12
)
)
Give this a shot:
$ret = array();
while ($el = each($array)) {
$ret[$el['value']['key']] = $el['value']['value'];
}
call_user_func_array("array_merge", $subarrays) can be used to "flatten" nested arrays.
What you want is something entirely different. You could use array_walk() with a callback instead to extract the data into the desired format. But no, the foreach loop is still faster. There's no array_* method to achieve your structure otherwise.
Hopefully it will help someone else, but here is a function I use to flatten arrays and make nested elements more accessible.
Usage and description here:
https://totaldev.com/flatten-multidimensional-arrays-php/
The function:
// Flatten an array of data with full-path string keys
function flat($array, $separator = '|', $prefix = '', $flattenNumericKeys = false) {
$result = [];
foreach($array as $key => $value) {
$new_key = $prefix . (empty($prefix) ? '' : $separator) . $key;
// Make sure value isn't empty
if(is_array($value)) {
if(empty($value)) $value = null;
else if(count($value) == 1 && isset($value[0]) && is_string($value[0]) && empty(trim($value[0]))) $value = null;
}
$hasStringKeys = is_array($value) && count(array_filter(array_keys($value), 'is_string')) > 0;
if(is_array($value) && ($hasStringKeys || $flattenNumericKeys)) $result = array_merge($result, flat($value, $separator, $new_key, $flattenNumericKeys));
else $result[$new_key] = $value;
}
return $result;
}
This should properly combine arrays with integer keys. If the keys are contiguous and start at zero, they will be dropped. If an integer key doesn't yet exist in the flat array, it will be kept as-is; this should mostly preserve non-contiguous arrays.
function array_flatten(/* ... */)
{
$flat = array();
array_walk_recursive(func_get_args(), function($value, $key)
{
if (array_key_exists($key, $flat))
{
if (is_int($key))
{
$flat[] = $value;
}
}
else
{
$flat[$key] = $value;
}
});
return $flat;
}
You could use !isset($key) or empty($key) instead to favor useful values.
Here's a more concise version:
function array_flatten(/* ... */)
{
$flat = array();
array_walk_recursive(func_get_args(), function($value, $key) use (&$flat)
{
$flat = array_merge($flat, array($key => $value));
});
return $flat;
}
I'm trying to build an associative array in PHP dynamically, and not quite getting my strategy right. Basically, I want to insert a value at a certain depth in the array structure, for instance:
$array['first']['second']['third'] = $val;
Now, the thing is, I'm not sure if that depth is available, and if it isn't, I want to create the keys (and arrays) for each level, and finally insert the value at the correct level.
Since I'm doing this quite a lot in my code, I grew tired of doing a whole bunch of "array_key_exists", so I wanted to do a function that builds the array for me, given a list of the level keys. Any help on a good strategy for this is appreciated. I'm sure there is a pretty simple way, I'm just not getting it...
php doesn't blame you if you do it just so
$array['first']['second']['third'] = $val;
print_r($array);
if you don't want your keys to be hard coded, here's a flexible solution
/// locate or create element by $path and set its value to $value
/// $path is either an array of keys, or a delimited string
function array_set(&$a, $path, $value) {
if(!is_array($path))
$path = explode($path[0], substr($path, 1));
$key = array_pop($path);
foreach($path as $k) {
if(!isset($a[$k]))
$a[$k] = array();
$a = &$a[$k];
}
$a[$key ? $key : count($a)] = $value;
}
// example:
$x = array();
array_set($x, "/foo/bar/baz", 123);
array_set($x, "/foo/bar/quux", 456);
array_set($x, array('foo', 'bah'), 789);
Create a function like:
function insert_into(&$array, array $keys, $value) {
$last = array_pop($keys);
foreach($keys as $key) {
if(!array_key_exists($key, $array) ||
array_key_exists($key, $array) && !is_array($array[$key])) {
$array[$key] = array();
}
$array = &$array[$key];
}
$array[$last] = $value;
}
Usage:
$a = array();
insert_into($a, array('a', 'b', 'c'), 1);
print_r($a);
Ouput:
Array
(
[a] => Array
(
[b] => Array
(
[c] => 1
)
)
)
That's tricky, you'd need to work with references (or with recursion, but I
chose references here):
# Provide as many arguments as you like:
# createNestedArray($array, 'key1', 'key2', etc.)
function createNestedArray(&$array) {
$arrayCopy = &$array;
$args = func_get_args();
array_shift($args);
while (($key = array_shift($args)) !== false) {
$arrayCopy[$key] = array();
$arrayCopy = &$arrayCopy[$key];
}
}
<?php
function setElements(&$a, array $path = [], $values = [])
{
if (!is_array($path)) {
$path = explode($path[0], substr($path, 1));
}
$path = "[ '" . join("' ][ '", $path) . "' ]";
$code =<<<CODE
if(!isset(\$a{$path})){
\$a{$path} = [];
}
return \$a{$path}[] = \$values;
CODE;
return eval($code);
}
$a = [];
setElements($a, [1,2], 'xxx');
setElements($a, [1,2,3], 233);
setElements($a, [1,2,4], 'AAA');
setElements($a, [1,2,3,4], 555);
print_r($a);
Output
Array
(
[1] => Array
(
[2] => Array
(
[0] => xxx
[3] => Array
(
[0] => 233
[4] => Array
(
[0] => 555
)
)
[4] => Array
(
[0] => AAA
)
)
)
)
You should check it here http://sandbox.onlinephpfunctions.com/