How to format a cell formula in lazarus FPSpreadsheet? - pascal

I have a problem in Lazarus using FPSpreadsheet, where I am not able to define a format for a cell.
ex: inline formatting,
Value := 1500.60
MySheet.WriteNumber(0,1,Value, nfFixedTh,2);
I have this result in A2, 1,500.60, but when I define a formula I can't get this formatting.
ex: A2 = 1500.60
A3 = 260.00
MySheet.WriteFormula(0,4,'=SUM(A2:A3)');
The Result is 1760.6, how to format the result cell, so that it looks like this 1,760.60
In excel this is done like this:
MySheet.Range[A2,A3].NumberFormat := '###,##0.00';
But I am not able to do the same with FPSpreadsheet
tks
I tried like this:
A2 = 1500.6
A3 = 260.00
MySheet.WriteFormula(0,4,'=SUM(A2:A3)');
MyCell := MySheet.GetCell(0,4);
MyCel^.NumberFormat := nfFixedTh;
The Result is 1760.6, how to format the result cell, so that it looks like this 1,760.60.
I also reversed calling
MyCell := MySheet.GetCell(0,4);
MyCel^.NumberFormat := nfFixedTh;
before writing the formula

The TsWorksheet has a bunch of formatting methods. Read the wiki article which Tom mentioned.
In your case, in order to format a number cell, you should call one of the WriteNumberFormat methods, like in the following example. BTW, it does not matter whether you apply the format before or after the cell received its value.
program Project1;
uses
fpsTypes, fpspreadsheet, xlsxOOXML;
var
workbook: TsWorkbook;
sheet: TsWorksheet;
begin
workbook := TsWorkbook.Create;
try
sheet := workbook.AddWorksheet('Test');
sheet.WriteNumber(1, 0, 1500.6);
sheet.WriteNumber(2, 0, 260.0);
sheet.WriteNumberFormat(4, 0, nfFixedTh, 2);
sheet.WriteFormula(4, 0, 'Sum(A2:A3)');
workbook.WriteToFile('test.xlsx', true);
finally
workbook.Free;
end;
end.

Related

How to identify cell is merged cell with google sheets api

I'm using Go API https://developers.google.com/sheets/api/quickstart/go
I have a sheet looks like this and want to automate some cell filling logic
| B | C |
--+------+------+
1 | b1 | c1 |
--+------+ |
2 | b2 | |
--+------+------+
3 | b3 | |
--+------+------+
While using readRange := "B1:C" by
resp, _ := s.srv.Spreadsheets.Values.Get(spreadsheetId, readRange).Do()
for _, row := range resp.Values {
...
}
I can't tell the difference between C2 and C3. In this case, I would like to fill some values to C3 but not C2. How do I check this via API?
You want to retrieve the information of merge cells in a sheet using Sheets API.
From your replying, I could understand like above. If my understanding is correct, the method of spreadsheets.values.get cannot retrieve the information of merge cells. In this case, please use the method of spreadsheets.get. The modified script is as follows.
Modified script:
ranges := []string{"B1:C"}
resp, _ := s.srv.Spreadsheets.Get(spreadsheetId).Ranges(ranges...).Do()
for _, sheet := range resp.Sheets {
for _, merge := range sheet.Merges {
fmt.Printf("%#v\n", merge)
}
}
Note:
This modified script supposes that you have already used Sheets API.
Range of B1:C means B1:C of the first tab.
When you run above script, you can retrieve the coordinates of the merged cells as the GridRange like below.
{
"sheetId": 0,
"startRowIndex": 0,
"endRowIndex": 2,
"startColumnIndex": 2,
"endColumnIndex": 3
}
This GridRange is C1:C2 of sheet ID 0 as the a1Notation.
From above result, it is found that C1:C2 is the merged cell.
In this case, when the value is put in C1, the value is displayed. But when the value is put in C2, the value is not displayed. Please be careful this.
If you want to put the value using the GridRange, you can use the method of spreadsheets.batchUpdate.
References:
spreadsheets.get
GridRange
spreadsheets.batchUpdate

Reading a text file and constructing a matrix from it

