PHPExcel : Reading cell values with rangetoArray() function and apply sum formula - phpexcel

I'm trying to read the cell formulas with rangetoArray() function and then there is array values return i want to sum it with the phpexcel library when this function read the range values.
include 'PHPExcel/Classes/PHPExcel.php';
include 'PHPExcel/Classes/PHPExcel/IOFactory.php'
$objPHPExcel = PHPExcel_IOFactory::load($uploadedFile);
$cellValues = $objPHPExcel->getActiveSheet()->rangeToArray('A1:A2');
//Basically i'm doing this for sum values in for each loop .. but it is custom created. i want PHPExcel Library Function for SUM and other Mathmatic Operations.
foreach ($cellValues as $cell) {
foreach ($cell as $finalValue) {
$sumVal += $finalValue;
}
}
//I want to read value through rangetoArray('A1:A5'); like this and automatic sum it with some PHPExcel Library function / method.
Thanks

Option #1
Create a formula, assigning to a dummy cell (A1 in this case, though it doesn't change the value of cell A1 in any way), and using the calculation engine to execute that formula:
$sumFormula = '=SUM(A1:B2)';
$rangeSum = PHPExcel_Calculation::getInstance()->calculateFormula(
$sumFormula,
'A1',
$objPHPExcel->getActiveSheet()->getCell('A1')
);
var_dump($rangeSum);
Option #2
Call the SUM() function directly from your own code, passing the result of the toArray() method to the function:
$rangeSum = PHPExcel_Calculation_MathTrig::SUM(
$objPHPExcel->getActiveSheet()->rangeToArray('A1:B2')
);
var_dump($rangeSum);
Option #3
Create your own class which will take the result of a fromArray() call, with a doSum() method:
class mySummer{
protected $_cellArray;
public function __construct(array $cells = array()) {
$this->_cellArray = PHPExcel_Calculation_Functions::flattenArray($cells);
}
public function doSum(){
return array_sum($this->_cellArray);
}
}
$summer = new mySummer(
$objPHPExcel->getActiveSheet()->rangeToArray('A1:B2')
);
var_dump($summer->doSum());

Related

How to use MDX constants/functions in icCube Reporting?

I have several functions/constants defined in the schema. An example is:
_latestPeriodWithData() which returns a date.
Goal: I want to use this value in the Reporting to set a Guide in a chart, using the demo example 'On Widget Options':
This is what I tried so far:
I have assigned this function as 'MDX / Value' for a Report Constant _lastDate and obtained the value for this constant using the java script function: context.eventValue('_lastDate'); but that just gives me the caption. The function context.eventObject("_lastDate").options.asMdx gives me _latestPeriodWithData(), but not its value.
On Widget Options
/**
* Result will be used while rendering.
*/
function(context, options, $box) {
// for debugging purpose
console.log(context.eventObject("_lastDate").options.asMdx);
options.guides[0].value = context.eventObject("_lastDate").options.asMdx; // <-- gives me _latestPeriodeWith data
// but not its value
options.guides[0].toValue = context.eventObject("_lastDate").options.asMdx;
options.guides[0].lineAlpha = 1;
options.guides[0].lineColor = "#c44";
return options;
}

xBestIndex malfunction (passing non-literal parameters to table valued function)

I'm trying to implement a table valued function (as a SQLite virtual table).
It's a function that would take a string and return a table with all the words of the string.
If I call it with literal values like below, it works fine.
SELECT word FROM splitstring("abc def ghi")
If, however, I call it with a column from another table it doesn't work:
SELECT a.Name, word FROM article a, splitstring(a.Text)
The xBestIndex method gets called all right, but right after that, I get an exception from the ExecuteReader method. The exception message is "xBestIndex malfunction". The xFilter method does not get called because of the exception.
My xBestIndex implementation is simple, it just marks the parameter so I can see it in xFilter:
public override SQLiteErrorCode BestIndex(SQLiteVirtualTable table, SQLiteIndex index)
{
index.Outputs.ConstraintUsages.ElementAt(0).argvIndex = 1;
index.Outputs.ConstraintUsages.ElementAt(0).omit = 1;
return SQLiteErrorCode.Ok;
}
Am I'm doing something wrong or is it impossible to pass non-literal parameters to table valued functions?
Found the issue! I was using constraints that had usable=0. The BestIndex method gets called multiple times by SQLite, the second time with a non-usable constraint.
Here is the fixed body of the BestIndex method.
public override SQLiteErrorCode BestIndex(SQLiteVirtualTable table, SQLiteIndex index)
{
if (index.Inputs.Constraints.Count() != 2)
throw new ArgumentException("The generate_series function requires two integer (long) parameters!");
if (index.Inputs.Constraints.All(c=>c.usable == 1))
{
index.Outputs.ConstraintUsages.ElementAt(0).argvIndex = 1;
index.Outputs.ConstraintUsages.ElementAt(0).omit = 1;
index.Outputs.ConstraintUsages.ElementAt(1).argvIndex = 2;
index.Outputs.ConstraintUsages.ElementAt(1).omit = 1;
}
else
{
index.Outputs.IndexNumber = -1;
index.Outputs.EstimatedCost = double.MaxValue;
}
return SQLiteErrorCode.Ok;
}
Now I check the usable flag. When BestIndex gets called with a constraint with usable=0 I skip it i.e. return a high estimated cost for that index so it doesn't get used.

PHPexcel - getOldCalculatedValue and rangeToArray

Searched for quite a while now, but I'm stuck at the following problem.
I am using PHPexcel 1.8.0
The spreadsheet is read using the following code:
$rowData = $sheet->rangeToArray('A' . $row . ':' . $highestColumn . $row, NULL, TRUE, TRUE);
So far ok and it works well.
But some spreadsheets contain external referenced data.
And for that I want to use "getOldCalculatedValue".
How do I combine "getOldCalculatedValue" with "rangeToArray" ?
Or is "rangeToArray" inappropriate for this ?
Thanks for any help or hints !
Simple answer, you can't combine the two
rangeToArray() is a simple method for a simple purpose, it doesn't try to do anything clever, simply to return the data from the worksheet as efficiently and quickly as possible
getOldCalculatedValue() is used for a very specific circumstance, and isn't guaranteed to be correct even then, because it retrieves the last value calculated for the cell in MS EXcel itself, which ,ay not be correct if the external workbook wasn't available to MS Excel in that circumstance, or MS Excel formula evaluation was disable.
When calculating cells values from a formula, the PHPExcel calculation engine should use the getOldCalculatedValue() as a fallback if it finds an external reference, and rangeToArray() will try to use this method, but it isn't perfect, especially when that reference in nested deep inside other formulae referenced in other cells.
If you know that a formula in a cell contains an external reference, you should use getOldCalculatedValue() directly for that cell
I came up with the following solution.
Maybe not perfect, but it currently does the job. Thanks for any improvements!
With PHPExcel included and the excel file uploaded and ready, I continue with:
$sheet = $objPHPExcel->getSheet(0);
$highestRow = $sheet->getHighestRow();
Create a new array to store the cell values of a row
$arr_row = array();
Loop through the rows
for ($rownumber = 2; $rownumber <= $highestRow; $rownumber++){
$row = $sheet->getRowIterator($rownumber)->current();
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
Then loop through the cells of the current row
foreach ($cellIterator as $cell) {
Find cells with a formula
$cellcheck = substr($cell->getValue(),0,1);
if($cellcheck == '='){
$cell_content = $cell->getOldCalculatedValue();
}
else{
$cell_content = $cell->getValue();
}
Add the cell values to the array
array_push($arr_row,$cell_content);
Close cell loop
}
At this point I use the $arr_row to do further calculations and string formatting, before finally inserting it into a mysql table.
Close row loop
}
I made some changes in the function rangeToArray() inside Worksheet.php.
Worked fine!
public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false) {
// Returnvalue
$returnValue = array();
// Identify the range that we need to extract from the worksheet
list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange);
$minCol = PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] -1);
$minRow = $rangeStart[1];
$maxCol = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] -1);
$maxRow = $rangeEnd[1];
$maxCol++;
// Loop through rows
$r = -1;
for ($row = $minRow; $row <= $maxRow; ++$row) {
$rRef = ($returnCellRef) ? $row : ++$r;
$c = -1;
// Loop through columns in the current row
for ($col = $minCol; $col != $maxCol; ++$col) {
$cRef = ($returnCellRef) ? $col : ++$c;
// Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
// so we test and retrieve directly against _cellCollection
if ($this->_cellCollection->isDataSet($col.$row)) {
// Cell exists
$cell = $this->_cellCollection->getCacheData($col.$row);
if ($cell->getValue() !== null) {
if ($cell->getValue() instanceof PHPExcel_RichText) {
$returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
} else {
if ($calculateFormulas)
{ ##################################### CHANGED LINES
if(!preg_match('/^[=].*/', $cell->getValue()))
{
$returnValue[$rRef][$cRef] = $cell->getCalculatedValue(); # THE ORIGINAL CODE ONLY HAD THIS LINE
}
else
{
$returnValue[$rRef][$cRef] = $cell->getOldCalculatedValue();
}
} ##################################### CHANGED LINES
else
{
$returnValue[$rRef][$cRef] = $cell->getValue();
}
}
if ($formatData) {
$style = $this->_parent->getCellXfByIndex($cell->getXfIndex());
$returnValue[$rRef][$cRef] = PHPExcel_Style_NumberFormat::toFormattedString(
$returnValue[$rRef][$cRef],
($style && $style->getNumberFormat()) ?
$style->getNumberFormat()->getFormatCode() :
PHPExcel_Style_NumberFormat::FORMAT_GENERAL
);
}
} else {
// Cell holds a NULL
$returnValue[$rRef][$cRef] = $nullValue;
}
} else {
// Cell doesn't exist
$returnValue[$rRef][$cRef] = $nullValue;
}
}
}
// Return
return $returnValue;
}

