In a normal array you can select this way
$key='example';
echo $array[$key];
How about in a multidimensional?
$keys='example[secondDimension][thirdDimension]';
echo $array[$keys];
What's the proper way to go about this?
i think this solution is good.
note that you have to wrap all keys with "[" and "]".
$array = array(
'example' => array(
'secondDimension' => array(
'thirdDimension' => 'Hello from 3rd dimension',
)
),
);
function array_get_value_from_plain_keys($array, $keys)
{
$result;
$keys = str_replace(array('[', ']'), array("['", "']"), $keys); // wrapping with "'" (single qoutes)
eval('$result = $array' . $keys . ';');
return $result;
}
$keys = '[example][secondDimension][thirdDimension]'; // wrap 1st key with "[" and "]"
echo array_get_value_from_plain_keys($array, $keys);
Learn more about eval() function
if you also want to check if the value is defined or not then you can use this function
function array_check_is_value_set_from_plain_keys($array, $keys)
{
$result;
$keys = str_replace(array('[', ']'), array("['", "']"), $keys); // wrapping with "'" (single qoutes)
eval('$result = isset($array' . $keys . ');');
return $result;
}
Giving a better name to that function will be appreciated ^^
Here is a solution without using eval:
$array = [
'example' => [
'secondDimension' => [
'thirdDimension' => 'Hello from 3rd dimension',
],
],
];
$keys = '[example][secondDimension][thirdDimension]';
function get_array_value( $array, $keys ) {
$keys = explode( '][', trim( $keys, '[]' ) );
return get_recursive_array_value( $array, $keys );
}
function get_recursive_array_value( $array, $keys ) {
if ( ! isset( $array[ $keys[0] ] ) ) {
return null;
};
$res = $array[ $keys[0] ];
if ( count( $keys ) > 1 ) {
array_shift( $keys );
return get_recursive_array_value( $res, $keys );
} else {
return $res;
}
}
echo get_array_value( $array, $keys );
Want you to use the $b array to follow the nested keys of the $a array and get the value in $c ?
<?php
$a = [ 'key_1' => [ 'key_2' => [ 'key_3' => 'value', ], ], ] ;
$b = ['key_1', 'key_2', 'key_3', ] ;
if ($b)
{
$c = $a ; // $a is not copied (copy-on-write)
foreach($b as $k)
if (isset($c[$k]))
$c = $c[$k] ;
else
{
unset($c);
break;
}
var_dump($c);
}
/*
output :
string(5) "value"
*/
or do you want to generate the $b array for an arbitrary formed string and get $c as a reference ?
<?php
$a = [ 'key_1' => [ 'key_2' => [ 'key_3' => 'value', ], ], ] ;
$b = '[key_1][key_2][key_3]';
if ($b !== '')
{
$b = explode('][', trim($b, '[]'));
$c = &$a ;
foreach($b as $k)
if (isset($c[$k]))
$c = &$c[$k] ;
else
{
unset($c);
break;
}
}
var_dump($c);
$c = 'new val' ;
unset($c);
var_dump($a['key_1']['key_2']['key_3']);
/*
output :
string(5) "value"
string(7) "new val"
*/
You have to use a separate variable for each dimension of the array. A common pattern you see with multidimensional arrays where you need to do something with the 2nd dimension is something like this:
$pets = [
'dog' => ['Jack', 'Fido', 'Woofie'],
'cat' => ['Muggles', 'Snowball', 'Kitty'],
];
// Loop through keys in first dimension
foreach ($pets as $type => $names) {
foreach ($names as $index => $name) {
// And do something with second dimension using the variable
// you've gained access to in the foreach
$pets[$type][$index] = strtoupper($name);
}
}
Related
I have a string containing an array key position which I am trying to use this position to access the array ($arr).
Example of string ($str) which has a string value of svg.1.linearGradient.0.#style
This would be the equivalent of ['svg'][1]['linearGradient'][0]['#style']
How can I use the string to access/retrieve data from $arr using the above position?
For example, lets say i wanted to unset the array key unset($arr['svg'][1]['linearGradient'][0]['#style']) - how can i achieve this programmatically?
You can use the mechanism of passing the value by reference:
$result = &$arr;
$path = explode('.', $str);
for($i = 0; $i < count($path); $i++) {
$key = $path[$i];
if (is_array($result) && array_key_exists($key, $result)) {
if ($i == count($path) - 1) {
unset($result[$key]); // deleting an element with the last key
} else {
$result = &$result[$key];
}
} else {
break; // not found
}
}
unset($result); // resetting value by reference
print_r($arr);
fiddle
One method could be to split the string on ., then iterate through the "keys" to traverse your $arr object. (Please excuse my poor php, it's been a while...)
Example:
$arr = (object)[
"svg" => (array)[
(object)[],
(object)[
"linearGradient" => [
(object)[
"#style" => "testing",
],
],
],
],
];
$str = "svg.1.linearGradient.0.#style";
$keys = explode('.', $str);
$val = $arr;
foreach($keys as $key) {
$val = is_object($val)
? $val->$key
: $val[$key];
}
echo $val;
https://3v4l.org/h8YPv
Unsetting a key given the path:
$arr = (object)[
"svg" => (array)[
(object)[],
(object)[
"linearGradient" => [
(object)[
"#style" => "testing",
],
],
],
],
];
$str = "svg.1.linearGradient.0.#style";
$keys = explode('.', $str);
$exp = "\$arr";
$val = $arr;
foreach($keys as $index => $key) {
$exp .= is_object($val)
? "->{'" . $key . "'}"
: "[" . $key . "]";
$val = is_object($val) ? $val->$key : $val[$key];
}
eval("unset($exp);");
https://3v4l.org/Fdo6B
Docs
explode()
eval()
I have an array like the below:
$arrays = [
'a' => [
'name' => "Name 1",
'age' => "99",
'add' => ""
],
'b' => [
'name' => "Name 2",
'age' => "99",
'add' => "Add2"
],
'c' => [
'name' => "Name 3",
'age' => "99",
'add' => "Add3"
],
'd' => [
'name' => "",
'age' => "",
'add' => "Add4"
]
];
I want to get a result like:
$res = [
'a' => ['add'],
'd' => ['name','age']
];
I have tried with the below code, but it returns 1.
$status = array_walk_recursive($arrays, function($v, $k) {
global $output;
if (empty($v) && $v !== 0)
$output[$k] = $v;
});
I want to do it without using any loops because my real input array is very large and I am concerned with performance.
If your input is always of a fixed depth, you can map the existing values to the keys of all empty items:
$output = array_map(function($row) {
return array_keys(array_filter($row, function ($e) {
return empty($e) && $e !== 0;
}));
}, $arrays);
The outer function runs for each "row", the value of which is then converted into a list of all keys with empty values (excluding zeroes, as in the question).
This will keep the outer keys B & C as empty arrays, so if you want them to be removed as well then run another iteration of array_filter over the result:
$output = array_filter($output)
See https://3v4l.org/c23ZB
As mentioned in the comments, there are still several loops going on here, they're just not as visible in the code. A regular foreach loop might end up being a lot easier to read, and possibly perform faster as well.
You can use next combination of array_walk & array_filter:
$result = [];
array_walk(
$arrays,
function($el, $key) use(&$result) {
$empty = array_filter($el, function($el){return $el == "";});
$empty_keys = array_keys($empty);
if (count($empty_keys)) $result[$key] = $empty_keys;
}
);
Try it here
This is another way to achieve your desired output.
$result = [];
foreach($arrays as $key => $value) {
$empty_arr = array_filter($value, function ($ele) {
return empty($ele);
});
$empty_arr_keys = array_keys($empty_arr);
if(!empty($empty_arr_keys)) $result[$key] = $empty_arr_keys;
}
print_r($result);
#iainn's answer can be sharpened up by calling !strlen() on the deep values.
Code: (Demo)
var_export(
array_filter(
array_map(
fn($row) => array_keys(
array_filter(
$row,
fn($v) => !strlen($v)
)
),
$array
)
)
);
But you will end up making fewer iterations and writing cleaner, more intuitive/readable code if you use classic loops. This is how I would write it in my own project:
Code: (Demo)
$result = [];
foreach ($array as $rowKey => $row) {
foreach ($row as $key => $value) {
if (!strlen($value)) {
$result[$rowKey][] = $key;
}
}
}
var_export($result);
I'm trying to parse a string from a URL and convert it into a PHP array, but so far I haven't been able to get it to work correctly.
The string contains comma separated values which are then grouped by periods, for example:
course.id,course.title,course.author,course.duration,course.trailer.video_key,course.trailer.duration,course.lessons.id,course.lessons.title,track.id,track.title,track.caption.srt,track.caption.vtt,track.caption.text
The Array equivalent to this string would be:
PHP:
$array = [
'course' => [
'id',
'title',
'author',
'duration',
'trailer' => [
'video_key',
'duration'
],
'lessons' => [
'id',
'title'
]
],
'track' => [
'id',
'title',
'caption' => [
'srt',
'vtt',
'text'
]
]
];
My current solution is this:
PHP:
$string = 'course.id,course.title,course.author,course.duration,course.trailer.video_key,course.trailer.duration,course.lessons.id,course.lessons.title,track.id,track.title,track.caption.srt,track.caption.vtt,track.caption.text';
$parameters = explode( ',', $string );
$array = array();
$inset = array();
foreach ( $parameters as $parameter ) {
$segments = explode( '.', $parameter );
if ( ! array_key_exists( $segments[0], $inset ) ) {
$array[ $segments[0] ] = array();
$inset[ $segments[0] ] = true;
}
$array[ $segments[0] ] = array_merge( $array[ $segments[0] ], addSegment( $segments, 1 ) );
}
function addSegment( $segments, $counter ) {
$results = array();
if ( end( $segments ) == $segments[ $counter ] ) {
return array( $segments[ $counter ] => null );
}
$results[ $segments[ $counter ] ] = addSegment( $segments, $counter + 1 );
return $results;
}
This somewhat works, however; it fails with simpler string like this one
course,track,lessons
I think this calls for recursion but I'm not good at writing this so I'm hoping someone has already done this and can help me.
Thank you.
You can achieve this without recursion - just double loop and use of &:
$arr = explode(",", "course.id,course.title,course.author,course.duration,course.trailer.video_key,course.trailer.duration,course.lessons.id,course.lessons.title,track.id,track.title,track.caption.srt,track.caption.vtt,track.caption.text");
foreach($arr as $e) {
$e = explode(".", $e);
$obj = &$res; //set the current place root
while(count($e) > 1) { // last element will be add as value
$key = array_shift($e);
if (!isset($obj[$key]))
$obj[$key] = [];
$obj = &$obj[$key]; //reset the current place
}
$obj[] = array_shift($e); // add the last as value to current place
}
$res will contain your desire output
I have a string as:
$string = "My name is {name}. I live in {detail.country} and age is {detail.age}";
and I have an array like that and it will be always in that format.
$array = array(
'name' => 'Jon',
'detail' => array(
'country' => 'India',
'age' => '25'
)
);
and the expected output should be like :
My name is Jon. I live in India and age is 25
So far I tried with the following method:
$string = str_replace(array('{name}','{detail.country}','{detail.age}'), array($array['name'],$array['detail']['country'],$array['detail']['age']));
But the thing is we can not use the plain text of string variable. It should be dynamic on the basis of the array keys.
You can use preg_replace_callback() for a dynamic replacement:
$string = preg_replace_callback('/{([\w.]+)}/', function($matches) use ($array) {
$keys = explode('.', $matches[1]);
$replacement = '';
if (sizeof($keys) === 1) {
$replacement = $array[$keys[0]];
} else {
$replacement = $array;
foreach ($keys as $key) {
$replacement = $replacement[$key];
}
}
return $replacement;
}, $string);
It also exists preg_replace() but the above one allows matches processing.
You can use a foreach to achieve that :
foreach($array as $key=>$value)
{
if(is_array($value))
{
foreach($value as $key2=>$value2)
{
$string = str_replace("{".$key.".".$key2."}",$value2,$string);
}
}else{
$string = str_replace("{".$key."}",$value,$string);
}
}
print_r($string);
The above will only work with a depth of 2 in your array, you'll have to use recurcivity if you want something more dynamic than that.
Here's a recursive array handler: http://phpfiddle.org/main/code/e7ze-p2ap
<?php
function replaceArray($oldArray, $newArray = [], $theKey = null) {
foreach($oldArray as $key => $value) {
if(is_array($value)) {
$newArray = array_merge($newArray, replaceArray($value, $newArray, $key));
} else {
if(!is_null($theKey)) $key = $theKey . "." . $key;
$newArray["{" . $key . "}"] = $value;
}
}
return $newArray;
}
$array = [
'name' => 'Jon',
'detail' => [
'country' => 'India',
'age' => '25'
]
];
$string = "My name is {name}. I live in {detail.country} and age is {detail.age}";
$array = replaceArray($array);
echo str_replace(array_keys($array), array_values($array), $string);
?>
echo "my name is ".$array['name']." .I live in ".$array['detail']['countery']." and my age is ".$array['detail']['age'];
$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);})