Use array to select array keys recursively - php

I am having a bit of trouble trying to explain this correctly, so please bear with me...
I need to be able to recursively select keys based on a given array. I can do this via a fairly simple foreach statement (as shown below). However, I prefer to do things via PHP's built in functions whenever possible.
$selectors = array('plants', 'fruits', 'apple');
$list = array(
'plants' => array(
'fruits' => array(
'apple' => 'sweet',
'orange' => 'sweet',
'pear' => 'tart'
)
)
);
$select = $list;
foreach ($selectors as $selector) {
if (isset($select[$selector])) {
$select = $select[$selector];
} else {
exit("Error: '$selector' not found");
}
}
echo $select;
See this code in action
My Question: Is there a PHP function to recursively select array keys? If there is not, is there a better way than in the example above?

If i understand , you are searching about :
http://php.net/recursivearrayiterator
and
http://php.net/recursiveiteratoriterator
And code something like that:
$my_itera = new RecursiveIteratorIterator(new RecursiveArrayIterator($my_array));
$my_keys = array();
foreach ($my_itera as $my_key => $value) {
for ($i = $my_itera->getDepth() - 1; $i >= 0; $i--) {
$my_key = $my_itera->getSubIterator($i)->key() . '_' . $my_key;
}
$my_keys[] = $my_key;
}
var_export($my_keys);
I Hope it works.

Related

Avoid Nested Loop in PHP

I am writing a method which takes an array of $topicNames and an array of $app and concatenates each $app to $topicNames like the following
public function getNotificationTopicByAppNames(array $topicNames, array $apps)
{
$topics = [];
foreach ($topicNames as $topicName) {
foreach ($apps as $app) {
$topic = $app . '_' . $topicName;
$topics[] = $topic;
}
}
return $topics;
}
}
The input and result are like the following...
$topicNames = [
'one_noti',
'two_noti',
'three_noti'
];
$apps = [
'one_app',
'two_app'
];
// The return result of the method will be like the following
[
'one_app_one_noti',
'two_app_one_noti',
'one_app_two_noti',
'two_app_two_noti',
'one_app_three_noti',
'two_app_three_noti'
]
My question is instead of doing nested loops, is there any other way I can do? Why do I want to avoid nested loops? Because currently, I have $topic. Later, I might want to add languages, locations etc...
I know I can use map, reduce, array_walks, each those are basically going through one by one. Instead of that which another alternative way I can use? I am okay changing different data types instead of the array as well.
If you dont care about the order you can use this
function getNotificationTopicByAppNames(array $topicNames, array $apps)
{
$topics = [];
foreach($apps as $app){
$topics = array_merge($topics, preg_filter('/^/', $app.'_', $topicNames));
}
return $topics;
}
print_r(getNotificationTopicByAppNames($topicNames,$apps));
Output
Array
(
[0] => one_app_one_noti
[1] => one_app_two_noti
[2] => one_app_three_noti
[3] => two_app_one_noti
[4] => two_app_two_noti
[5] => two_app_three_noti
)
Sandbox
You can also switch loops and use the $ instead to postfix instead of prefix. Which turns out to be in the same order you had. I thought of prefixing as a way to remove the loop. Then i thought why not flip it.
function getNotificationTopicByAppNames(array $topicNames, array $apps)
{
$topics = [];
foreach($topicNames as $topic){
$topics = array_merge($topics, preg_filter('/$/', '_'.$topic, $apps));
}
return $topics;
}
print_r(getNotificationTopicByAppNames($topicNames,$apps));
Output
Array
(
[0] => one_app_one_noti
[1] => two_app_one_noti
[2] => one_app_two_noti
[3] => two_app_two_noti
[4] => one_app_three_noti
[5] => two_app_three_noti
)
Sandbox
The trick here is using preg_filter.
http://php.net/manual/en/function.preg-filter.php
preg_filter — Perform a regular expression search and replace
So we search with ^ start or $ end which doesn't capture anything to replace and then we just add on what we want. I've used this before when I wanted to prefix a whole array with something, etc.
I couldn't test it in a class, so I made it a regular function, so adjust as needed.
Cheers!
You can use :
<?php
public function mergeStacks(...$stacks)
{
$allStacks = call_user_func_array('array_merge', $stacks);
return $this->concatString($allStacks);
}
private function concatString(&$stack, $index = 0, &$result = [])
{
if(count($stack) == 0){
return '';
}
if($index == count($stack)){
return $result;
}
array_walk($stack, function($value, $key) use($index, &$result, $stack){
if($key > $index){
array_push($result, $stack[$index] . '_' . $value);
}
});
$index = $index + 1;
return $this->concatString($stack, $index, $result);
}
And then when you want to get the array, no matter if you have languages or topics etc, you can just do :
$this->mergeStacks($languages, $topics, $locations, .....);
Where $languages, $topics, $locations are simple arrays.
Instead of accepting only topics name parameter try something like this:
function getNotificationTopicByAppNames(array $apps, array ...$names)
{
$topics = [];
foreach ($names as $nameArray) {
foreach ($nameArray as $topicName) {
foreach ($apps as $app) {
$topic = $app . '_' . $topicName;
$topics[] = $topic;
}
}
}
return $topics;
}
$topicNames = [
'one_noti',
'two_noti',
'three_noti'
];
$languagesNames = [
'test_en',
'test_other',
'test_other2'
];
$apps = [
'one_app',
'two_app'
];
print_r(getNotificationTopicByAppNames($apps,$topicNames,$languagesNames));
you can pass any number of arrays to array.

