Wildcard array loop in PHP - php

I have an array which contains a great deal of data, which has been generated from a JSON file. Most of the data is used to populateinput elements, but some of the keys contain hidden, default values to be used for calculations later.
The array looks something like this:
[name] => 'My App'
[default_E17] => 0.009
[default_H17] => 0.0236
[default_K17] => 50
[default_P17] => 0.0118
[default_E19] => 0.03
I want to loop over all default_* keys, and output them using HTML. Essentially, I want a foreach loop, but only for the keys whose format matches default_*. Does anyone know if this is possible?
Please note that the values in the array before [default_*] keys is variable length, so I cannot easily use an array_splice().

You use strpos($key, "default_") === 0 to show that it start with default_ and its not in the middle or end
$array = array();
$array['name'] = 'My App';
$array['default_E17'] = "0.009";
$array['default_H17'] = "0.0236";
$array['default_K17'] = "50";
$array['default_P17'] = "0.0118";
$array['default_E19'] = "0.03";
$array['E19_default_test'] = "1.03";
echo "<pre>";
* You can use foreach *
$list = array();
foreach ( $array as $key => $value ) {
if (strpos($key, "default_") === 0) {
$list[$key] = $value;
}
}
var_dump($list);
You can also use array_flip with array_filter
$array = array_flip(array_filter(array_flip($array),function($var) { return (strpos($var, "default_") === 0);}));
var_dump($array);
You can also use FilterIterator
class RemoveDefaultIterator extends FilterIterator {
public function accept() {
return (strpos($this->key(), "default_") === 0);
}
}
$list = new RemoveDefaultIterator(new ArrayIterator($array));
var_dump(iterator_to_array($list));
They would all Output
array
'default_E17' => string '0.009' (length=5)
'default_H17' => string '0.0236' (length=6)
'default_K17' => string '50' (length=2)
'default_P17' => string '0.0118' (length=6)
'default_E19' => string '0.03' (length=4)

foreach( $arr as $k => $v ) { //iterate the array
if( strpos($k, 'default_') !== FALSE ) //search if the key contains 'default_'
$default_values[] = $v; // if so, store the values for the 'default_' keys
}

Just iterate over your array
Foreach( $inputArray AS $key=>$val ) {
// check if key is the one we need
if( ... ) {
// it is - deal with it
}
}
depending of the keys you use if() can be simple substr() or regexp matching.

You can use a FilterIterator for this.
Its essetially the same as looping over the whole array, because that's the only way really, but it strips you from doing this in your productive loop, thus generating less noise.
Here's how:
class Default_FilterIterator extends FilterIterator
{
public function accept()
{
if (strpos($this->key(), 'default') === 0) {
return true;
}
return false;
}
}
$yourArray = array('default_stuff' => 'foo', 'otherstuff' => 'bar');
$filterdArray = new Default_FilterIterator(new ArrayIterator($yourArray));
foreach ($filteredArray as $key => $value) {
//deal only with default values here
}

foreach ($your_array as $key => $value) {
// check if the $key starst with 'default_'
if (substr($key, 0, 8) == "default_") {
//do your thing...
echo "<input type='text' value='" . $value . "'>";
}
}

Related

Convert a nested Array to Scalar and then reconvert the scalar to nested array