PHP manipulating multiarray data

I have a problem. I don't know how to show the exactly data I want.
My array is like this:
$races[$numRace][$finalPosition]
$races[1][1] = array ('stephan','1:27,895');
$races[1][2] = array ('george', '1:29,075');
$races[1][3] = array ('peter', '1:29,664');
$races[1][4] = array ('benson', '1:29,915');
$races[2][1] = array ('benson', '1:41,113');
$races[2][2] = array ('stephan','1:41,434');
$races[2][3] = array ('george', '1:43,654');
foreach ($races as $v1) {
foreach ($v1 as $v2) {
foreach ($v2 as $v3) {
echo "$v3\n";
}
}
}
This one shows me every data of $race array.
My question is: How can I do for showing just results for race 2?
Important: We don't know how many runners have participated on each race (So, we need a "foreach").
I would like a result like this:
benson
stephan
george
Simply iterate through only the relevant array. Based on your code your foreach should be:
foreach ($races[2] as $pos => $info){
echo $pos.': '.$info[0];
}
I've hard-coded the value 2 in the code but you could easily use a variable (foreach ($races[$raceNum]).
If you don't already know, => makes it possible for us to use both the key and the value as variables in our loop.

Flex datagrid universal numeric sorting function?

I am using a Flex dataGrid, and need to sort some of my columns numerically.
Looking at the sortCompareFunction, it seems like i need to create a different function for each column that i want to sort, because my sort function has to know what field it is sorting on.
Is there any way that I can pass the field to be sorted on into the function? so that I only need one numeric sorting function.
I did it using this function:
function fieldNumericSorter(field:String):Function {
return function (obj1:Object, obj2:Object):int {
return sign( int(obj1[field]) - int(obj2[field]) );
}
}
and for each column that needed sorting set
colToBeSorted.sortCompareFunction = fieldNumericSorter("fieldname");
If you are using a DataGrid with an XML dataProvider, this worked for me (modified from Alex's answer):
private function xmlDataGridNumericSorter(field:String):Function
{
return function (obj1:Object, obj2:Object):int
{
var num:Number = ((Number)(obj1.attribute(field)) - (Number)(obj2.attribute(field)));
return (num > 0) ? 1 : ((num < 0) ? -1 : 0);
}
}
and
dataGridColumn.sortCompareFunction = xmlDataGridNumericSorter(xmlAttribute.name().toString());
A very nice solution, considering how common a procedure this probably is..
Thanks Alex, hope this helps people further.
I did not use a numeric function to sort - instead did the following:
arrayCollObject.addItem({Col1: rowData[0], Col2: parseFloat(rowData[1])});
This seems to do the sorting correctly.

Resources