How can I make HTML data attributes string from a nested array in PHP?

Some time ago I had to parse nested data attributes to a JSON, so I found a JS solution here on SO. Eg.:
data-title="Title" data-ajax--url="/ajax/url" data-ajax--timeout="10" data-ajax--params--param-1="Param 1"
to
['title' => 'Title', 'ajax' => ['url' => '/ajax/url', 'timeout' => 10, 'params' => ['param-1' => 'Param 1']]]
So now I need a reverse action in PHP. I need to make attributes string from nested array to use it later in HTML. There can be infinite levels.
I've tried recursive functions. Tried recursive iterators. Still no luck. I always lose top level keys and get something like data-ajax--url=[...] --timeout=[...] --param-1=[...] (missing -ajax part) and so on. The part I can't get is the keys - getting values is easy. Any advice would be welcome.
This can be achieved with some simple concepts like loop,
recursive function and static variable.
Use of static variables is very important here since they remember the last modified value within the function's last call.
Within the loop, we are checking if the currently traversed value is an array.
If it's an array, we are modifying the prefix with the current key and calling the recursive function and .
If not, we are simply concatenating the prefix with the present key.
Try this:
$data = ['title' => 'Title', 'ajax' => ['url' => '/ajax/url', 'timeout' => 10, 'params' => ['param-1' => 'Param 1']]];
function formatter($data = array()) {
static $prefix = 'data-';
static $attr_string = '';
foreach($data as $key => $value) {
if (is_array($value)) {
$prefix .= $key.'--';
formatter($value);
} else {
$attr_string .= $prefix.$key.'="'.$value.'" ';
}
}
return $attr_string;
}
echo formatter($data);
Output:
data-title="Title" data-ajax--url="/ajax/url" data-ajax--timeout="10" data-ajax--params--param-1="Param 1"
So after almost 6 hours of trying to figure this out and lots of searches, also with some hints from Object Manipulator, I found this answer on SO and just had to adapt it to my needs:
function makeDataAttributes(array $attributes)
{
$rs = '';
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($attributes));
foreach ($iterator as $key => $value) {
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
$key = $iterator->getSubIterator($i)->key() . '--' . $key;
}
$rs .= ' data-' . $key . '="' . $iterator->current() . '"';
}
return trim($rs);
}
Thanks everyone for your comments. It helped me to define my search more clearly. Also got some new knowledge about iterators.

Get node from php array by one of the fields

I have an array with structure generated like this:
$groups = array();
while ($group = mysql_fetch_array($groups_result)) {
$groups[] = array( 'id' => $group['id'], 'name' => $group['name']);
}
How can I later in the code get the name of the group by its id? For example, I would like a function like:
function get_name_by_id($id, $array);
But I'm looking for some solution which is already implemented in PHP. I know it would be easier to make arrays with array[5] = array('name' => "foo") etc where 5 in this case is id, but in my code there is a lot of arrays already created like i mentioned above and I cannot easily switch it.
$groups = array();
while ($group = mysql_fetch_assoc($groups_result)) {
$groups[$group['id']] = array( 'name' => $group['name']);
}
$name = $groups['beer']['name'];
also please not using fetch_assoc is more efficient than fetch array
function get_name_by_id($id, $data) {
foreach($data as $d) {
if ($d['id'] == $id) {
return $d['name'];
}
}
// return something else, id was not found
}

