PHP manipulating multidimensional array values - php

I have a result set as an array from a database that looks like:
array (
0 => array (
"a" => "something"
"b" => "something"
"c" => "something"
)
1 => array (
"a" => "something"
"b" => "something"
"c" => "something"
)
2 => array (
"a" => "something"
"b" => "something"
"c" => "something"
)
)
How would I apply a function to replace the values of an array only on the array key with b? Normally I would just rebuild a new array with a foreach loop and apply the function if the array key is b, but I'm not sure if it's the best way. I've tried taking a look at many array functions and it seemed like array_walk_recursive is something I might use, but I didn't have luck in getting it to do what I want. If I'm not describing it well enough, basically I want to be able to do as the code below does:
$arr = array();
foreach ($result as $key => $value)
{
foreach ($value as $key2 => $value2)
{
$arr[$key][$key2] = ($key2 == 'b' ? $this->_my_method($value2) : $value2);
}
}
Should I stick with that, or is there a better way?

Using array_walk_recursive:
If you have PHP >= 5.3.0 (for anonymous functions):
array_walk_recursive($result, function (&$item, $key) {
if ($key == 'b') {
$item = 'the key is b!';
}
});
Otherwise something like:
function _my_method(&$item, $key) {
if ($key == 'b') {
$item = 'the key is b!';
}
}
array_walk_recursive($result, '_my_method');

