Refactoring PHP code - reducing number of foreach statements - php

as you can see in my code below, I'm using the same code (foreach loop with array assignment and trailing json_decode) three times for three variables that are structured similarly. I'm wondering how I can optimize my code so that it doesn't repeat functions unnecessarily. Would variable variables be helpful in this situation? Can I move the recurring lines of code to inside the first foreach statement?
Here's what my code looks like now:
date_default_timezone_set('America/Los_Angeles');
$stocks = array('MSFT' => 'http://ichart.finance.yahoo.com/table.csv?s=MSFT', 'AAPL' => 'http://ichart.finance.yahoo.com/table.csv?s=AAPL', 'FB' => 'http://ichart.finance.yahoo.com/table.csv?s=FB', 'ZNGA' => 'http://ichart.finance.yahoo.com/table.csv?s=ZNGA');
foreach ($stocks as $key=>$stock) {
$fh = fopen($stock, 'r');
$header = fgetcsv($fh);
$varname = $key . '_data';
$$varname = array();
while ($line = fgetcsv($fh)) {
${$varname}[count($$varname)] = array_combine($header, $line);
}
fclose($fh);
}
foreach($MSFT_data as $val){
$MSFT[] = array((strtotime($val['Date']) * 1000), ((float)$val['Close']));
}
$MSFT = json_encode(array_reverse($MSFT));
foreach($AAPL_data as $val){
$AAPL[] = array((strtotime($val['Date']) * 1000), ((float)$val['Close']));
}
$AAPL = json_encode(array_reverse($AAPL));
foreach($FB_data as $val){
$FB[] = array((strtotime($val['Date']) * 1000), ((float)$val['Close']));
}
$FB = json_encode(array_reverse($FB));
Thanks. Let me know if you have any questions.

For the three loops, try:
function dateCloseLoop($data) {
foreach($data as $val){
$tmp[] = array((strtotime($val['Date']) * 1000), ((float)$val['Close']));
}
return json_encode(array_reverse($tmp));
}
So your code would be something like:
$MSFT = dateCloseLoop($MSFT_data);
$AAPL = dateCloseLoop($AAPL_data);
$FB = dateCloseLoop($FB_data);

You could just use an associative array, storing a data array for each stock key, and use nested foreach's on that nested array.
Something like this:
$res = array();
$stocks = array('MSFT' => '...', 'AAPL' => '...', 'FB' => '...', 'ZNGA' => '...');
foreach ($stocks as $stock => $url) {
$fh = fopen($url, 'r');
$header = fgetcsv($fh);
$res[$stock] = array();
while ($line = fgetcsv($fh)) {
$res[$stock][] = array_combine($header, $line);
}
fclose($fh);
}
$json = array();
foreach ($res as $stock => $data) {
$out = array();
foreach($data as $val){
$out[] = array((strtotime($val['Date']) * 1000), ((float)$val['Close']));
}
$json[$stock] = json_encode(array_reverse($out));
}

Related

PHP script to create new csv from another csv after rearranging the data

