Accessing nested arrays in PHP without many loops - php

Consider the following:
$dropdown = array (
"unitofmeasure" => array (
"m" => "meters",
"ft" => "feet"
),
"facing_direction" => array (
"0" => array ("West","North-West","North","North-East","East","South-East"),
"1" => array("South","South-West")
)
....
)
Assume there are n number of sub arrays, not just the two shown above.
Iteration solution:
foreach($dropdown as $key => $val) {
foreach($val as $k => $v) {
foreach($v as $id => $value) {
//manipulate values here
}
}
}
My question is:
is there not a more elegant solution available in PHP?
for example something like foreach($dropdown->children()->children() ...)
I know there are a few semi-similar questions on SO but they're slightly different and the answers are mediocre.

Yes, I personally tend to use array_walk_recursive with a closure(if you're using PHP above 5.3).
You can, obviously, also use recursion if you like getting your hands dirty.
I suppose an example is in order:
$array = [ 0 => [0 => [ 0 => 1 ...]]];
$manipulated_array = [];
array_walk_recursive($array, function($value) use (&$manipulated_array)
{
// do whatever you wish here
});

foreach() just expects an array, so if you only need to iterate ONE of those deeply nested arrays, then you can quite easily have
foreach($arr['level1']['level2'][...]['levelGazillion'] as ...)

I tend to use recursion in these situations:
function modify_array(&$arr)
{
if (is_array(arr)) {
foreach($arr as &$val) {
modify_array($val);
}
} else {
modify_value($arr);
}
}
where modify_value(&$val) is whatever you want to do to each non-array child at any arbitrary depth

Related

Accessing a PHP array

I'm sure this has been asked before, but I can't seem to find the answer.
To that end, I have an array that looks like this:
Array
(
[0] => Array
(
[status] => active
[sid] => 1
)
[1] => Array
(
[status] => expired
[sid] => 2
)
)
What I'd like to be able to do is type $arrayName["active"] and it return the SID code. I will be using this like a dictionary object of sorts. It's like I need to reindex the array so that it is the key/value pair that I need. I was just wondering if there was an easier way to do it.
You should convert your nested arrays into a single associative array. Something like this should take your example and turn it into an associative array:
$assoc_array = array();
foreach( $example_array as $values ) {
$assoc_array[$values["status"]] = $values["sid"];
}
You can then access the sid for a given status by using $assoc_array["expired"] (returns 2)
After seeing the others' solutions, I realize this might be bit of an overkill, but I'm still just gonna throw it out there:
$foo = array(
array('status' => 'active', 'sid' => 1),
array('status' => 'expired', 'sid' => 2),
);
// Get all the 'status' elements of each subarray
$keys = array_map(function($element) {
return $element['status'];
}, $foo);
// Get all the 'sid' elements of each subarray
$values = array_map(function($element) {
return $element['sid'];
}, $foo);
// Combine them into a single array, with keys from one and values from another
$bar = array_combine($keys, $values);
print_r($bar);
Which prints:
Array
(
[active] => 1
[expired] => 2
)
Manual pages:
array_map()
array_keys()
array_values()
array_combine()
Anonymous functions
You can use this function:
function findActive($my_array){
foreach($my_array as $array){
foreach($array as $val){
if($val['status']==='active'){
return $val['sid'];
}
}
}
return false;
}
access it via a loop or directly.
if($arrayName[0]['status'] == "active") {
echo $arrayName[0]['sid'];
}
If you want to check all the SIDs
foreach($arrayName as $item) {
if($item['status'] == "active") {
echo $item['sid'];
}
}
A more direct approach is just putting the loop in a function and return an array of all active session IDs
$sidArr = array();
foreach($yourArr as $val) {
if("active" == $val["status"]) {
array_push($sidArr, $val["sid"]);
}
}
reindex would be the best
$arrayName = array()
foreach ($data_array as $data)
$arrayName[$data['status']]=$data['sid'];
Or use a function
function get_sid($status)
{
global $data_array;
foreach ($data_array as $data) {
if ($data['status']==$status)
return $data['sid'];
}
return false;
}

PHP array unset string

I am trying to unset a group of array keys that have the same prefix. I can't seem to get this to work.
foreach ($array as $key => $value) {
unset($array['prefix_' . $key]);
}
How can I get unset to see ['prefix_' . $key] as the actual variable? Thanks
UPDATE: The $array keys will have two keys with the same name. Just one will have the prefix and there are about 5 keys with prefixed keys:
Array {
[name] => name
[prefix_name] => other name
}
I don't want to remove [name] just [prefix_name] from the array.
This works:
$array = array(
'aa' => 'other value aa',
'prefix_aa' => 'value aa',
'bb' => 'other value bb',
'prefix_bb' => 'value bb'
);
$prefix = 'prefix_';
foreach ($array as $key => $value) {
if (substr($key, 0, strlen($prefix)) == $prefix) {
unset($array[$key]);
}
}
If you copy/paste this code at a site like http://writecodeonline.com/php/, you can see for yourself that it works.
You can't use a foreach because it's only a copy of the collection. You'd need to use a for or grab the keys separately and separate your processing from the array you want to manipulate. Something like:
foreach (array_keys($array) as $keyName){
if (strncmp($keyName,'prefix_',7) === 0){
unset($array[$keyName]);
}
}
You're also already iterating over the collection getting every key. Unless you had:
$array = array(
'foo' => 1,
'prefix_foo' => 1
);
(Where every key also has a matching key with "prefix_" in front of it) you'll run in to trouble.
I'm not sure I understand your question, but if you are trying to unset all the keys with a specific prefix, you can iterate through the array and just unset the ones that match the prefix.
Something like:
<?php
foreach ($array as $key => $value) { // loop through keys
if (preg_match('/^prefix_/', $key)) { // if the key stars with 'prefix_'
unset($array[$key]); // unset it
}
}
You're looping over the array keys already, so if you've got
$array = (
'prefix_a' => 'b',
'prefix_c' => 'd'
etc...
)
Then $keys will be prefix_a, prefix_c, etc... What you're doing is generating an entirely NEW key, which'd be prefix_prefix_a, prefix_prefix_c, etc...
Unless you're doing something more complicated, you could just replace the whole loop with
$array = array();
I believe this should work:
foreach ($array as $key => $value) {
unset($array['prefix_' . str_replace('prefix_', '', $key]);
}

How to check if a specific value exists at a specific key in any subarray of a multidimensional array?

I need to search a multidimensional array for a specific value in any of the indexed subarrays.
In other words, I need to check a single column of the multidimensional array for a value. If the value exists anywhere in the multidimensional array, I would like to return true otherwise false
$my_array = array(
0 => array(
"name" => "john",
"id" => 4
),
1 => array(
"name" => "mark",
"id" => 152
),
2 => array(
"name" => "Eduard",
"id" => 152
)
);
I would like to know the fastest and most efficient way to check if the array $my_array contains a value with the key "id". For example, if id => 152 anywhere in the multidimensional array, I would like true.
Nothing will be faster than a simple loop. You can mix-and-match some array functions to do it, but they'll just be implemented as a loop too.
function whatever($array, $key, $val) {
foreach ($array as $item)
if (isset($item[$key]) && $item[$key] == $val)
return true;
return false;
}
The simplest way is this:
$my_array = array(
0 => array(
"name" => "john",
"id" => 4
),
1 => array(
"name" => "mark",
"id" => 152
),
2 => array(
"name" => "Eduard",
"id" => 152
)
);
if (array_search(152, array_column($my_array, 'id')) !== FALSE) {
echo 'FOUND!';
} else {
echo 'NOT FOUND!';
}
** PHP >= 5.5
simply u can use this
$key = array_search(40489, array_column($userdb, 'uid'));
Let's suppose this multi dimensional array:
$userdb=Array
(
(0) => Array
(
(uid) => '100',
(name) => 'Sandra Shush',
(url) => 'urlof100'
),
(1) => Array
(
(uid) => '5465',
(name) => 'Stefanie Mcmohn',
(pic_square) => 'urlof100'
),
(2) => Array
(
(uid) => '40489',
(name) => 'Michael',
(pic_square) => 'urlof40489'
)
);
$key = array_search(40489, array_column($userdb, 'uid'));
Here is an updated version of Dan Grossman's answer which will cater for multidimensional arrays (what I was after):
function find_key_value($array, $key, $val)
{
foreach ($array as $item)
{
if (is_array($item) && find_key_value($item, $key, $val)) return true;
if (isset($item[$key]) && $item[$key] == $val) return true;
}
return false;
}
If you have to make a lot of "id" lookups and it should be really fast you should use a second array containing all the "ids" as keys:
$lookup_array=array();
foreach($my_array as $arr){
$lookup_array[$arr['id']]=1;
}
Now you can check for an existing id very fast, for example:
echo (isset($lookup_array[152]))?'yes':'no';
A good solution can be one provided by #Elias Van Ootegan in a comment that is:
$ids = array_column($array, 'id', 'id');
echo isset($ids[40489])?"Exist":"Not Exist";
I tried it and worked for me, thanks buddy.
Edited
Note: It will work in PHP 5.5+
TMTOWTDI. Here are several solutions in order of complexity.
(Short primer on complexity follows):O(n) or "big o" means worst case scenario where n means the number of elements in the array, and o(n) or "little o" means best case scenario. Long discrete math story short, you only really have to worry about the worst case scenario, and make sure it's not n ^ 2 or n!. It's more a measure of change in computing time as n increases than it is overall computing time. Wikipedia has a good article about computational aka time complexity.
If experience has taught me anything, it's that spending too much time optimizing your programs' little-o is a distinct waste of time better spent doing something - anything - better.
Solution 0: O(n) / o(1) complexity:
This solution has a best case scenario of 1 comparison - 1 iteration thru the loop, but only provided the matching value is in position 0 of the array. The worst case scenario is it's not in the array, and thus has to iterate over every element of the array.
foreach ($my_array as $sub_array) {
if (#$sub_array['id'] === 152) {
return true;
}
}
return false;
Solution 1: O(n) / o(n) complexity:
This solution must loop thru the entire array no matter where the matching value is, so it's always going to be n iterations thru the array.
return 0 < count(
array_filter(
$my_array,
function ($a) {
return array_key_exists('id', $a) && $a['id'] == 152;
}
)
);
Solution 2: O(n log n) / o(n log n) complexity:
A hash insertion is where the log n comes from; n hash insertions = n * log n. There's a hash lookup at the end which is another log n but it's not included because that's just how discrete math works.
$existence_hash = [];
foreach ($my_array as $sub_array) {
$existence_hash[$sub_array['id']] = true;
}
return #$existence_hash['152'];
I came upon this post looking to do the same and came up with my own solution I wanted to offer for future visitors of this page (and to see if doing this way presents any problems I had not forseen).
If you want to get a simple true or false output and want to do this with one line of code without a function or a loop you could serialize the array and then use stripos to search for the value:
stripos(serialize($my_array),$needle)
It seems to work for me.
As in your question, which is actually a simple 2-D array wouldn't it be better? Have a look-
Let say your 2-D array name $my_array and value to find is $id
function idExists($needle='', $haystack=array()){
//now go through each internal array
foreach ($haystack as $item) {
if ($item['id']===$needle) {
return true;
}
}
return false;
}
and to call it:
idExists($id, $my_array);
As you can see, it actually only check if any internal index with key_name 'id' only, have your $value. Some other answers here might also result true if key_name 'name' also has $value
I don't know if this is better or worse for performance, but here is an alternative:
$keys = array_map(function($element){return $element['id'];}, $my_array);
$flipped_keys = array_flip($keys);
if(isset($flipped_keys[40489]))
{
// true
}
You can create a queue of sub-arrays and loop each:
function existsKeyValue($myArray, $key, $value) {
$queue = [$myArray]; //creating a queue of a single element, which is our outermost array
//when we reach the count of the queue we looped all inner loops as well and failed to find the item
for ($index = 0; $index < count($queue); $index++) {
//Looping the current array, finding the key and the value
foreach ($queue[$index] as $k => &$v) {
//If they match the search, then we can return true
if (($key === $k) && ($value === $v)) {
return true;
}
//We need to make sure we did not already loop our current array to avoid infinite cycles
if (is_array($v)) $queue[]=$v;
}
}
return false;
}
$my_array = array(
0 => array(
"name" => "john",
"id" => 4
),
1 => array(
"name" => "mark",
"id" => 152
),
2 => array(
"name" => "Eduard",
"id" => 152
)
);
echo var_dump(existsKeyValue($my_array, 'id', 152));
array_column returns the values of a single column of an array and we can search for a specific value for those via in_array
if (in_array(152, array_column($my_array, 'id'))) {
echo 'FOUND!';
} else {
echo 'NOT FOUND!';
}
Try with this below code. It should be working fine for any kind of multidimensional array search.
Here you can see LIVE DEMO EXAMPLE
function multi_array_search($search_for, $search_in) {
foreach ($search_in as $element) {
if ( ($element === $search_for) ){
return true;
}elseif(is_array($element)){
$result = multi_array_search($search_for, $element);
if($result == true)
return true;
}
}
return false;
}
You can use this with only two parameter
function whatever($array, $val) {
foreach ($array as $item)
if (isset($item) && in_array($val,$item))
return 1;
return 0;
}
different between isset vs array_key_exits
What's the difference between isset() and array_key_exists()?
different between == vs === How do the PHP equality (== double equals) and identity (=== triple equals) comparison operators differ?
function specificValue(array $array,$key,$val) {
foreach ($array as $item)
if (array_key_exits($item[$key]) && $item[$key] === $val)
return true;
return false;
}
function checkMultiArrayValue($array) {
global $test;
foreach ($array as $key => $item) {
if(!empty($item) && is_array($item)) {
checkMultiArrayValue($item);
}else {
if($item)
$test[$key] = $item;
}
}
return $test;
}
$multiArray = array(
0 => array(
"country" => "",
"price" => 4,
"discount-price" => 0,
),);
$test = checkMultiArrayValue($multiArray);
echo "<pre>"
print_r($test);
Will return array who have index and value
I wrote the following function in order to determine if an multidimensional array partially contains a certain value.
function findKeyValue ($array, $needle, $value, $found = false){
foreach ($array as $key => $item){
// Navigate through the array completely.
if (is_array($item)){
$found = $this->findKeyValue($item, $needle, $value, $found);
}
// If the item is a node, verify if the value of the node contains
// the given search parameter. E.G.: 'value' <=> 'This contains the value'
if ( ! empty($key) && $key == $needle && strpos($item, $value) !== false){
return true;
}
}
return $found;
}
Call the function like this:
$this->findKeyValue($array, $key, $value);

List values in multi-dimensional array in php

I have been working on this a while. I see multi-dimensional arrays in php are not that easy.
Here is my code:
while (list($key,$value) = each ($x))
{
Print "$key => $value\n<br>\n";
}
This works well to display the keys of the main array. what I get is :
visitors => Array
actions => Array
actions-average => Array
time-average => Array
pages-entrance => Array
What I want is the visitors and the value (number of visitors), value of actions, etc.
I want to then save the value in Mysql. Some I will have to convert from a string to and int or date.
I need to list one more level deep. But I cannot see how to do this.
--------------Added -----------
So what I have is an array of arrays. I need to step through each array.
did you try print_r ?
if you need more control over formatting then embedded loops as suggested by #Nick is the best option. Although it would be more natural and safer to use foreach loops rather than while.
foreach($x as $key => $value){
foreach( $value as $key2 => $value2){
print "$key $key2 => $value2\n<br />\n";
}
}
see PHP manual: each , there is a "caution" frame.
EDIT 1
I update sample code above for 2 day array.
It seems your array has more than 2 dimension. Then you should use recursion.
function my_print_r($x,$header="")
{
foreach($x as $key => $value){
if(is_array($value))
my_print_r($value,$header . $key . " " );
else
print "$header $key2 => $value2\n<br />\n";
}
}
Try loops like this code:
$arrA=array("a", "b", "c");
$arrB=array("x", "y", "z");
$x=array("visitors" => $arrA, "actions" => $arrB);
foreach($x as $key => $value)
{
foreach($value as $v)
echo "$key => $v<br>\n";
}
OUTPUT
visitors => a<br>
visitors => b<br>
visitors => c<br>
actions => x<br>
actions => y<br>
actions => z<br
the best way is var_dump($arr);
<?php
var_dump($_SERVER);
?>
with output that includes types, string length, and will iterate over objects as well.
Since you want to iterate over an array, give foreach a try:
foreach ($arr as $el)
{
// ... Work with each element (most useful for non associative arrays, or linear arrays)
}
// or
foreach ($arr as $key => $value)
{
// ... Work with $key and $value pairs (most useful for hashes/associative arrays)
}
/ / . . . here, we take variable $company for nested array and in this variable we put 2 employee id and its called multi-dimensional array and we take $company variable to assign the associative array with keys so we print the output with keys
$company=[
$emp=[1,"neha","employee",12000,30000],
$emp1=[2,"moni","manager",12000],
];
$company=["first" => $emp,"second" => $emp1];
foreach($company as $key => $value)
{
echo "$key ";
foreach($value as $v1){
echo "$v1";
}
}
output :-
employee ID name designation salary bonus
first 1 neha employee 12000 30000
second 2 moni manager 12000

PHP Change Array Keys

Is there a way to change all the numeric keys to "Name" without looping through the array (so a php function)?
[
0 => 'blabla',
1 => 'blabla',
2 => 'blblll',
// etc ...
]
If you have an array of keys that you want to use then use array_combine
Given $keys = array('a', 'b', 'c', ...) and your array, $list, then do this:
$list = array_combine($keys, array_values($list));
List will now be array('a' => 'blabla 1', ...) etc.
You have to use array_values to extract just the values from the array and not the old, numeric, keys.
That's nice and simple looking but array_values makes an entire copy of the array so you could have space issues. All we're doing here is letting php do the looping for us, not eliminate the loop. I'd be tempted to do something more like:
foreach ($list as $k => $v) {
unset ($list[$k]);
$new_key = *some logic here*
$list[$new_key] = $v;
}
I don't think it's all that more efficient than the first code but it provides more control and won't have issues with the length of the arrays.
No, there is not, for starters, it is impossible to have an array with elements sharing the same key
$x =array();
$x['foo'] = 'bar' ;
$x['foo'] = 'baz' ; #replaces 'bar'
Secondarily, if you wish to merely prefix the numbers so that
$x[0] --> $x['foo_0']
That is computationally implausible to do without looping. No php functions presently exist for the task of "key-prefixing", and the closest thing is "extract" which will prefix numeric keys prior to making them variables.
The very simplest way is this:
function rekey( $input , $prefix ) {
$out = array();
foreach( $input as $i => $v ) {
if ( is_numeric( $i ) ) {
$out[$prefix . $i] = $v;
continue;
}
$out[$i] = $v;
}
return $out;
}
Additionally, upon reading XMLWriter usage, I believe you would be writing XML in a bad way.
<section>
<foo_0></foo_0>
<foo_1></foo_1>
<bar></bar>
<foo_2></foo_2>
</section>
Is not good XML.
<section>
<foo></foo>
<foo></foo>
<bar></bar>
<foo></foo>
</section>
Is better XML, because when intrepreted, the names being duplicate don't matter because they're all offset numerically like so:
section => {
0 => [ foo , {} ]
1 => [ foo , {} ]
2 => [ bar , {} ]
3 => [ foo , {} ]
}
This is an example prefixing all the keys with an underscore.
We use array_combine to combine the array keys with the array values, but we first run an array_map function on the array keys, which takes a simple function that adds the prefix.
$prefix = '_';
$arr = array_combine(
array_map(function($v) use ($prefix){
return $prefix.$v;
}, array_keys($arr)),
array_values($arr)
);
See a live example here https://3v4l.org/HABl7
I added this for an answer to another question and seemed relevant. Hopefully might help someone that needs to change the value of the keys in an array. Uses built-in functions for php.
$inputArray = array('app_test' => 'test', 'app_two' => 'two');
/**
* Used to remap keys of an array by removing the prefix passed in
*
* Example:
* $inputArray = array('app_test' => 'test', 'app_two' => 'two');
* $keys = array_keys($inputArray);
* array_walk($keys, 'removePrefix', 'app_');
* $remappedArray = array_combine($keys, $inputArray);
*
* #param $value - key value to replace, should be from array_keys
* #param $omit - unused, needed for prefix call
* #param $prefix - prefix to string replace in keys
*/
function removePrefix(&$value, $omit, $prefix) {
$value = str_replace($prefix, '', $value);
}
// first get all the keys to remap
$keys = array_keys($inputArray);
// perform internal iteration with prefix passed into walk function for dynamic replace of key
array_walk($keys, 'removePrefix', 'app_');
// combine the rewritten keys and overwrite the originals
$remappedArray = array_combine($keys, $inputArray);
// see full output of comparison
var_dump($inputArray);
var_dump($remappedArray);
Output:
array(2) {
'attr_test' =>
string(4) "test"
'attr_two' =>
string(3) "two"
}
array(2) {
'test' =>
string(4) "test"
'two' =>
string(3) "two"
}
I think that he want:
$a = array(1=>'first_name', 2=>'last_name');
$a = array_flip($a);
$a['first_name'] = 3;
$a = array_flip($a);
print_r($a);
The solution to when you're using XMLWriter (native to PHP 5.2.x<) is using $xml->startElement('itemName'); this will replace the arrays key.
change array key name "group" to "children".
<?php
echo json_encode($data);
function array_change_key_name( $orig, $new, &$array ) {
foreach ( $array as $k => $v ) {
$res[ $k === $orig ? $new : $k ] = ( (is_array($v)||is_object($v)) ? array_change_key_name( $orig, $new, $v ) : $v );
}
return $res;
}
echo '<br>=====change "group" to "children"=====<br>';
$new = array_change_key_name("group" ,"children" , $data);
echo json_encode($new);
?>
result:
{"benchmark":[{"idText":"USGCB-Windows-7","title":"USGCB: Guidance for Securing Microsoft Windows 7 Systems for IT Professional","profile":[{"idText":"united_states_government_configuration_baseline_version_1.2.0.0","title":"United States Government Configuration Baseline 1.2.0.0","group":[{"idText":"security_components_overview","title":"Windows 7 Security Components Overview","group":[{"idText":"new_features","title":"New Features in Windows 7"}]},{"idText":"usgcb_security_settings","title":"USGCB Security Settings","group":[{"idText":"account_policies_group","title":"Account Policies group"}]}]}]}]}
=====change "group" to "children"=====
{"benchmark":[{"idText":"USGCB-Windows-7","title":"USGCB: Guidance for Securing Microsoft Windows 7 Systems for IT Professional","profile":[{"idText":"united_states_government_configuration_baseline_version_1.2.0.0","title":"United States Government Configuration Baseline 1.2.0.0","children":[{"idText":"security_components_overview","title":"Windows 7 Security Components Overview","children":[{"idText":"new_features","title":"New Features in Windows 7"}]},{"idText":"usgcb_security_settings","title":"USGCB Security Settings","children":[{"idText":"account_policies_group","title":"Account Policies group"}]}]}]}]}
Use array array_flip in php
$array = array ( [1] => Sell [2] => Buy [3] => Rent [4] => Jobs )
print_r(array_flip($array));
Array ( [Sell] => 1 [Buy] => 2 [Rent] => 3 [Jobs] => 4 )
I did this for an array of objects. Its basically creating new keys in the same array and unsetting the old keys.
public function transform($key, $results)
{
foreach($results as $k=>$result)
{
if( property_exists($result, $key) )
{
$results[$result->$key] = $result;
unset($results[$k]);
}
}
return $results;
}
<?php
$array[$new_key] = $array[$old_key];
unset($array[$old_key]);
?>
To have the same key I think they must be in separate nested arrays.
for ($i = 0; $i < count($array); $i++) {
$newArray[] = ['name' => $array[$i]];
};
Output:
0 => array:1 ["name" => "blabla"]
1 => array:1 ["name" => "blabla"]
2 => array:1 ["name" => "blblll"]
You could create a new array containing that array, so:
<?php
$array = array();
$array['name'] = $oldArray;
?>

Categories