I'm using PHP Excel to create an Excel using a template Excel file. The problem is I have a datagrid and which I styled the header and first row in template. Here how it looks like:
The top leftmost coordinate is C49.
If I have 100 rows, I need to copy style of first row and paste it 100 times.
Here is my code
$cstart = 2;
$rstart = 49;
$count = 1;
$input = $worksheet->getStyle(num2char($cstart) . $rstart);
foreach ($b_data['rawData'] as $value) {
$worksheet->setCellValueByColumnAndRow($cstart, $rstart, $count++)
->setCellValueByColumnAndRow($cstart + 1, $rstart, $value['key'])
->setCellValueByColumnAndRow($cstart + 5, $rstart, $value['value']);
$interval = num2char($cstart) . $rstart . ':' . num2char($cstart+5) . $rstart;
$worksheet->duplicateStyle($input, $interval);
$rstart++;
}
function num2char($num) {
$numeric = $num % 26;
$letter = chr(65 + $numeric);
$num2 = intval($num / 26);
if ($num2 > 0) {
return num2char($num2 - 1) . $letter;
} else {
return $letter;
}
}
However, I had the following:
but what I expected is:
Is it bug or am I doing something wrong?
As Mark pointed out in the comments; a merged cell is structural, not style. So copying the style will not automatically copy the merged cells.
There is a feature request to be able to duplicate entire rows including merged cells
One way to deal with this is to check if the cells are part of a merge using the isInMergeRange() function like this:
$workbook = new PHPExcel; // prepare the workbook
$sheet = $workbook->getActiveSheet(); // get the sheet
$sheet->mergeCells('A1:E1'); // merge some cells for tesing
$cell = $sheet->getCell('A1'); // get a cell to check if it is merged
$cell->isInMergeRange() // check if cell is merged
^This returns a Boolean value indicating if it is part of a merged cell.
Another function that may interest you is the isMergeRangeValueCell() function:
$cell->isMergeRangeValueCell()
^This returns a Boolean value indicating it is part of a merged cell and the cell contains the value for the merged range; it returns false in all other situations.
Related
I want to print an array in spiral order. For arrays with sizes 3x3, 4x4, ...etc. my code works correctly, but for 3x5, 4x6 or 5x8 sizes the output is wrong, returning only the first iteration.
This is my simple code:
private function _spiral($rows, $cols, array $array) {
$offset = 0;
while($offset < ($rows - 1)){
for($col = $offset; $col <= $cols - 1; $col++){
print($array[$offset][$col] . ' ');
}
$offset++;
$cols--;
for($row = $offset; $row < $rows; $row++){
print($array[$row][$cols] . ' ');
}
$rows--;
for($col = $cols - 1; $col >= $offset; $col--){
print($array[$rows][$col] . ' ');
}
for($row = $rows; $row >= $offset; $row--){
print($array[$row][$offset - 1] . ' ');
}
}
}
Example with 3 rows and 4 columns:
$array = array(
array(00,01,02,03),
array(10,11,12,13),
array(20,21,22,23)
)
Expected result for this array is 0 1 2 3 13 23 22 21 20 10 11 12, but the output of my function stops after 10.
For 4 rows and 4 columns:
$array = array(
array(00,01,02,03),
array(10,11,12,13),
array(20,21,22,23),
array(30,31,32,33)
)
...it should return 0 1 2 3 13 23 33 32 31 30 20 10 11 12 22 21, and that is what my code returns.
But I want both cases to work with my code. How can I correct the code to also produce the correct output for the first, and other cases?
There are a few problems with your code:
it does not treat the four directions of traversal in the same way. You have four loops for these four directions, but in some you have <= as loop-end condition in others <, in some the condition is on something minus 1, in others not.
it has no provision for when all elements have been printed by the first or second inner loop, and thus the remaining loops will in some cases print already printed elements.
the outer loop condition does not check whether there are still columns that need traversal. It is not enough to test for such rows only.
Although you could try to fix your code, I think it is better to start from scratch, taking into account that the solution should be symmetric for all four directions. This is an important intuitive reaction to develop: spot symmetries. This will lead to less code and fewer bugs.
You want to traverse a dimension (row or column) in your array until you reach the border of the array or an element you already printed. Then you want to turn 90° to the right and repeat exactly the same logic over and over again. So if your code looks different for these different directions, something is not right.
I will share two implementations. Both will use the concept of the "current" cell, and let it move around in spiral motion.
The first solution treats going back or forward along a row with the same code, and similarly it has one piece of code for traversing a column forward or backward. So this solution has two inner loops, one for traversing along a row, and another for traversing along a column. The direction in which a row or column is traversed is kept in the $direction variable, which flips between 1 and -1 at each execution of the outer loop:
function _spiral(array $array) {
// No need to have the number of rows and columns passed as arguments:
// We can get that information from the array:
$rows = count($array);
$cols = count($array[0]);
// Set "current" cell to be outside array: it moves into it in first inner loop
$row = 0;
$col = -1;
$direction = 1; // Can be 1 for forward and -1 for backward
while ($rows > 0 and $cols > 0) {
// Print cells along one row
for ($step = 0; $step < $cols; $step++) {
$col += $direction;
print $array[$row][$col] . ' ';
}
// As we have printed a row, we have fewer rows left to print from:
$rows--;
// Print cells along one column
for ($step = 0; $step < $rows; $step++) {
$row += $direction;
print $array[$row][$col] . ' ';
}
// As we have printed a column, we have fewer columns left to print from:
$cols--;
// Now flip the direction between forward and backward
$direction = -$direction;
}
}
Note the perfect symmetry between the first inner loop and the second inner loop.
In a second solution, this use of symmetry is taken one step further, in order to replace the two inner loops with only one. For that to happen we must abandon the use of separate variables for rows and columns, and use the concept of a size related to a dimension:
function _spiral(array $array) {
// This version of the function aims to treat rows and columns in the same way,
// They are just another dimension, but all the logic is exactly the same:
// $size[] has the number of rows in $size[0] and number of columns in $size[1]
$size = Array(count($array), count($array[0]));
// $current[] has the current row in $current[0] and current column in $current[1]
$current = Array(0, -1);
// $direction[] has the current row-traversal direction in $direction[0]
// and column-traveral direction in $direction[1]
$direction = Array(1, 1);
$dimension = 0; // Which dimension to traverse along, can be 0 for row, 1 for column
while ($size[$dimension] > 0) {
// Switch dimension (row to column, column to row), to traverse along
$dimension = 1 - $dimension;
// Print one line along that dimension, in its current direction
for ($step = 0; $step < $size[$dimension]; $step++) {
$current[$dimension] += $direction[$dimension];
print $array[$current[0]][$current[1]] . ' ';
}
// As we have printed a line, we have fewer left to print from:
$size[1 - $dimension]--;
// Now flip the direction between forward and backward for this dimension:
$direction[$dimension] = -$direction[$dimension];
}
}
An extended version
Upon request more than one year later: here is a version that allows one to choose the corner to start from, and whether to do it counter-clockwise instead of clockwise. This function will not print the result, but return a 1D array, with the spiral sequence. This way you can decide yourself what to do with the result: print it, or ... whatever.
function spiral(array $array, $startRight = false, $startBottom = false,
$counterClockWise = false) {
// This version allows to select which corner to start from, and in which direction.
// $size[] has the number of rows in $size[0] and number of columns in $size[1]
$size = [count($array), count($array[0])];
// $direction[] has the current row-traversal direction in $direction[0]
// and column-traversal direction in $direction[1]
$direction = [$startBottom ? -1 : 1, $startRight ? -1 : 1];
// Which dimension to traverse along: false means row, true means column.
// Every one of the optional arguments will flip the first dimension to use:
$dimension = ($startBottom xor $startRight xor $counterClockWise);
// $current[] has the current row in $current[0] and current column in $current[1]
$current = [$startBottom * (count($array)-1), $startRight * (count($array[0])-1)];
// Go back one step, outside of the grid
$current[!$dimension] -= $direction[!$dimension];
while ($size[$dimension] > 0) {
// Switch dimension (row to column, column to row), to traverse along
$dimension = !$dimension;
// Print one line along that dimension, in its current direction
for ($step = 0; $step < $size[$dimension]; $step++) {
$current[$dimension] += $direction[$dimension];
$result[] = $array[$current[0]][$current[1]]; // store in new array
}
// As we have printed a line, we have fewer left to print from:
$size[!$dimension]--;
// Now flip the direction between forward and backward for this dimension:
$direction[$dimension] = -$direction[$dimension];
}
return $result; // Return the resulting spiral as a 1D array
}
See it run on eval.in
Hi I'm using FPDF for generating PDF file. I'm displaying each item inside my foreach using Cell function of FPDF. Now I have a conditional statement to get the last iteration or the last item in my foreach and adding a certain value in it. After that I need to overwrite(display) the last iteration with a new value.
Image Above is the items that I'm displaying. The last item is the last iteration. The first value of the last item is 263.50 and after I get it because it's the last iteration. I'm adding .01 value on it to be 263.51.
I can display the new value but the old value is still there.
Here is my sample code
$this->SetXY(29, $y);
$this->SetFont('Arial','',8.5);
$this->Cell(18,4,$value_of_ins = str_replace(',', '', number_format($inscost_conv, 2)),1,0,'R'); // this line is to display the items
if ($i == $len - 1) { // to get the last iteration
$value_of_ins = $value_of_ins + 1;
$this->SetXY(29, $y);
$this->SetFont('Arial','',8.5);
$this->Cell(18,4,$value_of_ins,1,0,'R'); // display the last item with new value
}
$i++;
I suppose you shouldn't write last value until you add 1 to it. Something like this:
$this->SetXY(29, $y);
$this->SetFont('Arial','',8.5);
$value_of_ins = str_replace(',', '', number_format($inscost_conv, 2));
if ($i == $len - 1) {
$value_of_ins += 1
}
$this->Cell(18,4,$value_of_ins,1,0,'R'); // this line is to display the items
I have a text file that contain data like this (EURUSD quotes)
19710104,000000,0.53690,0.53690,0.53690,0.53690,1
19710105,000000,0.53660,0.53660,0.53660,0.53660,1
19710106,000000,0.53650,0.53650,0.53650,0.53650,1
19710107,000000,0.53680,0.53680,0.53680,0.53680,1
19710108,000000,0.53710,0.53710,0.53710,0.53710,1
19710111,000000,0.53710,0.53710,0.53710,0.53710,1
19710112,000000,0.53710,0.53710,0.53710,0.53710,1
I want to move some data to another file like
0.53690,0.53690,0.53690,0.53690
and add some difrent calculated number to each line like (Moving average and RSI, Stoch ...) so the file can be trained by Neural Network, final file must be like this
OPEN, HIGH, LOW, CLOSE, VOL, MA50, MA20, RSI14, StochMain, StochSignal,
so I need some hints
You should use the PHP functions fgetcsv and fputcsv. See working example below that you can tweak to your needs.
It assumes that your input values given are in the format OPEN, CLOSE, HIGH, LOW, VOL. Introduce RSI and Stochastic etc in the same way that the Moving Average works.
<?php
// Prepare variables
$row = 1;
$output = array();
// Attempt to open the file quotes.txt
if (($handle = fopen("quotes.txt", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$row++;
// This part of the while loop will be hit for each line in the quotes.txt
// $data is an array that contains the values of this line from the csv
// $output is a new array that you are generating
// Create a new sub-array in the output array (add a new line to the main output)
$output[$row] = array();
// Add the third value of the input array to the start of the new array (the opening price)
$output[$row][] = $data[2];
// Add the fourth value of the input array to the new array (the closing price)
$output[$row][] = $data[3];
// Add the fifth value of the input array to the new array (the high price)
$output[$row][] = $data[4];
// Add the sixth value of the input array to the new array (the low price)
$output[$row][] = $data[5];
// Add the seventh value of the input array to the new array (the volume)
$output[$row][] = $data[6];
// Add moving average to the new array
$output[$row][] = calculate_ma($output, $row);
}
fclose($handle);
}
// Create new file or open existing to save the output to
$handle = fopen('output.csv', 'w');
// Flatten the arrays and save the csv data
foreach ($output as $file) {
$result = [];
array_walk_recursive($file, function($item) use (&$result) {
$result[] = $item;
});
fputcsv($handle, $result);
}
/**
* Calculate the value for the MA using the values in $output.
*/
function calculate_ma($output, $row) {
// For this example we will just say that the MA is equal to the closing price of this period
// and the previous four periods, divided by 5.
$ma = $output[$row][1] + $output[$row-1][1] + $output[$row-2][1] + $output[$row-3][1] + $output[$row-4][1];
$ma = $ma / 5;
return $ma;
}
?>
The output of the above code, using the same input as you have pasted in your question, will be:
0.53690,0.53690,0.53690,0.53690,1,0.10738
0.53660,0.53660,0.53660,0.53660,1,0.2147
0.53650,0.53650,0.53650,0.53650,1,0.322
0.53680,0.53680,0.53680,0.53680,1,0.42936
0.53710,0.53710,0.53710,0.53710,1,0.53678
0.53710,0.53710,0.53710,0.53710,1,0.53682
0.53710,0.53710,0.53710,0.53710,1,0.53692
Bear in mind that the first four moving averages will be incorrect, as they do not have 5 periods of data from which to calculate the 5-period MA.
To calculate a larger MA (50-period) without a huge bunch of code, replace the MA function with:
function calculate_ma($output, $row) {
$period = 50;
for ($x = 0 ; $x < $period ; $x++){
$ma = $ma + $output[$row-$x][1];
}
$ma = $ma / $period;
return $ma;
}
I strongly believe that you should open file, read line by line, explode it with ',' and then store every line to some kind of map, do some calculations and finally save it to another file.
http://php.net/manual/en/function.explode.php
How to read a file line by line in php
I just want to ask if there's a function in excel/phpexcel that can SUM a range of
cells but skip to add every one cell.
Ex. Range is A1:G1 and it will only sum A1+C1+E1+G1 skipping every 1 cell.
Or how can I do that? Please take note that range is dynamic. It could be a1:g1 or further or less.
Build the formula dynamically in your PHP script:
$startCell = 'A';
$endCell = 'H';
$row = 1;
$formula = '=' . $startCell . $row;
while ($startCell++ != $endCell && $startCell++ != $endCell) {
$formula .= '+' . $startCell . $row;
}
EDIT
Note that I've also added a pure Excel formula answer to your previous question
So I have a large table (701-ish rows, 19 columns). I need to extract the innertext in each td, and then I write it to a csv. The problem is, this takes forever. Doing just 100, takes 32 seconds. This is the code I have:
for ($j = 0; $j < 100; $j++)
{
$f = $html->find("td",$j); // get the td elements from the html
$rowArray[] = $f->innertext; // store that text inside the array
if(($j+1) % 19 == 0) // hit the end of the row
{
$txt .= implode(",", $rowArray) . "\r\n"; // format with comma's and throw it into $txt
unset($rowArray); // clear the array, for the next record
$rowArray = array(); // re-set the array
}
}
The 100 is a temporary value while I test, it really is closer to 13000. The biggest issue is finding the TD values. Is there a faster way for this or is this as good as I can get it?
Basically, looking for the quickest way to extract TD data from an HTML table so I can write it to a CSV.
Did a str_replace to get the stuff I didn't want out and was able to get the contents a lot quicker and faster.