I have a csv with following structure:
And I need the output csv as follows:
That means taking the faetures from each column and put it in the single row.
I am using php office to fetch and write the csv. I have written the following:
if ( false === $handle = fopen('../../../3b.csv', 'r') )
throw new Exception('File open failed.');
$headers = fgetcsv($handle);
$row = '';
$row = array();
$data = '';
$data = array();
while ( false !== $fields = fgetcsv($handle) ) {
$fields = array_combine($headers, $fields);
foreach($fields as $key=>$value){
if($key!='sku'){
if($value==''){
continue;
}
}
$row[$key] = $value;
}
if(sizeof($row)==1){
unset($row['sku']);
}
$row = array_filter( $row );
$data[] = $row;
}
$data = array_filter($data);
$use_keys = ['sku','AC Rating','color','Feature','Finish','Grade','Installation Location','Installation Method','Plank Style','Size','Specie','Tile Format','Warranty','Wear Layer','Width','LifeStyle',
'Material','Style','Thickness','Appearance','PEIRating','ProtectionRating'];
foreach($data as $key=>$value){
$new_arr = [];
foreach($use_keys as $apk_item) {
$new_value = '';
if (isset($data[$key][$apk_item])) {
$new_value = str_replace(",","|",$data[$key][$apk_item]);
}
$new_arr[$apk_item] = $new_value;
}
$data[$key] = $new_arr;
}
$data = array_filter($data, 'array_filter');
$final_array = array();
foreach ($data as $features) {
$product = array('feature' => '');
foreach ($features as $key => $feature) {
if ($key == 'sku') {
$product['sku'] = $feature;
}
else {
if($feature!=''){
$product['feature'] .= $key;
$product['value'] .= $feature;
}
}
}
$final_array[] = $product;
}
$final_array = array_filter($final_array);
$table = '<table border="1" id="csvtable">
<thead><tr><th>sku</th><th>feature</th><th>value</th></tr></thead>
<tbody>';
foreach($final_array as $value){
$sku = $value["sku"];
$combinedfeature = explode(",", $value['feature']);
foreach($combinedfeature as $single){
$table .= '<tr><td width="20%">'.$sku.'</td><td width="40%">'.$single['feature'].'</td><td width="40%">'.$single['value'].'</td></tr>';
}
}
$table .= '</tbody></table>';
print_r($table);
It's giving wrong output. How can I do this? Anyone can help, please?
A much more compact method would be to read the input and write out the target file in one loop.
This code reads in each line, combines it with the header and then extracts the sku (and removes it from the details). Then loops over the remaining details, and if there is a value to output it writes the output to the result file.
As each value may also be a comma separated list, this uses explode() to split them into individual items and writes them out as separate parts...
$inputFile = "a.csv";
$outputFile = "a1.csv";
$inputHandle = fopen($inputFile, 'r');
$outputHandle = fopen($outputFile, 'w');
$headers = fgetcsv($inputHandle);
fputcsv($outputHandle, ["sku", "feature", "value" ]);
while ( false !== $fields = fgetcsv($inputHandle) ) {
$fields = array_combine($headers, $fields);
$sku = $fields['sku'];
unset($fields['sku']);
foreach ( $fields as $name => $field ) {
if (!empty(trim($field))) {
$subFields = explode(",", $field );
foreach ( $subFields as $value ) {
fputcsv($outputHandle, [$sku, $name, $value]);
}
}
}
}
fclose($inputHandle);
fclose($outputHandle);

PHP Get duplicate values in array and average second value