I need to construct a matrix; a number of columns and rows are also in the first row of the matrix, I'll make an example so its more clearer.
4 3
1 2 3
5 6 7
9 10 8
1 11 13
Where m=4 (number of rows) and n=3 (number of columns)
This is an example of a text file. Is something like this even possible?
Program Feb;
const
max=100;
type
Matrix=array[1..max,1..max] of integer;
var datoteka:text;
m,n:integer;
counter:integer;
begin
assign(datoteka,'datoteka.txt');
reset(datoteka);
while not eoln(datoteka) do
begin
read(datoteka, m);
read(datoteka, n);
end;
repeat
read eoln(n)
until eof(datoteka)
write (m,n);
end.
My code isn't a big help, cause I don't know how to write it.
First, have a look at the code I wrote to do the task, and then look at my explanation below.
program Matrixtest;
uses
sysutils;
var
NoOfCols,
NoOfRows : Integer;
Source : TextFile;
Matrix : array of array of integer;
FileName : String;
Row,
Col : Integer; // for-loop iterators to access a single cell of the matrix
Value : Integer;
begin
// First, construct the name of the file defining the matrix
// This assumes that the file is in the same folder as this app
FileName := ExtractFilePath(ParamStr(0)) + 'MatrixDef.Txt';
writeln(FileName); // echo it back to the screen so we can see it
// Next, open the file
Assign(Source, FileName);
Reset(Source);
read(Source, NoOfRows, NoOfCols);
writeln('Cols: ', NoOfCols, 'Rows: ', NoOfRows);
SetLength(Matrix, NoOfCols, NoOfRows);
readln(source); // move to next line in file
// Next, read the array data
for Row := 1 to NoOfRows do begin
for Col := 1 to NoOfCols do begin
read(Source, Value);
Matrix[Col - 1, Row - 1] := Value;
end;
end;
// Display the array contents
for Row := 1 to NoOfRows do begin
for Col := 1 to NoOfCols do begin
writeln('Row: ', Row, ' contents', Matrix[Col - 1, Row - 1]);
end;
end;
Close(Source); // We're done with the file, so close it to release OS resources
readln; // this waits until you press a key, so you can read what's been displayed
end.
In your program, you can use a two-dimensional array to represent your matrix. Free Pascal supports multi-dimensional arrays; see https://wiki.lazarus.freepascal.org/Multidimensional_arrays for more information.
This is a complex task, so it helps to know how to do more basic things like reading an array of a size known at compile-time from a text file.
The wrinkle in this task is that you are supposed to read the dimensions (numbers of rows and columns) of the matrix at run-time from the file which contains the matrix's contents.
One inefficient way to do this would be to declare the matrix array with huge dimensions, larger than anything you would expect in practice, using the type of array declaration in the Wiki page linked above.
A better way is to use dynamic arrays, whose dimensions you can set at run-time. To use this, you need to know:
How to declare a dynamic array in Free Pascal
How to set the dimensions of the array at run-time, once you've picked them up from your matrix-definition file (hint: SetLength is the way to do this)
The fact that a Free Pascal dynamic array is zero-based
The easiest way of managing zero-based arrays is to write your code (in terms of Row and Column variables) as if the matrix were declared as array[1..NoOfRows, 1..NoOfColumns] and subtract one from the array indexes only when you actually access the array, as in:
Row := 3;
Column := 4;
Value := Matrix[Row - 1, Column - 1];

Transpose a string in Pascal