I have an array that looks like this.
$array = [
0 => 'abc',
1 => [
0 => 'def',
1 => 'ghi'
],
'assoc1' => [
'nassoc' => 'jkl',
'nassoc2' => 'mno',
'nassoc3' => '',
'nassoc4' => false
]
];
The $array can have numeric keys or be an assoc array or a mixed one. The level of nesting is not known. Also the values of the array can also be bool or null or an empty string ''
I need to able to convert this into a scalar array with key value pairs. And then later reconvert it back to the exact same array.
So the scalar array could look like
$arrayScalar = [
'0' => 'abc',
'1[0]' => 'def',
'1[1]' => 'ghi',
'assoc1[nassoc]' => 'jkl',
'assoc1[nassoc2]' => 'mno',
'assoc1[nassoc3]' => '',
'assoc1[nassoc4]' => false
];
And then later be able to get back to the initial $array.
I wrote a parser and it does not currently handle bool values correctly.
I have a feeling this is at best a super hacky method to do what I am after. Also I have been able to test it only so much.
function flattenNestedArraysRecursively($nestedArray, $parent = '', &$flattened = [])
{
$keys = array_keys($nestedArray);
if (empty($keys)) {
$flattened[$parent] = 'emptyarray';
} else {
foreach ($keys as $value) {
if (is_array($nestedArray[$value])) {
$reqParent = (!empty($parent)) ? $parent . '|!|' . $value : $value;
$this->flattenNestedArraysRecursively($nestedArray[$value], $reqParent, $flattened);
} else {
$reqKey = (!empty($parent)) ? $parent . '|!|' . $value : $value;
$flattened[$reqKey] = $nestedArray[$value];
}
}
}
return $flattened;
}
function reCreateFlattenedArray($flatArray): array
{
$arr = [];
foreach ($flatArray as $key => $value) {
$keys = explode('|!|', $key);
$arr = $this->reCreateArrayRecursiveWorker($keys, $value, $arr);
}
return $arr;
}
function reCreateArrayRecursiveWorker($keys, $value, $existingArr)
{
//Outside to Inside
$keyCur = array_shift($keys);
//Check if keyCur Exists in the existingArray
if (key_exists($keyCur, $existingArr)) {
// Check if we have reached the deepest level
if (empty($keys)) {
//Return the Key => value mapping
$existingArr[$keyCur] = $value;
return $existingArr;
} else {
// If not then continue to go deeper while appending deeper levels values to current key
$existingArr[$keyCur] = $this->reCreateArrayRecursiveWorker($keys, $value, $existingArr[$keyCur]);
return $existingArr;
}
} else {
// If Key does not exists in current Array
// Check deepest
if (empty($keys)) {
//Return the Key => value mapping
$existingArr[$keyCur] = $value;
return $existingArr;
} else {
// Add the key
$existingArr[$keyCur] = $this->reCreateArrayRecursiveWorker($keys, $value, []);
return $existingArr;
}
}
}
Is there a better more elegant way of doing this, maybe http_build_query or something else I am not aware of.
Sandbox link -> http://sandbox.onlinephpfunctions.com/code/50b3890e5bdc515bc145eda0a1b34c29eefadcca
Flattening:
Your approach towards recursion is correct. I think we can make it more simpler.
We loop over the array. if the value is an array in itself, we recursively make a call to this new child subarray.
This way, we visit each key and each value. Now, we are only left to manage the keys to assign them when adding to our final resultant array, say $arrayScalar.
For this, we make a new function parameter which takes the parent key into account when assigning. That's it.
Snippet:
$arrayScalar = [];
function flatten($array,&$arrayScalar,$parent_key){
foreach($array as $key => $value){
$curr_key = empty($parent_key) ? $key : $parent_key . '[' . $key . ']';
if(is_array($value)){
flatten($value,$arrayScalar,$curr_key);
}else{
$arrayScalar[$curr_key] = $value;
}
}
}
flatten($array,$arrayScalar,'');
var_export($arrayScalar);
Demo: http://sandbox.onlinephpfunctions.com/code/1e3092e9e163330f43d495cc9d4acb672289a987
Unflattening:
This one is a little tricky.
You might have already noticed that the keys in the flattened array are of the form key1[key2][key3][key4] etc.
So, we collect all these individually in a new array, say $split_key. It might look like this.
array (
'key1',
'key2',
'key3',
'key4',
)
To achieve the above, we do a basic string parsing and added in-between keys to the array whenever we reach the end of the key string or [ or ].
Next, to add them to our final resultant array, we loop over the collected keys and check if they are set in our final array. If not so, set them. We now pass child array reference to our temporary variable $temp. This is to edit the same copy of the array. In the end, we return the result.
Snippet:
<?php
function unflatten($arrayScalar){
$result = [];
foreach($arrayScalar as $key => $value){
if(is_int($key)) $key = strval($key);
$split_key = [];
$key_len = strlen($key);
$curr = '';
// collect them as individual keys
for($i = 0; $i < $key_len; ++$i){
if($key[ $i ] == '[' || $key[ $i ] == ']'){
if(strlen($curr) === 0) continue;
$split_key[] = $curr;
$curr = '';
}else{
$curr .= $key[ $i ];
}
if($i === $key_len - 1 && strlen($curr) > 0){
$split_key[] = $curr;
}
}
// collecting them ends
//add them to our resultant array.
$temp = &$result;
foreach($split_key as $sk){
if(!isset($temp[ $sk ])){
$temp[ $sk ] = [];
}
$temp = &$temp[$sk];
}
$temp = $value;
}
return $result;
}
var_export(unflatten($arrayScalar));
Demo: http://sandbox.onlinephpfunctions.com/code/66136a699c3c5285eed3d3350ed4faa5bbce4b76

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.

