I have a simple Template engine and I have a problem giving it a data from assoc array. Can anybody give me an advice? In method getStatisticData, I give an assoc array as first variable $data. My input array is in the form:
[0] => Array
(
[OrderNumber] => 1
[Name] => Zahid
[Total revenue] => 8363.38
)
I'm trying to get data from it using foreach but it doesn't work. I'm getting notice
Notice: Array to string conversion in C:\Server\htdocs\Task\lib\TemplateGen.php on line 34
protected function getStatisticData($data, $template){
$text = "";
if($data === false) {
return "We don't have any data in database";
}
foreach($data as $key => $value){
$data[$key] = $value;
$text .= $this->template_gen->getReplaceTemplate($data ,$template);
}
return $text;
}
getReplaceContent and associated methods from TemplateGen.php:
private function getReplaceContent($dataString, $content)
{
$search = array();
$replace = array();
$i = 0;
foreach ($dataString as $key => $value) {
$search[$i] = "%$key%";
$replace[$i] = $value;
$i++;
}
return str_replace($search, $replace, $content); ## LINE 34
}
function getReplaceTemplate($dataString, $template)
{
return $this->getReplaceContent($dataString, $this->getTemplate($template));
}
function getTemplate($name)
{
return $content = file_get_contents($this->config->tpl_path . $name . ".tpl");
}
UPDATE:
I got 2 new errors
Warning: implode(): Invalid arguments passed in C:\Server\htdocs\Task\lib\TemplateGen.php on line 28
Fatal error: Cannot redeclare add_percent() (previously declared in C:\Server\htdocs\Task\lib\TemplateGen.php:25) in C:\Server\htdocs\Task\lib\TemplateGen.php on line 25
Line 25:
function add_percent($i) {
Line 28:
return implode("", str_replace(array_map("add_percent", array_keys($dataString)), array_values($dataString),$content));
UPDATE2:
In theory, everything provided in new method should work very well. But there are same problems which were at the beginning
Notice: Array to string conversion in C:\Server\htdocs\Task\lib\TemplateGen.php on line 44`
line 44 :
return str_replace(
array_map($addPercent, array_keys($data)), array_values($data), $template
);
But if I'am using my getStatisticData instead of yours, it works but there are many other errors
My method :
protected function getStatisticData($data, $template){
$text = array();
if($data === false) {
return "We don't have any data in database";
}
$i=0;
foreach($data as $dataString){
if (!empty($data[$i+1])){
foreach($dataString as $key => $value){
$dataString[$i][$key] = $dataString[$i][$value];
}}
$text .= $this->template_gen->getReplaceTemplate($dataString ,$template);
}
return $text;
}
Your code is rather inefficient at present, and it's leading to some isuues. Let's look at the first method you posted:
protected function getStatisticData($data, $template){
$text = "";
if($data === false) {
return "We don't have any data in database";
}
foreach($data as $key => $value){
$data[$key] = $value;
$text .= $this->template_gen->getReplaceTemplate($data ,$template);
}
return $text;
}
The foreach loop is doing the same thing for each key/value pair (plus $data[$key] = $value is redundant as you are already getting each key and value in the foreach loop), so you could eliminate the loop, and replace it with something like this:
protected function getStatisticData($data, $template){
if ($data === false) {
return "We don't have any data in database";
}
return $this->template_gen->getReplaceTemplate($data, $template);
}
Similarly for getReplaceContent - you're basically using the array keys and array values as the search and the replacement values. You can use PHP's handy array_keys and array_values instead of building new arrays. The catch is that the array keys need to be surrounded by %, but that is easy to do using array_map - just define a function that will take in a string and add % to either end of it, and then array_map it to your array keys:
private function getReplaceContent($data, $template)
{
$addPercent = function( $i ){
return "%$i%";
};
return str_replace(
array_map( $addPercent, array_keys($data)), array_values($data), $template
);
}
Now, calling getStatisticData will return the template text with all the replaced data in it.
Sample input:
$arr = array(
'OrderNumber' => 1,
'Name' => 'Zahid',
'Total revenue' => '8363.38'
);
$template =
'<p>Name: %Name%<br>
Total revenue: %Total revenue%<br>
Order number: %OrderNumber%</p>';
Output of getStatisticData:
<p>Name: Zahid<br>
Total revenue: 8363.38<br>
Order number: 1</p>
EDIT: it is not clear from the OP what the input to getStatisticData is, but it looks like it is supposed to be an array of associative arrays. If this is so, the code for getStatisticData should be altered as follows:
protected function getStatisticData($dataArray, $template){
if ($dataArray === false) {
return "We don't have any data in database";
}
$text = "";
foreach ($dataArray as $aa) {
$text .= $this->template_gen->getReplaceTemplate($aa, $template);
}
return $text;
}
Sample input:
$arr =
[
[ 'OrderNumber' => 1,
'Name' => 'Zahid',
'Total revenue' => '8363.38'
],
[ 'OrderNumber' => 2,
'Name' => 'Paul',
'Total revenue' => '123.45'
],
[ 'OrderNumber' => 3,
'Name' => 'Jane',
'Total revenue' => '567.89'
],
];
Output:
<p>Name: Zahid<br>
Total revenue: 8363.38<br>
Order number: 1</p>
<p>Name: Paul<br>
Total revenue: 123.45<br>
Order number: 2</p>
<p>Name: Jane<br>
Total revenue: 567.89<br>
Order number: 3</p>
I'm assuming that the associative array $data has simply strings in it. I'm assuming that the $data array has its keys as the template placeholder and the value as the value to input.
If so, and if you're trying to get a single string, then consider doing this:
protected function getStatisticData($data, $template) {
if ($data === false) {
return "We don't have any data in the database.";
}
return this->template_gen->getReplaceTemplate($data, $template);
}
private function getReplaceContent($dataString, $content) {
content = '';
foreach ($dataString as $key => $value) {
$content .= str_replace("%{$key}%", $value, $content);
}
return $content;
}
You don't need quite as many foreach loops. One should do just fine.
Related
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
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.
I'm trying to search in multidimensional array and get the value but return doesn't work.
function search($arr,$q){
foreach($arr as $key => $val){
if(trim($key) == $q) return $arr;
else if(is_array($val)) search($val,$q);
}
}
But echo and print work.
Where is the problem?
Not sure you've solved it already, but this is a solution that might help you on your way:
<?php
$arr = ['firstname' => 'John', 'lastname' => 'Dough', 'jobs' => ['job1' => 'writer', 'job2' => 'dad']];
$result = null; // the container for the search result
$key = 'job1'; // the key we are looking for
findKey($arr, $key, $result); // invoke the search function
/** print the result of our search - if key not found, outputs 'NULL' */
echo '<pre>';
var_dump($result); // output: string(6) "writer"
echo '<pre>';
/**
* #param array $arr the multidimensional array we are searching
* #param string $key the key we are looking for
* #param $result passed by reference - in case the key is found, this variable 'stores' the corresponding value.
*/
function findKey($arr = [], $key = '', &$result)
{
foreach ($arr as $key0 => $value0) {
if($key0 == $key) {
$result = $value0;
}
if(is_array($value0)) {
findKey($value0, $key, $result);
}
}
return false;
}
I am trying to make first array value to uppercase.
Code:
$data = $this->positions_model->array_from_post(array('position', 'label'));
$this->positions_model->save($data, $id);
So before save($data, $id) to database I want to convert position value to uppercase. I have tried by this
$data['position'] = strtoupper($data['position']);
but than it is not storing the value in db with uppercase but as it is what user inputs.
Current output of $data:
Array ( [position] => it [label] => Information Technology )
And I want it in uppercase as IT
Added Model Method
public function get_positions_array($id = NULL, $single = FALSE)
{
$this->db->get($this->_table_name);
$positions = parent::get($id, $single);
$array = array();
foreach($positions as $pos){
$array[] = get_object_vars($pos);
}
return $array;
}
Main MY_Model method
public function array_from_post($fields)
{
$data = array();
foreach ($fields as $field) {
$data[$field] = $this->input->post($field);
}
return $data;
}
This should work:
$data = $this->positions_model->array_from_post(array('position', 'label'));
$data['position'] = strtoupper($data['position']);
$this->positions_model->save($data, $id);
If Its not, then $data array have only read attribute.
The array_from_post() method returns an array with the format below:
$data = array(
'position' => 'it',
'label' => 'Information Technology'
);
So, you could make first value of the array to uppercase, by using array_map or array_walk functions as follows:
$data = array_map(function($a) {
static $i = 0;
if ($i === 0) { $i++; return strtoupper($a); }
else return $a;
}, $array);
Note: This only works on PHP 5.3+, for previous versions, use the function name instead.
Here is the array_walk example, which modifies the $data:
array_walk($data, function(&$value, $key) {
static $i = 0;
if ($i == 0) { $i++; $value = strtoupper($value); }
});
Again, if you're using PHP 5.2.x or lower, you could pass the function name instead.
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?