I'm very new to Pascal and still learning much. I have to write a code that :
Takes input of a string
Split the string into two characters each (Snippet)
Use the Snippet to get an index from an array
Transpose the Snippet to a certain value
If Index + Transpose is larger than the length of the Array, return nothing
If not, append the transposed Snippet to a result string
Return the transposed string
I can only write 1 through 3, the rest is still a blur for me. Helps are appreciated.
(And I also want to improve it without many for loops. Any thoughts?)
program TransposeString;
var
melody : Array[1..24] of String[2] = ('c.', 'c#', 'd.', 'd#', 'e.', 'f.', 'f#', 'g.', 'g#', 'a.', 'a#', 'b.', 'C.', 'C#', 'D.', 'D#', 'E.', 'F.', 'F#', 'G.', 'G#', 'A.', 'A#', 'B.');
songstring, transposedstring : String;
transposevalue : byte;
function Transpose(song : String; transposevalue : byte): String;
var
songsnippet : String[2];
iter_song, iter_index, index : byte;
begin
for iter_song := 1 to length(song) do
begin
if iter_song mod 2 = 0 then continue;
songsnippet := song[iter_song] + song[iter_song + 1]; //Split the string into 2 characters each
for iter_index := 1 to 24 do
begin
if melody[iter_index] = songsnippet then
begin
index := iter_index; //Get Index
break;
end;
end;
//Check Transpose + Index
//Transpose Snippet
//Append Snippet to Result String
end;
end;
begin
readln(songstring);
readln(transposevalue);
transposedstring := transpose(songstring, transposevalue);
writeln(transposedstring);
end.
As a starter for you to work from, rather than just spoon-feeding an answer:
You have the index of the snippet (note) in index. Assuming the notes are in order you need to return the note from the array positions above it, so
result := result + melody[iter_index + transposevalue];
You need to check the length of the array before trying to read from it, otherwise it'll crash (step 5). This is just an if statement.
I wouldn't worry too much about for loops - 2 deep nesting isn't that bad. If you wanted to split it out a bit then GetTransposedNote(const note:string): string; could be split out as a new function.
Things you may want to think about are:
What if you can't find the note in the array?
Do you want to be case-sensitive
What if the input string has an odd number of characters?
You are most of the way there already, though.

Fast way to search lines of Tmemo

I have a TMemo on a form which allows users to enter a list of items. People can enter many items here. When they click Save the contents of the TMemo is checked and then added to the database.
I have a second list in a TStringList which I loop over and check to see if any of it's items are contained in the TMemo.
In a nut shell it looks like this
....
//slItems = TStringList
//mItems = TMemo
for i := slItems.Count -1 downto 0 do
begin
if mItems.Lines.IndexOf(slItems[i]) = -1 then
slItems[i].Delete;
end;
----
So stringlist looped, check to see if it exists in memo, if not delete from list.
However, with 200+ items this is starting to slow down a lot, and with 1000 it gets real bad.
Whats the fastest way to search a TMemo?
Read all of TMemo into a local TStringList and work from that. Every time you're accessing TMemo.Lines you're relying on Windows messaging to talk to the windows-provided multi line text box. Anything but efficient!
....
//slItems = TStringList
//mItems = TMemo
//L = TStringList
L.Text := mItems.Text; // edited per David's suggestion.
L.Sorted := True; // per Uwe Raabe's suggestion.
for i := slItems.Count -1 downto 0 do
begin
if L.IndexOf(slItems[i]) = -1 then
slItems[i].Delete;
end;
----

Pascal programming help