php multi array create dynamically

I have q question: what is the easiest way to create multi-dimensional array in php dynamically?
Here a static version:
$tab['k1']['k2']['k3'] = 'value';
I would like to avoid eval()
I'm not successful with variable variable ($$)
so I'm trying to develop a function fun with such interface:
$tab = fun( $tab, array( 'k1', 'k2', 'k3' ), 'value' );
Do you have a solution? What is the simplest way?
regards,
Annie
There are a number of ways to achieve this, but here is one which uses PHP's ability to have N arguments passed to a function. This gives you the flexibility of creating an array with a depth of 3, or 2, or 7 or whatever.
// pass $value as first param -- params 2 - N define the multi array
function MakeMultiArray()
{
$args = func_get_args();
$output = array();
if (count($args) == 1)
$output[] = $args[0]; // just the value
else if (count($args) > 1)
{
$output = $args[0];
// loop the args from the end to the front to make the array
for ($i = count($args)-1; $i >= 1; $i--)
{
$output = array($args[$i] => $output);
}
}
return $output;
}
Here's how it would work:
$array = MakeMultiArray('value', 'k1', 'k2', 'k3');
And will produce this:
Array
(
[k1] => Array
(
[k2] => Array
(
[k3] => value
)
)
)
Following function will work for any number of keys.
function fun($keys, $value) {
// If not keys array found then return false
if (empty($keys)) return false;
// If only one key then
if (count($keys) == 1) {
$result[$keys[0]] = $value;
return $result;
}
// prepare initial array with first key
$result[array_shift($keys)] = '';
// now $keys = ['key2', 'key3']
// get last key of array
$last_key = end($keys);
foreach($keys as $key) {
$val = $key == $last_key ? $value : '';
array_walk_recursive($result, function(&$item, $k) use ($key, $val) {
$item[$key] = $val;
});
}
return $result;
}
This should work if $tab always has 3 indices:
function func(&$name, $indices, $value)
{
$name[$indices[0]][$indices[1]][$indices[2]] = $value;
};
func($tab, array( 'k1', 'k2', 'k3' ), 'value' );

Iterate a PHP array from a specific key