php foreach supplied argument not valid

I am getting results from a mysql table and putting each cell into an array as follows:
$sqlArray = mysql_query("SELECT id,firstName FROM members WHERE id='$id'");
while ($arrayRow = mysql_fetch_array($sqlArray)) {
$friendArray[] = array(
'id' => $arrayRow['id'],
'firstName' => $arrayRow['firstName'],
);
}
Then I do a search for a specific friend. For example if I want to search for a friend name Osman, i would type and o and it will return to me all the results that start with the letter o. Here is my code for that:
function array_multi_search($array, $index, $pattern, $invert = FALSE) {
$output = array();
if (is_array($array)) {
foreach($array as $i => $arr) {
// The index must exist and match the pattern
if (isset($arr[$index]) && (bool) $invert !== (bool) preg_match($pattern, $arr[$index])) {
$output[$i] = $arr;
}
}
}
return $output;
}
$filtered = array_multi_search($friendArray, 'firstName', '/^o/i');
and then it will print out all the results. My problem is that it returned an error saying "Invalid argument supplied to foreach()" and that is why I added the if(is_array)) condition. It is working fine if I leave this code in the index.php page, but I moved it to a subfolder named phpScripts and it doesn't work there. Any Help?
$output is not returning any value because apparently $friendArray is not an array. But I verified that it is by using print_r($friendArray) and it returns all the member's id and firstName.
P.S. I use JavaScript to the the call using AJAX.
If your array structure is such:
$friendArray[] = array(
'id' => $arrayRow['id'],
'firstName' => $arrayRow['firstName'],
);
This means your array is indexed and two levels.
So the correct way to walk through it is:
foreach($array as $cur_element) {
$id = $cur_element['id'];
$firstName = $cur_element['firstName'];
}
Change this:
foreach($array as $i => $arr) {
To this:
foreach((array)$array as $i => $arr) {
Are you sure that $array is not empty?

PHP - Help building a multi dimensional array

I would like to know how to get the values into this array. Can someone please help?
Each box whether it is the in or outbox should only be listed once and then have multiple ids associated with them. I need to be able to tell which id came from what box. The ids that are in the array are only samples.
$arr =
array(
'Inbox'=> array('id' => array(8, 9, 15)),
'Outbox'=> array('id' => array(8, 9, 15))
);
Thanks
$inbox = $db->Query("SELECT * FROM mail_inbox");
$outbox = $db->Query("SELECT * FROM mail_outbox");
foreach($inbox as $key => $array)
{
$output['Inbox']]['id'][] = $array['msg_seq'];
}
foreach($outbox as $key => $array)
{
$output['Outbox']]['id'][] = $array['msg_seq'];
}
print_r($output);
This will give me the db fields from the inbox but I have no idea how to get the outbox in there as well. I also get undefined index for ['Box']
Now that I know what you are saying, to input stuff, do something like this to input it into the array:
$ID = 9;
$box = "Inbox";
$arr[$box]['id'][] = $ID;
or
$IDs = array(9,5,13);
$box = "Inbox";
$array = array($box => $IDs);
or if you were getting it from a Database
$dbarray[0] = array('ID' => 9,
'Box' => 'Outbox');
foreach($dbarray as $key => $array)
{
$output[$array['Box']]['ids'][] = $array['ID'];
}
Multi deminsional arrays
The key or index is the first bracket
$array[key]="foo"
is the same as
$array = array('key' => 'foo');
if there is a second bracket, it of the array inside the value part of an array. IE
$array['key']['key2'] = "bar";
is the same as
$array = array('key' => array('key2' => 'bar'));
Basically, multideminsional arrays are just arrays inside of arrays.
foreach($arr as $box => $array)
{
echo $box;
// $box = The Box
foreach($array['ids'] as $ID)
{
echo $ID . ",";
// $ID = The ID
}
echo "<br>";
}
Sample:
Outbox 9,13,15,
Inbox 9,13,15,
This goes through each box, echos the box name, and each ID inside of the box, and echos the ID.
To access only one box
foreach($arr['Inbox'] as $ID)
{
echo $ID . ",";
}
Sample Output:
9,13,15,

Categories