I posted this earlier and it got closed because I did not show my try at coding it, so here is the question:
SECTIONS
$160 = section 1
$220 = section 2
$280 = section 3
$350 = section 4
$425 = section 5
Develop pseudocode that accepts as input the name of an unspecified number of masqueraders who each have paid the full cost of their costume and the amount each has paid.
A masquerader may have paid for a costume in any of the five sections in the band. The algorithm should determine the section in which the masquerader plays, based on the amount he/she has paid for the costume. The algorithm should also determine the number of masqueraders who have paid for costumes in each section.
The names of persons and the section for which they have paid should be printed. A listing of the sections and the total number of persons registered to play in each section should also be printed, along with the total amount paid in each section.
Here is my try at it: *Note this is programmed in Pascal, and I need help fixing it up and finishing it. Please help and thanks again.
program Masqueraders;
uses
WinCrt; { Allows Writeln, Readln, cursor movement, etc. }
const
MAX = 5; {this determine the amount of masquarader entered}
Type
listname = Array[1..MAX] of string;
listsect = Array[1..MAX] of string;
var
names : listname;
sections : listsect;
i, amount, TotalMas, TotalAmt, c1, c2, c3, c4, c5, amt1, amt2, amt3, amt4, amt5 : integer;
begin
amount := 1;
while amount <> 0 do
begin
i := i + 1;
readln(names[i]);
readln(amount);
if(amount = 160) then
begin
c1 := c1 + 1; {Count the number of persons for section 1}
amt1 := amt1 + amount; {accumulate the amount for section 1}
sections[i] := 'Section 1';
end;
if(amount = 220) then
begin
c2 := c2 + 1; {Count the number of persons for section 1}
amt2 := amt2 + amount; {accumulate the amount for section 1}
sections[i] := 'Section 2';
end; {end the IF for section 2}
if(amount = 280) then
begin
c3 := c3 + 1; {Count the number of persons for section 1}
amt3 := amt3 + amount; {accumulate the amount for section 1}
sections[i] := 'Section 3';
end; {end the IF for section 3}
if(amount = 350) then
begin
c4 := c4 + 1;
amt4 := amt4 + amount;
sections[i] := 'Section4';
end; {end If for section 4}
if (amount = 425) then
begin
c5 := c5 + 1;
amt5 := amt5 + amount;
sections[i] := 'Section5';
end;{end the while loop}
TotalMas := c1 + c2 + c3;
TotalAmt := amt1 + amt2 + amt3;
writeln('Name Section'); {Heading for the output}
for i := 1 to MAX do
begin
write(names[i]);
writeln(' ',sections[i]);
end;
writeln('Section 1: ');
write('Masquader: ', c1);
write('Amount: ', amt1);
writeln('Total Number of Masquarader: ', TotalMas);
writeln('Total Amount Paid by masquarader: ', TotalAmt);
end;
end.
In short, it should accept an undefined number of people and assign them to their respective sections based on the amount of money they entered, then calculate the number of people in each section. This is my current output:
Name John Money=160 Section 1
Name Keith Money=220 Section John
This is what I want:
Name John Money=160 Section1
Name Keith Money=220 Section2
I'm not really familiar with Pascal so I can't tell you how to fix this but one issue I notice is that your code seems to violate the "DRY" rule: Don't Repeat Yourself. The code inside each if amt = xxx block looks almost exactly the same, so is there a way you can write that code once, and then call your code with different parameters each time? Something so that you're not rewriting the same code five times.
Here's are a few hints to improve your code:
Do you think there might be a way to print "Section {i}" in your final loop without looking it up using sections[i], thereby negating the need for the array completely?
How could you build this so that adding a section 6 wouldn't require modifications to your code?
listname and listsect are awfully similar, what modifications to your code could you do to eliminate the need to have two identical definitions?
What happens if the user enters 0 for the amount the third time they are prompted?
Note: one of these hints should point you directly to the source of your problem.
Here are the problems I see:
The requirements say an "unspecified number of masqueraders" but you have set the max to 5. You can't store the list of names in a fixed size array if there are possibly going to be more than 5 masqueraders. As is, the app may either crash or corrupt memory (depending on the version of Pascal you're using) when user enters the 6th masquerader. If you are allowed to print the masquerader's Name and Section immediately after it is input, then you should print that right after the user inputs the Name and Amount (not after the input while loop). However, if it is required that you print the list of Names and Sections after all input, then you need to store the Name+Section in a variable length data structure like a linked list or even simpler a string.
The variables c1 to c5 and amt1 to amt5 would be better declared as two arrays (c and amt) of 1..MAX instead of 10 variables. When user inputs the amount, use it determine the section number which then can be used as the index into the c and amt arrays.
Not sure what implementation of Pascal you're using but it would be safer to initialize all variables before use otherwise they could contain unpredictable values.
The sections array is unnecessary. All it ends up containing is the title of the section which doesn't need to be calculated or stored.
If you used a repeat/until loop instead of a while loop, it would avoid the slightly kludgy "amount := 1" initialization at the top to enter the while loop. The repeat/until would be until Amount = 0.
TotalMas is only adding c1 to c3 (what about c4 and c5)?
TotalAmt is only adding amt1 to amt3 (what about amt4 and amt5)?
The for-loop to print the names and section is completely wrong, see points 1 and 4.
Hope this helps and let me know if you need clarification on any points.
My gripes with this code:
1) The variable names stink. Virtually all of them are way too short, they do not communicate what they do.
2) The wrong loop control structure is used here.
3) I see virtually identical code repeated 5 times. This is simply crying out for arrays.
4) The comments explain what you are doing, not why you are doing it. Half of them are virtually completely useless, if you're going to comment an end you should simply comment it with the identity of whatever it's an end to. end; //if I see only one comment that even tries to explain why and you've got it wrong as you're mixing up people with sections.
5) Your sections array accomplishes nothing useful. The assignments to the values are always fixed and thus there is no reason to store them at all--if you need the labels you can create them at print time.
6) You are assuming there are only going to be 5 people. That's not given in the problem--there's 5 sections.

Resources