I have an array that looks like this:
$ratingsInPosts = array
(
array("1",3),
array("2",5),
array("2",2),
array("5",2),
array("90",1),
array("5",6),
array("2",2),
);
I Want to find duplicate values in the first column and avarage its values from the second column.
So that this("1",3),("2",5),("2",2),("5",2),("90",1),("5",6),("2",2)
ends up like this ("1",3),("2",3),("5",4),("90",1)
Try this tested solution
I got the required Output
$ratingsInPosts = array
(
array("1",3),
array("2",5),
array("2",2),
array("5",2),
array("90",1),
array("5",6),
array("2",2),
);
$arr1 = array_column($ratingsInPosts, 0);
$p = array_count_values($arr1);
foreach($p as $key => $value)
{
$sum = 0;
for($i=0; $i < $value; $i++)
{
$pos = array_search($key, $arr1);
$sum += $ratingsInPosts[$pos][1];
unset($arr1[$pos]);
unset($ratingsInPosts[$pos]);
}
$re[] = array('"'.$key.'"',$sum/$value);
}
print_r($re);
I hope it helps you:
$groups = array();
// in this loop we group values by first column
foreach ($ratingsInPosts as $row) {
$key = $row[0];
if (!isset($groups[$key]) {
$groups[$key] = array();
}
$groups[$key][] = $row[1];
}
$result = array();
foreach ($groups as $key => $value) {
$avg = array_sum($value) / count($value);
$row = array($key, $avg);
$result[] = $row;
}
<?php
header('Content-Type: text/plain');
$ratingsInPosts = array(array("1",3),array("2",5),array("2",2),array("5",2),array("90",1),array("5",6),array("2",2));
$result = array();
$output = array();
foreach($ratingsInPosts as $array){
$result[$array[0]][] = $array[1];
}
foreach($result as $key=>$array){
$output[] = array($key,round(array_sum($array)/count($array)));
}
var_export($output);
?>

Divide string to assoc array

I have a string like this:
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
I need to convert it to array like this:
app=>[
'name=>'My App',
'id'=>'myappid',
'components'=>[
'cache'=>['id'=>'Memcache']
]
]
I use explode for divide on the pieces, but i don't know what i need to do next.
$arr = explode(";", $config);
$data = [];
foreach ($arr as $item) {
$pieces = explode('=', $item);
$pieces_dotes = explode('.', $pieces[0]);
foreach ($pieces_dotes as $piece) {
//what i need to do here?
}
}
How about this? Within the inner foreach loop, you're assigning the temp variable by reference to itself, so you build a stack of array keys, and then assign the value to it at the end.
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
$arr = explode(";", $config);
$data = [];
foreach ($arr as $item) {
$temp = &$data;
$item = trim($item);
if (empty($item)) continue;
list($setting, $value) = explode("=", trim($item));
$setting = explode(".", trim($setting));
foreach ($setting as $set) {
$temp = &$temp[$set];
}
$temp = trim($value, '" ');
}
print_r($data);
This can be solved by building the array levels based on the key and keeping a reference to the last added level in the array. My solution (based on what you already had) is as follows:
<?php
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
$arr = explode(";", $config);
$data = [];
foreach ($arr as $item) {
$pieces = explode('=', $item);
$currentValue = trim(str_replace('"', '', $pieces[1]));
$pieces_dotes = explode('.', $pieces[0]);
// Set initial value of $currentLevel to root of data array
$currentLevel = &$data;
// Initiate count & iterator vars
$numPieces = count($pieces_dotes);
$i = 0;
foreach ($pieces_dotes as $piece) {
// Increment $i and set $newKey to be trimmed version of current piece of the dot notated level
$i++;
$newKey = trim($piece);
// Skip empty $newKey
if(empty($newKey)) {
continue;
}
// Assign the $currentValue once we are at the required depth
if($i == $numPieces) {
$currentLevel[$newKey] = $currentValue;
}
// We are not at the required depth yet so initiate the key as an array if required
if(empty($currentLevel[$newKey])) {
$currentLevel[$newKey] = [];
}
// Set the $currentLevel to reference the new key
if(is_array($currentLevel[$newKey])) {
$currentLevel = &$currentLevel[$newKey];
}
}
}
Hope this helps!
The solution using custom recursive function fillBypath:
/**
* Fills an array by hierarchical 'key path'
*
* #param type $value value in each 'key path'
* #param type $keys hierarchical key list
* #param type $arr the resulting array(passed by reference)
*/
function fillByPath($value, $keys, &$arr) {
$c = count($keys) - 1;
foreach ($keys as $idx => $k) {
if ($idx == $c) { // check if it's the last key in the "key path"
$arr[$k] = $value;
break;
}
if (isset($arr[$k]) && is_array($arr[$k])) {
fillByPath($value, array_slice($keys, $idx + 1), $arr[$k]);
break;
} else {
$arr[$k] = [];
fillByPath($value, array_slice($keys, $idx + 1), $arr[$k]);
break;
}
}
}
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
$result = [];
foreach (array_filter(explode(";", $config)) as $path) {
list($keyPath, $v) = explode(" = ", trim($path));
$keys = explode(".", $keyPath); // getting key list
fillByPath($v, $keys, $result);
}
print_r($result);
The output:
(
[app] => Array
(
[name] => "My App"
[id] => "myappid"
[components] => Array
(
[cache] => Array
(
[id] => "Memcache"
)
)
)
)

Merging and looping multi dimensional array

I'm trying to loop some array using foreach. This is the code which demonstrates what I'm doing
$q = "SELECT * "
."FROM ".TBL_FRUITSDETAILS."";
$fruitsdetails = $database->query($q);
$var = array();
while($line = mysql_fetch_assoc($fruitsdetails)){
$var[] = $line;
}
$q = "SELECT * "
."FROM ".TBL_NUMDETAILS."";
$numdetails = $database->query($q);
$var2 = array();
while($line2 = mysql_fetch_assoc($numdetails)){
$var2[] = $line2;
// $n++;
}
$out = array();
foreach ($var as $key => $value){
// $out[] = array_merge_recursive($value, $var2[$key]);
foreach ($var2 as $key => $value) {
$out1[] = array_merge_recursive($var[$key], $var2[$key]);
}
}
print_r(json_encode($out1));
However, this outputs
appleone
bananatwo
appleone
bananatwo
and I want to display it like this instead
appleone
appletwo
bananaone
bananatwo
Try this,
$var = array (1,2);
$var2 = array (a,b);
$out = array();
foreach ($var as $key => $value){
foreach($var2 as $k=>$v){
$out[] = $value.$v;
}
}
print_r(json_encode($out));
I think your loop should be something like this. There's no need of using array_merge_recursive function
$var = array (1,2);
$var2 = array ('a','b');
$result = array();
foreach($var as $key => $val){
foreach($var2 as $k => $v){
$result[] = $val.$v;
}
}
There is no need to use array_merge_recursive just a nested loop will suffice:
$var = array (1,2);
$var2 = array ('a','b');
$out = array();
foreach($var as $arrayNumeric){
foreach($var2 as $arrayAlphaNumeric){
$out[] = $arrayNumeric.$arrayAlphaNumeric;
}
}

How to extend a multidimensional array in php

if my code looks like...
$result = $mysqli->query("SELECT id, name, color FROM fruits");
$list = array();
while($r = $result->fetch_array()) {
$list[] = $r;
}
print json_encode(array('list' => $list));
the result is...
list[0].id = '1', list[0].name = 'apple', list[0].color = 'red';
list[1].id = '2', list[1].name = 'banana', list[1].color = 'yellow';
...
It's fine, but i need additional infos in the list.
How can i extend the arrays and get something like:
list[0].id = '1', list[0].name = 'apple', list[0].color = 'red', list[0].taste = 'sour';
list[1].id = '2', list[1].name = 'banana', list[1].color = 'yellow', list[0].taste = 'sweet';
...
This does not work:
$result = $mysqli->query("SELECT id, name, color FROM fruits");
$list = array();
while($r = $result->fetch_array()) {
$list[] = $r;
$list[]['taste'] = 'sweet'; //DOES NOT WORK
array_push($list,array("taste" => 'sweet')); //DOES ALSO NOT WORK
}
print json_encode(array('list' => $list));
Thank you!!!
Because $list[] = $value is the same as array_push($list, $value). There's no such thing as $list[]['key'].
You should add it to the $r variable instead, before you add it to $list:
while($r = $result->fetch_array()) {
$r['taste'] = 'sweet'; //DOES WORK!
$list[] = $r;
}
If you already have $list, you can simply loop through and add it:
foreach ($list as &$array) {
$array['taste'] = 'sweet';
}
unset($array); //remember to unset references
..or if you hate references:
foreach ($list as $key => $array) {
$list[$key]['taste'] = 'sweet';
}
You need an intermediate variable
$result = $mysqli->query("SELECT id, name, color FROM fruits");
$list = array();
while($r = $result->fetch_array()) {
$r['taste'] = 'sweet'; //Modify an existing array
$list[] = $r;
}
print json_encode(array('list' => $list));

Categories