I know how to iterate an array in PHP, but I want to iterate an array from a specific key.
Assume that I have a huge array
$my_array = array(
...
...
["adad"] => "value X",
["yy"] => "value Y",
["hkghgk"] => "value Z",
["pp"] => "value ZZ",
...
...
)
I know the key where to start to iterate ("yy"). Now I want to iterate only from this key to another key.
I know that I don't want to do this:
$start_key = "yy";
foreach ($my_array as $key => $v)
{
if ($key == $start_key)
...
}
I was looking for Iterator, but I don't think this is what I need.
Try combining array_search, array_key, and LimitIterator. Using the example from the LimitIterator page and some extra bits:
$fruitsArray = array(
'a' => 'apple',
'b' => 'banana',
'c' => 'cherry',
'd' => 'damson',
'e' => 'elderberry'
);
$startkey = array_search('d', array_keys($fruitsArray));
$fruits = new ArrayIterator($fruitsArray);
foreach (new LimitIterator($fruits, $startkey) as $fruit) {
var_dump($fruit);
}
Starting at position 'd', this outputs:
string(6) "damson" string(10) "elderberry"
There is a limit to this approach in that it won’t loop around the array until the start position again. It will only iterate to the end of an array and then stop. You would have to run another foreach to do the first part of the array, but that can be easily done with the code we already have.
foreach (new LimitIterator($fruits, 0, $startkey-1) as $fruit) {
var_dump($fruit);
}
This starts from the first element, up to the element before the one we searched for.
foreach always resets the array's array pointer. You just can't do that the way you imagine.
You still have a few ways. The foreach way is just skipping everything until you found the key once:
$start_key = "yy";
$started = false;
foreach ($my_array as $key => $v)
{
if ($key == $start_key) {
$started = true;
}
if (!$started) {
continue;
}
// your code
}
You could as well work with the array pointer and use the while (list($key, $v) = each($array)) method:
$start_key = "yy";
reset($array); // reset it to be sure to start at the beginning
while (list($key, $v) = each($array) && $key != $start_key); // set array pointer to $start_key
do {
// your code
} while (list($key, $v) = each($array));
Alternatively, you can just extract the array you want to iterate over like MarkBaker proposed.
Perhaps something like:
foreach(array_slice(
$my_array,
array_search(
$start_key,array_keys($my_array)
),
null,
true) as $key => $v) {}
Demo
You can use array_keys and array_search.
Like this:
$keys = array_keys( $my_array ); // store all of your array indexes in a new array
$position = array_search( "yy" ,$keys ); // search your starting index in the newly created array of indexes
if( $position == false ) exit( "Index doesn't exist" ); // if the starting index doesn't exist the array_search returns false
for( $i = $position; $i < count( $keys ); $i++ ) { // starting from your desired index, this will iterate over the rest of your array
// do your stuff to $my_array[ $keys[ $i ] ] like:
echo $my_array[ $keys[ $i ] ];
}
Try it like this:
$startkey = array_search('yy', array_keys($my_array));
$endkey = array_search('zz', array_keys($my_array));
$my_array2 = array_values($my_array);
for($i = $startkey; $i<=$endkey; $i++)
{
// Access array like this
echo $my_array2[$i];
}
If this pull request makes it through, you will be able to do this quite easily:
if (seek($array, 'yy', SEEK_KEY)) {
while ($data = each($array)) {
// Do stuff
}
}

php array keys problem

I have the following code:
$rt1 = array
(
'some_value1' => 'xyz1',
'some_value2' => 'xyz2',
'value_1#30'=>array('0'=>1),
'value_2#30'=>array('0'=>2),
'value_3#30'=>array('0'=>3),
'value_1#31'=>array('0'=>4),
'value_2#31'=>array('0'=>5),
'value_3#31'=>array('0'=>6),
'some_value3' => 'xyz3',
'some_value4' => 'xyz4',
);
$array_30 = array
(
'0'=>1,
1=>'2',
2=>'3'
);
$array_31 = array
(
'0'=>4,
'1'=>'5',
'2'=>'6'
);
I need to make it an array and insert the array_30 and array_31 into a DB.
foreach($rt1 as $value){
$rt2[] = $value['0'];
}
The question was updated, so here is an updated answer. Quick check, you should really try and update this to whatever more generic purpose you have, but as a proof of concept, a runnable example:
<?php
$rt1 = array
(
'some_value1' => 'xyz1',
'some_value2' => 'xyz2',
'value_1#30'=>array('0'=>1),
'value_2#30'=>array('0'=>2),
'value_3#30'=>array('0'=>3),
'value_1#31'=>array('0'=>4),
'value_2#31'=>array('0'=>5),
'value_3#31'=>array('0'=>6),
'some_value3' => 'xyz3',
'some_value4' => 'xyz4',
);
$finalArrays = array();
foreach($rt1 as $key=>$value){
if(is_array($value)){
$array_name = "array_".substr($key,-2);
${$array_name}[] = $value['0'];
}
}
var_dump($array_30);
var_dump($array_31);
?>
will output the two arrays with the numbers 1,2,3 and 4,5,6 respectivily
i assume you want to join the values of each of the second-level arrays, in which case:
$result = array();
foreach ($rt1 as $arr) {
foreach ($arr as $item) {
$result[] = $item;
}
}
Inspired by Nanne (which reminded me of dynamically naming variables), this solution will work with every identifier after the \#, regardless of its length:
foreach ( $rt1 as $key => $value )
{
if ( false == strpos($key, '#') ) // skip keys without #
{
continue;
}
// the part after the # is our identity
list(,$identity) = explode('#', $key);
${'array_'.$identity}[] = $rt1[$key]['0'];
}
Presuming that this is your actual code, probably you will need to copy the array somewhere using foreach and afterwards create the new array as you wish:
foreach($arr as $key => $value) {
$arr[$key] = 1;
}

Categories