I tried to add a column named FTE containing this formula :
=I2/SUMPRODUCT(I:I,(M:M=M2) * (C:C=C2) * (N:N=N2))
This formula worked when applied in Excel, but when added from php, I just get an error:
maximum execution time calculation.php phpexcel
$assembly = $arrayWorksheet->addColValByRow($assembly, 'FTE', ['= I', '/SUMPRODUCT(I:I,(M:M=M', ')*(C:C=C', ')*(N:N=N'], '))');
// Output : =I2/SUMPRODUCT(I:I,(M:M=M2)*(C:C=C2)*(N:N=N2))
Sadly I can't figure out why. My initial formula was SUMIFS that I converted into SUMPRODUCT, because I know that SUMIFS isn't implemented yet.
function addColValByRow($worksheet, $title, $valArray, $finalVal = NULL) {
// Take the last column
$lastCol = key( array_slice( $worksheet[1], -1, 1, TRUE ) );
$newCol = ++$lastCol;
// tke the last cell
$lastCell = key( array_slice( $worksheet, -1, 1, TRUE ) );
$worksheet[1][$newCol] = $title;
for ($i = 2; $i <= $lastCell; $i++) {
$worksheet[$i][$newCol] = "";
foreach (array_keys($valArray) as $key) {
$worksheet[$i][$newCol] .= $valArray[$key] . $i;
}
if ($finalVal != NULL) {
$worksheet[$i][$newCol] .= $finalVal;
}
}
return $worksheet;
}
It's not SUMPRODUCT() that's causing the problem, it's the fact that PHPExcel doesn't support column/row ranges fully, so it's a column range like I:I or M:M or C:C that's causing the problem.
If you can change this to an actual cell range (e.g. I1:I2048), then it shouldn't be an issue.
And (for future reference) SUMIFS() is implemented in the latest code in the develop branch on github
Related
Goal: I'm trying to add a feature to my Excel 2016 VSTO plugin. The feature will get 1 column from the active sheet, and iterate over it changing the background color based on string length.
Problem: I'm having trouble getting string length from the cells. I can not figure out the proper syntax I currently have var count = row.Item[1].Value2.Text.Length;
Code: Here is what I have
public void CharacterLengthCheck(int length = 24, int Column = 3)
{
Worksheet sheet = Globals.ThisAddIn.Application.ActiveSheet;
var RowCount = sheet.UsedRange.Rows.Count;
Range column = sheet.Range[sheet.Cells[1, Column], sheet.Cells[RowCount, Column]];
foreach (Range row in column)
{
var count = row.Item[1].Value2.Text.Length;
if (count > length)
{
row.Item[1].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
}
else
{
row.Item[1].Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Green);
}
}
}
I think your problem is here:
row.Item[1].Value2.Text.Length
The length function is Len(x), so try Len(row.Item[1].Value2.Text)
Thank you to #Jeeped for their advice on using conditional formatting. However the answer to my original question is.
Change
var count = row.Item[1].Value2.Text.Length;
to
string text = (row.Item[1].Value).ToString();
var count = text.length;
I believe this is because value is dynamic and needs to be explicitly cast to string
I'm pretty new to XLL programming but I believe I've done my homework on this little problem. I'm trying to set multiple cell values using xlSet but xlSet just duplicates the first value of my array, as if I had passed it a single reference. If instead I consecutively call xlSet for each cell individually, it works. But it's ugly and, no doubt, slow.
I know xlSet can't be called from a UDF. This isn't a UDF. Elsewhere I saw that somebody had a struct alignment problem in the xlMulti, but I'm sending the same xlMulti back to Excel, so that's not a problem. (I tried the /Zp8 compiler switch, anyway.)
I started with SDK Framework stuff, eg., TempActiveRef(1,1,0,2) and then replaced those calls with the more direct XLCALL.H stuff, mainly to increase my chances of getting responses here.
Office 2010, VSTO 2010, Win7 64bit SP1.
Here's what works and what doesn't:
__declspec(dllexport) int WINAPI testCmd(void)
{
XLOPER12 ref, xValsInMulti, xResSet;
XLMREF12 mref;
int i, res;
// Build an xltypeRef XLOPER12 that points to three cells, A2:C2
res = Excel12(xlSheetId, &ref, 0);
if (res != xlretSuccess) return 0;
ref.xltype = xltypeRef;
ref.val.mref.lpmref = &mref;
mref.count = 1;
mref.reftbl[0].rwFirst = mref.reftbl[0].rwLast = 1;
mref.reftbl[0].colFirst = 0;
mref.reftbl[0].colLast = 2;
// Fetch the cell values into an xltypeMulti.
// This works. Returns 0. And xValsInMulti.type becomes xlTypeMulti
res = Excel12(xlCoerce, &xValsInMulti, 1, &ref );
// Change cell reference to the next row (A3:C3)
mref.reftbl[0].rwFirst = mref.reftbl[0].rwLast = 2;
// Attempt to set the values. Doesn't work. All cells become value of A2.
Excel12(xlSet, &xResSet, 2, &ref, xValsInMulti);
Excel12(xlcAlert, 0, 1, &xResSet); // Displays "TRUE"
// Try again (in the next row) setting each cell individually. This works.
mref.reftbl[0].rwFirst = mref.reftbl[0].rwLast = 3;
for (i=0; i<3; i++)
{
mref.reftbl[0].colFirst = mref.reftbl[0].colLast = i;
Excel12(
xlSet, &xResSet, 2, &ref, xValsInMulti.val.array.lparray+i
);
Excel12(xlcAlert, 0, 1, &xResSet); // Displays "TRUE"
}
Excel12(xlFree, 0, 1, &xValsInMulti);
return 1;
}
I need to find selected cells for the control CGridCtrl using the following code.
CCellRange cells = m_Grid.GetSelectedCellRange();
if( cells.Count() == 0 )
return ;
for (int i = cells.GetMaxRow(); i >= cells.GetMinRow(); i--)
{
for (int j=cells.GetMinCol(); j<=cells.GetMaxCol();j++)
{
BOOL selected = m_Grid.GetCell(i,j)->IsSelected();
TRACE(_T("Row %d Column %d Selected = %d State %d\n"),i,j,selected, m_Grid.GetCell(i,j)->GetState());
if(selected)
{
m_Grid.DeleteRow(i);
break;
}
}
}
Since the cells I selected are not contiguous, I need to use IsSelected() to find a cell selected. The only last cell return TRUE. Is there a way to determine the cells selected for the control?
I don't know if the flag in the cells are correct, but the correct function to check if a cell is selected are:
BOOL CGridCtrl::IsCellSelected(CCellID &cell) const
BOOL CGridCtrl::IsCellSelected(int nRow, int nCol) const
Just check the implementation. There is a m_SelectedCellMap that contains all selected ranges.
I am looking for a solution to search for certain strings in a Google Sheet and, when found, replace them with another string from a list in another sheet.
For better understanding, I prepared a Sheet for you:
https://docs.google.com/a/vicampo.de/spreadsheets/d/1mETtAY72K6ST-hg1qOU9651265nGq0qvcgvzMRqHDO8/edit?usp=sharing
So here's the exact task I want to achieve:
In every single cell in column A of sheet "Text", look for the strings given in column A in sheet "List" and, when found, replace it with the corresponding string in column B of the sheet "List".
See my Example: Look in cell A1 for the string "Lorem" and replace it with "Xlorem", then look for the string "Ipsum" and replace it with "Xipsum", then look for the string "amet" and replace it with "Xamet" then move on to cell B1 and start again looking for the strings...
I have tried different functions and managed to do this with a function for one cell. But how to do it in a loop?
Thanks everyone who is interested in helping out with this problem!
Although there must be 'nicer' solutions, a quick solution (as long is the number of cells with the words you want replaced is not too long), would be:
=ArrayFormula(regexreplace(regexreplace(regexreplace(A1:A; List!A1; List!B1); List!A2; List!B2); List!A3; List!B3))
Probably the best for you, in this case, should be creating a new function to your Google Spreadsheet. It tends to be, in the general case, more simple, clear and powerfull than that kind of complex formulas that should do the same.
In this particular case, I have the same problem, so you can use the same function:
Click on "Tools" menu, then click on the "Script Editor" option. Into the script editor, erase the draft and paste this function:
function preg_quote( str ) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// * example 1: preg_quote("$40");
// * returns 1: '\$40'
// * example 2: preg_quote("*RRRING* Hello?");
// * returns 2: '\*RRRING\* Hello\?'
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
}
function ARRAYREPLACE(input,fromList,toList,caseSensitive){
/* default behavior it is not case sensitive */
if( caseSensitive === undefined ){
caseSensitive = false;
}
/* if the from list it is not a list, become a list */
if( typeof fromList != "object" ) {
fromList = [ fromList ];
}
/* if the to list it is not a list, become a list */
if( typeof toList != "object" ) {
toList = [ toList ];
}
/* force the input be a string */
var result = input.toString();
/* iterates using the max size */
var bigger = Math.max( fromList.length, toList.length) ;
/* defines the words separators */
var arrWordSeparator = [ ".", ",", ";", " " ];
/* interate into the lists */
for(var i = 0; i < bigger; i++ ) {
/* get the word that should be replaced */
var fromValue = fromList[ ( i % ( fromList.length ) ) ]
/* get the new word that should replace */
var toValue = toList[ ( i % ( toList.length ) ) ]
/* do not replace undefined */
if ( fromValue === undefined ) {
continue;
}
if ( toValue == undefined ) {
toValue = "";
}
/* apply case sensitive rule */
var caseRule = "g";
if( !caseSensitive ) {
/* make the regex case insensitive */
caseRule = "gi";
}
/* for each end word char, make the replacement and update the result */
for ( var j = 0; j < arrWordSeparator.length; j++ ) {
/* from value being the first word of the string */
result = result.replace( new RegExp( "^(" + preg_quote( fromValue + arrWordSeparator[ j ] ) + ")" , caseRule ), toValue + arrWordSeparator[ j ] );
/* from value being the last word of the string */
result = result.replace( new RegExp( "(" + preg_quote( arrWordSeparator[ j ] + fromValue ) + ")$" , caseRule ), arrWordSeparator[ j ] + toValue );
/* from value in the middle of the string between two word separators */
for ( var k = 0; k < arrWordSeparator.length; k++ ) {
result = result.replace(
new RegExp(
"(" + preg_quote( arrWordSeparator[ j ] + fromValue + arrWordSeparator[ k ] ) + ")" ,
caseRule
),
/* need to keep the same word separators */
arrWordSeparator[ j ] + toValue + arrWordSeparator[ k ]
);
}
}
/* from value it is the only thing in the string */
result = result.replace( new RegExp( "^(" + preg_quote( fromValue ) + ")$" , caseRule ), toValue );
}
/* return the new result */
return result;
}
Just save your script and the new function it will be available to you. Now, you have the function that replaces all the first values list by the second value list.
=ARRAYREPLACE(C2;A1:A4;B1:B4)
for example, takes the C2 text and replaces all the elements found in the A1:A4 list by the equivalent into the B1:B4 list.
Copy Sample File With Explanation
Problem
The challenge is:
Find & Replace multiple values in the input of multiple cells.
ArrayFormula's
Solutions which I account as Array-Solution must be:
based on open ranges
no need to drag the formula down
no need to modify the formula when new items in lists appear
These tests must be passed:
Is ArrayFormula
User can set Case Sensitivity
Replaces Emojis
Replaces Special Chars $\[]. etc.
CrashTest. Works for 10K rows of data
CrashTest. Works for 2K replacements
Script
I recommend using the not-regex-based script in this case. This algorithm finds and replaces text by chars:
Usage
Use as a regular formula from sheet:
=substitutes(A12:A;List!A1:B)
Code
Save this code to use the formula above:
/**
* Substitutes in every entry in array
* Text from prefilled array
*
* #param {array} input The array of strings.
* #param {array} subTable The array of string pairs: search texts / replace texts.
* #param {boolean} caseSensitive [optional=false]
* TRUE to match Apple and apple as different words
* #return The input with all replacement made
* #customfunction
*/
function substitutes(input, subTable,caseSensitive) {
// default behavior it is not case sensitive
caseSensitive = caseSensitive || false;
// if the input is not a list, become a list */
if( typeof input != "object" ) {
input = [ input ];
}
var res = [], text;
for (var i = 0; i < input.length; i++) {
// force each array element in the input be a string
text = input[i].toString();
for (var ii = 0; ii < subTable.length; ii++) {
text = replaceAll_(
text,
subTable[ii][0],
subTable[ii][1],
caseSensitive);
}
res.push(text);
}
return res;
}
/***
* JavaScript Non-regex Replace
*
* Original code sourse:
* https://stackoverflow.com/a/56989647/5372400
*/
function replaceAll_(str, find, newToken, caseSensitive) {
var i = -1;
// sanity check & defaults
if (!str) {
// Instead of throwing, act as
// COALESCE if find == null/empty and str == null
if ((str == null) && (find == null))
return newToken;
return str;
}
if (!find || find === ''){ return str; }
if (find === newToken) { return str; }
caseSensitive = caseSensitive || false;
find = !caseSensitive ? find.toLowerCase() : find;
// search process, search by char
while ((
i = (!caseSensitive ? str.toLowerCase() : str).indexOf(
find, i >= 0 ? i + newToken.length : 0
)) !== -1
) {
str = str.substring(0, i) +
newToken +
str.substring(i + find.length);
}
return str;
}
Monster Formula
I've used the RegEx algorithm to solve it with native functions. This method is not recommended as it slows down your Worksheet.
The formula is:
=INDEX(SUBSTITUTE(REGEXREPLACE(TRANSPOSE(QUERY(TRANSPOSE(IFERROR(SPLIT(SUBSTITUTE(TRANSPOSE(QUERY(TRANSPOSE(IFERROR(VLOOKUP(SPLIT(REGEXREPLACE(A12:A;SUBSTITUTE(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1");"𑇡";"(.*)");INDEX(REGEXREPLACE(TRIM(TRANSPOSE(QUERY(TRANSPOSE(IF(SEQUENCE(COUNTA(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2));MAX(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2)))-(SEQUENCE(COUNTA(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2)))-1)*MAX(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2))<=INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2);"𑇣"&SEQUENCE(COUNTA(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2));MAX(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2)))-(SEQUENCE(COUNTA(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2)))-1)*MAX(INDEX(LEN(REGEXREPLACE(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡");"[^𑇡]";""))/2))&"𑇤";));;2^99)));" ?𑇣";"$")));"𑇤");{List!A1:A\List!B1:B};2;)&"𑇩"));;2^99));"𑇩 ";"𑇩")&"𝅘";"𑇩")&SPLIT(REGEXREPLACE(A12:A;"(?i)"&SUBSTITUTE(SUBSTITUTE(QUERY(FILTER(REGEXREPLACE(List!A1:A;"(\\|\+|\*|\?|\[|\^|\]|\$|\(|\)|\{|\}|\=|\!|\<|\>|\||\:|\-)";"\\$1")&"𑇦";List!A1:A<>"");;2^99);"𑇦 ";"|");"𑇦";"");"𑇡")&"𝅘";"𑇡")))&"𝅗";;2^99));"𝅗 *";"");"𝅘";""))
Other Solutions
Nested formulas
Nested SUBSTITUTE or REGEXREPLACE formulas as was noted in other answers.
Formulas you need to drag down for the result
Here's a sample formula. Basic logic - split the text into parts → modify parts individually → to join the new result.
This formula must be copied down:
=JOIN(" ";
ArrayFormula(
IFERROR(VLOOKUP(TRANSPOSE(SPLIT(A1;" "));List!A:B;2;0);TRANSPOSE(SPLIT(A1;" ")))))
An improvement on JPV's answer, which is orders of magnitude faster and works with arbitrary query and replacement strings:
=ArrayFormula(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(A1:A, List!A1, List!B1), List!A2, List!B2), List!A3, List!B3))
Using this format, a 15,000 cell spreadsheet with an 85-length replacement list will update in just a few seconds. Simply assemble the formula string using your scripting language of choice and you're good to go!
With new Labmda and Friends:
=LAMBDA(data,re,with,BYROW(data,LAMBDA(r,if(r="","",REDUCE(r,SEQUENCE(counta(re)),LAMBDA(ini,v,REGEXREPLACE(ini,INDEX(re,v),INDEX(with,v))))))))(C5:C6,E5:E7,F5:F7)
=> Named function
=SUBSTITUTES_RE(list0,list_re,list_with)
↑ This will substitute using regular expressions
substututes
Definition is the same, but REGEXREPLACE is replaced with SUBSTITUTE
Other examples here:
https://docs.google.com/spreadsheets/d/1IMymPZlibT6DX4yzDX4OXj2XBZ48zEl6vBUzIHJIzVE/edit#gid=0
Here is a bit simpler of a script than Thiago Mata's. I modified the script from https://webapps.stackexchange.com/a/46895 to support either single cell or range input
function MSUBSTITUTE(input, subTable)
{
var searchArray = [], subArray = [], outputArray = [];
for (var i = 0, length = subTable.length; i < length; i++)
{
if (subTable[i][0])
{
searchArray.push(subTable[i][0]);
subArray.push(subTable[i][1]);
}
}
var re = new RegExp(searchArray.join('|'), 'g');
/* Check if we got just a single string */
if (typeof( input ) == "string")
{
outputArray.push(input.replace(re, function (match) {return subArray[searchArray.indexOf(match)];}));
}
else /* we got an array of strings */
{
for (var i = 0; i < input.length; i++)
{
/* force each array element in the input be a string */
var text = input[i].toString();
outputArray.push(text.replace(re, function (match) {return subArray[searchArray.indexOf(match)];}))
}
}
return outputArray;
}
I've found a simple way to do this with "ARRAYFORMULA"
You must have one list with the text to find and in a contiguos column, the list you want to replace de data, for example:
#
D
E
1
ToFind
ToReplace
2
Avoc4do
Avocado
3
Tomat3
Tomate
4
On1on
Onion
5
Sug4r
Sugar
then use this formula
=ARRAYFORMULA(FIND(A1:A1000,D1:D5,E1:E5))
A1:A1000 is the original column where you have multiple rows with the word "Avoc4do, Tomat3, On1on, Sugar", ArrayFormula works with a matrix where others formulas can't (formula FIND can't work finding in a matrix, so we use ArrayFormula)
Then you will have a colum with the 1000 rows but now with the "ToReplace" text in order, so now cut and copy in the column A, that's it.
Got it
Lorem ipsum dolor sit xamet Lorem ipsum
= textjoin("";true;ARRAYFORMULA(ifna(vlookup(REGEXEXTRACT(A1;"("®EXREPLACE(A1;"("&(textJOIN("|";true;lookuprange))&")";")($1)(")&")");lookuprange;2;false);REGEXEXTRACT(A1;"("®EXREPLACE(A1;"("&(textJOIN("|";true;lookuprange))&")";")($1)(")&")"))))
Xlorem ipsum dolor sit Xamet Xlorem ipsum
How to autofit content in cell using jxl api?
I know this is an old question at this point, but I was looking for the solution to this and thought I would post it in case someone else needs it.
CellView Auto-Size
I'm not sure why the FAQ doesn't mention this, because it very clearly exists in the docs.
My code looked like the following:
for(int x=0;x<c;x++)
{
cell=sheet.getColumnView(x);
cell.setAutosize(true);
sheet.setColumnView(x, cell);
}
c stores the number of columns created
cell is just a temporary place holder for the returned CellView object
sheet is my WriteableSheet object
The Api warns that this is a processor intensive function, so it's probably not ideal for large files. But for a small file like mine (<100 rows) it took no noticeable time.
Hope this helps someone.
The method is self explanatory and commented:
private void sheetAutoFitColumns(WritableSheet sheet) {
for (int i = 0; i < sheet.getColumns(); i++) {
Cell[] cells = sheet.getColumn(i);
int longestStrLen = -1;
if (cells.length == 0)
continue;
/* Find the widest cell in the column. */
for (int j = 0; j < cells.length; j++) {
if ( cells[j].getContents().length() > longestStrLen ) {
String str = cells[j].getContents();
if (str == null || str.isEmpty())
continue;
longestStrLen = str.trim().length();
}
}
/* If not found, skip the column. */
if (longestStrLen == -1)
continue;
/* If wider than the max width, crop width */
if (longestStrLen > 255)
longestStrLen = 255;
CellView cv = sheet.getColumnView(i);
cv.setSize(longestStrLen * 256 + 100); /* Every character is 256 units wide, so scale it. */
sheet.setColumnView(i, cv);
}
}
for(int x=0;x<c;x++)
{
cell=sheet.getColumnView(x);
cell.setAutosize(true);
sheet.setColumnView(x, cell);
}
It is fine, instead of scanning all the columns. Pass the column as a parameter.
void display(column)
{
Cell = sheet.getColumnView(column);
cell.setAutosize(true);
sheet.setColumnView(column, cell);
}
So when you wiill be displaying your text you can set the particular length. Can be helpfull for huge excel files.
From the JExcelApi FAQ
How do I do the equivilent of Excel's "Format/Column/Auto Fit Selection"?
There is no API function to do this for you. You'll need to write code that scans the cells in each column, calculates the maximum length, and then calls setColumnView() accordingly. This will get you close to what Excel does but not exactly. Since most fonts have variable width characters, to get the exact same value, you would need to use FontMetrics to calculate the maximum width of each string in the column. No one has posted code on how to do this yet. Feel free to post code to the Yahoo! group or send it directly to the FAQ author's listed at the bottom of this page.
FontMetrics presumably refers to java.awt.FontMetrics. You should be able to work something out with the getLineMetrics(String, Graphics) method I would have though.
CellView's autosize method doesn't work for me all the time. My way of doing this is by programatically set the size(width) of the column based on the highest length of data in the column. Then perform some mathematical operations.
CellView cv = excelSheet.getColumnView(0);
cv.setSize((highest + ((highest/2) + (highest/4))) * 256);
where highest is an int that holds the longest length of data in the column.
setAutosize() method WILL NOT WORK if your cell has over 255 characters. This is related to the Excel 2003 max column width specification: http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP005199291.aspx
You will need to write your own autosize method to handle this case.
Try this exemple:
expandColumns(sheet, 3);
workbook.write();
workbook.close();
private void expandColumn(WritableSheet sheet, int amountOfColumns){
int c = amountOfColumns;
for(int x=0;x<c;x++)
{
CellView cell = sheet.getColumnView(x);
cell.setAutosize(true);
sheet.setColumnView(x, cell);
}
}
Kotlin's implementation
private fun sheetAutoFitColumns(sheet: WritableSheet, columnsIndexesForFit: Array<Int>? = null, startFromRowWithIndex: Int = 0, excludeLastRows : Int = 0) {
for (columnIndex in columnsIndexesForFit?.iterator() ?: IntProgression.fromClosedRange(0, sheet.columns, 1).iterator()) {
val cells = sheet.getColumn(columnIndex)
var longestStrLen = -1
if (cells.isEmpty()) continue
for (j in startFromRowWithIndex until cells.size - excludeLastRows) {
if (cells[j].contents.length > longestStrLen) {
val str = cells[j].contents
if (str == null || str.isEmpty()) continue
longestStrLen = str.trim().length
}
}
if (longestStrLen == -1) continue
val newWidth = if (longestStrLen > 255) 255 else longestStrLen
sheet.setColumnView(columnIndex, newWidth)
}
}
example for use
sheetAutoFitColumns(sheet) // fit all columns by all rows
sheetAutoFitColumns(sheet, arrayOf(0, 3))// fit A and D columns by all rows
sheetAutoFitColumns(sheet, arrayOf(0, 3), 5)// fit A and D columns by rows after 5
sheetAutoFitColumns(sheet, arrayOf(0, 3), 5, 2)// fit A and D columns by rows after 5 and ignore two last rows