Untested but I think this will work.
function replace_b (&$arr)
{
foreach ($arr as $k => $v)
{
if ($k == 'b')
{
/* Do something */
}
if (is_array($v)
{
replace_b($arr[$k]);
}
}
}
The function will move through an array checking the keys for b. If the key points to an array it recursively follows it down.

use array_walk_recursive documented here
$replacer = function($x) {return "I used to be called $x";}; //put what you need here
$replaceB = function(&$v, $k) use ($replacer) {if ($k === 'b') $v = $replacer($v);};
array_walk_recursive($arr, $replaceB);
The replacer function might be overkill. You can replace it with a literal or anything you like.

Related

How do I get the value of the first occurrence of array_walk_recursive in php

I have a deep multidimensional array that I am needing to extract the value of a specific key. I have found that the array_walk_recursive function will be my best option. I only need the first occurrence.
My array looks like this - (except much more complicated)
Array (
[vehicle info] => Array (
[one] => Array (
[submodel] => LX
[engine] => 2.3
)
[two] => Array (
[color] => blue
[year] => 2007
[wheels] => 4
)
[three] => Array (
[submodel] => LX
[make] => Ford
[model] => F-150
[offroad] => No
)
)
)
The issue here is, submodel is in both one and three. Additionally, the array is not consistent, so I must use array_walk_recursive to search through it for the matching key, then return the value for that key.
Here is my current code -
array_walk_recursive ($array, (function ($item, $key) {
$wanted = "submodel";
if ($key === $wanted) {
echo ("$key is $item");
}
}));
The above returns submodel is LXsubmodel is LX.
Bonus Question!!
How can I search for multiple keys and return the first corresponding value for each of those? I was thinking putting all wanted keys in an array, then do a foreach loop, but don't quite know how to structure this. I am new to php.
array_walk_recursive() is the appropriate native function to call for this task. Keep track of which keys have already been declared in the result array and ensure that they are never overwritten.
Code: (Demo)
$needles = ['submodel', 'offroad'];
$result = [];
array_walk_recursive(
$array,
function($value, $key) use ($needles, &$result) {
if (
in_array($key, $needles)
&& !isset($result[$key])
) {
$result[$key] = "$key is $value";
}
}
);
var_export($result);
Output:
array (
'submodel' => 'submodel is LX',
'offroad' => 'offroad is No',
)
If your application has performance concerns, then the native function becomes less attractive because it will always iterate the entire input array's structure -- even after all sought keys are encountered. If you want to "break early" (short circuit), then you will need to design your own recursive function which will return when all sought keys are found.
Code: (Demo)
$soughtKeys = array_flip(['submodel', 'offroad']);
function earlyReturningRecursion(array $array, array $soughtKeys, array &$result = []): array
{
foreach ($array as $key => $value) {
if (!array_diff_key($soughtKeys, $result)) { // check if result is complete
return $result;
} elseif (is_array($value)) {
earlyReturningRecursion($value, $soughtKeys, $result);
} elseif (isset($soughtKeys[$key]) && !isset($result[$key])) {
$result[$key] = "$key is $value";
}
}
return $result;
}
var_export(earlyReturningRecursion($array, $soughtKeys));
// same output as the first snippet
I would start by setting the values you want to null, and then only saving them if they haven't been found yet, by checking is_null(). I haven't tested this code, but it should look something like this:
$submodel = null;
array_walk_recursive ($array, (function ($item, $key) {
$wanted = "submodel";
if ($key === $wanted && is_null($submodel)) {
echo ("$key is $item");
$submodel = $item;
}
}));
array_walk_recursive() has the defect of not allowing to return matching results however in PHP 7 you could use an anonymous function and a variable to store the matching value.
$matching = null;
$wanted = "submodel";
array_walk_recursive ($array, function ($item, $key) use ($wanted, $matching) {
if (($key === $wanted) && is_null($matching)) {
$matching = $item;
}
});
As far as there is no way to return early from array_walk_recursive(), I'd suggest to create a function to find the first occurrence of $wanted:
$arr = [
'vehicle info' => [
'one' => ['submodel' => 'LX', 'engine' => '2.3'],
'two' => ['color' => 'blue', 'year' => '2007', 'wheels' => '4'],
'three' => ['submodel' => 'LX', 'make' => 'Ford', 'model' => 'F-150', 'offroad' => 'No'],
],
];
function find($needle, $haystack, $found = '')
{
foreach ($haystack as $key => $value) {
if ($found) {
break;
}
if ($key === $needle) {
$found = "{$needle} is {$value}";
break;
}
if (is_array($value)) {
$found = find($needle, $value, $found);
}
}
return $found;
}
$wanted = 'submodel';
$result = find($wanted, $arr);
var_dump($result); // string(14) "submodel is LX"
Live demo
Update: to search for multiple keys you'll need to do it in a loop:
$multiple_keys = array('submodel', 'year');
foreach ($multiple_keys as $wanted) {
var_dump(find($wanted, $arr));
}
// Output:
// string(14) "submodel is LX"
// string(12) "year is 2007"
Live demo

Default values for Associative array in PHP

I have a function that accepts an array parameter as
array('employee_name' => 'employee_location' )
eg:
array('John' => 'U.S', 'Dave' => 'Australia', 'Unitech' => 'U.S' )
I wish to keep 'U.S' as the default location and a optional value, so
So If I pass
array('John', 'Dave' => 'Australia', 'Unitech')
Is there a in-build function in PHP that automatically converts it to
array('John' => 'U.S', 'Dave' => 'Australia', 'Unitech' => 'U.S' )
There is no built-in function for that.
You should loop through your array and check if the key is numeric. If it is, use the value as the key and add your default as the value.
Simple example (using a new array for clarity):
$result = array();
foreach ($arr as $key => $value)
{
if (is_int($key)) // changed is_numeric to is_int as that is more correct
{
$result[$value] = $default_value;
}
else
{
$result[$key] = $value;
}
}
Obviously this would break on duplicate names.
Note that MI6 will hunt you down: $agents = array('007' => 'UK'); will be transformed into $agents['UK'] => 'US'... I know UK and US have a "special relation", but this is taking things a tad far, IMHO.
$agents = array('007' => 'UK');
$result = array();
foreach($agents as $k => $v)
{
if (is_numeric($k))//leave this out, of course
{
echo $k.' won\'t like this';//echoes 007 won't like this
}//replace is_numeric with is_int or gettype($k) === 'integer'
if (is_int($k))
{//'007' isn't an int, so this won't happen
$result[$v] = $default;
continue;
}
$result[$k] = $v;
}
Result and input look exactly alike in this example.
foreach ($arr as $k => $v) {
if (is_int($k)) {
unset($arr[$k]);
$arr[$v] = 'U.S.';
}
}
I would work with something like this:
foreach ( $array AS $key => $value )
{
if ( is_numeric($key) )
{
$key = 'U.S';
}
$array[$key] = $value;
}

Foreach: Get All The Keys That Have The Value "X"

Suppose I have an array like this:
$array = array("a","b","c","d","a","a");
and I want to get all the keys that have the value "a".
I know I can get them using a while loop:
while ($a = current($array)) {
if ($a == 'a') {
echo key($array).',';
}
next($array);
}
How can I get them using a foreach loop instead?
I've tried:
foreach ($array as $a) {
if ($a == 'a') {
echo key($array).',';
}
}
and I got
1,1,1,
as the result.
If you would like all of the keys for a particular value, I would suggest using array_keys, using the optional search_value parameter.
$input = array("Foo" => "X", "Bar" => "X", "Fizz" => "O");
$result = array_keys( $input, "X" );
Where $result becomes
Array (
[0] => Foo
[1] => Bar
)
If you wish to use a foreach, you can iterate through each key/value set, adding the key to a new array collection when its value matches your search:
$array = array("a","b","c","d","a","a");
$keys = array();
foreach ( $array as $key => $value )
$value === "a" && array_push( $keys, $key );
Where $keys becomes
Array (
[0] => 0
[1] => 4
[2] => 5
)
You can use the below to print out keys with specific value
foreach ($array as $key=>$val) {
if ($val == 'a') {
echo $key." ";
}
}
here's a simpler filter.
$query = "a";
$result = array_keys(array_filter($array,
function($element)use($query){
if($element==$query) return true;
}
));
use
foreach($array as $key=>$val)
{
//access the $key as key.
}

Recursively left trim a specific character from keys

I can't quite work this out...
I was hoping that there would be a default PHP function to do this, but it seems there isn't. The code I've found online seems to not really work for my situation, since often people have only need to the modify array values and not their keys.
I basically need a recursive function that replaces every key that starts with a '_' with the same key without that symbol....
Has anybody here used something similar before?
Try this:
function replaceKeys(array $input) {
$return = array();
foreach ($input as $key => $value) {
if (strpos($key, '_') === 0)
$key = substr($key, 1);
if (is_array($value))
$value = replaceKeys($value);
$return[$key] = $value;
}
return $return;
}
So this code:
$arr = array('_name' => 'John',
'ages' => array(
'_first' => 10,
'last' => 15));
print_r(replaceKeys($arr));
Will produce (as seen on codepad):
Array
(
[name] => John
[ages] => Array
(
[first] => 10
[last] => 15
)
)
Use PHP's native array_walk_recursive()
This seems to be a cleaner solution, with $key passed as a reference in the callback function as follows:
array_walk_recursive($your_array, function ($item, &$key) {
if (strpos($key, '_') === 0) {
$key = substr($key, 1);
}
});

PHP: Get key from array?

I am sure that this is super easy and built-in function in PHP, but I have yet not seen it.
Here's what I am doing for the moment:
foreach($array as $key => $value) {
echo $key; // Would output "subkey" in the example array
print_r($value);
}
Could I do something like the following instead and thereby save myself from writing "$key => $value" in every foreach loop? (psuedocode)
foreach($array as $subarray) {
echo arrayKey($subarray); // Will output the same as "echo $key" in the former example ("subkey"
print_r($value);
}
Thanks!
The array:
Array
(
[subKey] => Array
(
[value] => myvalue
)
)
You can use key():
<?php
$array = array(
"one" => 1,
"two" => 2,
"three" => 3,
"four" => 4
);
while($element = current($array)) {
echo key($array)."\n";
next($array);
}
?>
Use the array_search function.
Example from php.net
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');
$key = array_search('green', $array); // $key = 2;
$key = array_search('red', $array); // $key = 1;
$foo = array('a' => 'apple', 'b' => 'ball', 'c' => 'coke');
foreach($foo as $key => $item) {
echo $item.' is begin with ('.$key.')';
}
$array = array(0 => 100, "color" => "red");
print_r(array_keys($array));
If it IS a foreach loop as you have described in the question, using $key => $value is fast and efficient.
If you want to be in a foreach loop, then foreach($array as $key => $value) is definitely the recommended approach. Take advantage of simple syntax when a language offers it.
Another way to use key($array) in a foreach loop is by using next($array) at the end of the loop, just make sure each iteration calls the next() function (in case you have complex branching inside the loop)
Try this
foreach(array_keys($array) as $nmkey)
{
echo $nmkey;
}
Here is a generic solution that you can add to your Array library. All you need to do is supply the associated value and the target array!
PHP Manual: array_search() (similiar to .indexOf() in other languages)
public function getKey(string $value, array $target)
{
$key = array_search($value, $target);
if ($key === null) {
throw new InvalidArgumentException("Invalid arguments provided. Check inputs. Must be a (1) a string and (2) an array.");
}
if ($key === false) {
throw new DomainException("The search value does not exists in the target array.");
}
return $key;
}

Categories