I have an inefficient code. I need some help. Here’s the explanation:
On a project, I build an html file and an index at the beginning of the file (using a <TABLE…).
That TABLE have a max of 4 columns. I wrote code to achieve that goal, but the code is reliably… “wrong” !
I found a variant, but the “wrong” change …
What’s wrong ? In the result, case of the project example, if I have a number of lines to place in the table is a multiple of 4, I have a 5th column (and empty) that is created.
In the other version, if the number of lines in NOT divisible by 4, the files are sorted correctly in the Colums… ELSE: the Last column is not completely filled…
I take the time to create an example, believing it will help me to found what is wrong, but no, I failed.
Did you have ideas of how to correct the code ?
The used code (from the example’s button):
Dim Loc_Col As Integer = 1
Dim Total_Cnt As Integer = Val(TF.Text)
Dim Row_Cnt As Integer = (Total_Cnt \ 4) + 1 // Remove " + 1 " for the worst result !
Dim html_Col As Integer = 1
Dim EOL As String = EndOfLine
Dim Sommaire_List As String = "<table><tr><td>Column " + Str(html_Col) + EOL
Dim Loop_Idx As Integer
For Loop_Idx = 1 To Total_Cnt
// Display the file entries
Sommaire_List = Sommaire_List + Str(Loop_Idx) + ". This is a file entry.<br>" + EOL
// If the Column is full, add a brand new one…
If Loc_Col = Row_Cnt Then
html_Col = html_Col + 1
// Place the end of Column / Start of column tags "</td><td>"
Sommaire_List = Sommaire_List + "</td>" + EOL + "<td width=25%>Column " + Str(html_Col) + EOL
// Reset the Column Count
Loc_Col = 1
Else
// Increase the # of columns
Loc_Col = Loc_Col + 1
End If
Next
// Report the result
TA.Text = Sommaire_List + "</td></tr>" + EOL + "</table>" + EOL
84 MOD 4 = 0: this is the important computation to understand the screen shot above.
So, the code had to display 21 lines in each Column, but you can see it places 22 lines / Column excepted in the last Column where there is room for 4 columns.
Somewhere in my code, if I remove “+ 1” (where I cound the number of Rows), and in this specific case, the result is different (21 lines in each Column), but I get a 5th Column (empty).
In HTML tables you don’t fill up columns and pass to another one, you fill up rows, containing all columns, so you must compute every row and fill up all the 4 columns of it at once, including empty cells.
As you can see in the screen shot, in the original project, they are links to the issues description (it’s a magazine description document).
I do not need a …/… for each “line” (group of 4 links/entries); one is enough.
The main problem is that you decide how many things to put in each row using
(Total_Cnt \ 4)
If this is divisible by 4, it works.
eg 24 gets you 6 things in each column.
27 still suggests that each column needs 6 things, but you have 3 left over.
When you hit 25, your code adds an unwanted column.
Without rewriting your code for you, here is one approach…
Consider 27 Total_Cnt \ 4 gets you 6 Total_Cnt mod 4 gets you 3… the extra items
Add a variable called Pseudo_Cnt
Pseudo_cnt = (Total_Cnt \ 4) *4 //eg for 27, this gets you 24
if (Total_Cnt mod 4) <> 0 then
Pseudo_cnt = Pseudo_cnt = +4 // eg for 27, this gets you 28
end if
//for 27, you will get 7 items per column, but one column needs only 6
now, loop from 1 to Pseudo_cnt instead of 1 to Total_Cnt
and only add an item if Loop_Idx <= Total_Cnt
The actual code works fine only if the total count is NOT divisible by 4.
I use this project since… two years ago many times a week, and in most of the cases Total_Cnt MOD 4 <> 0… but yesterday, Total_Cnt was 84… And I wanted to squash the bug when I saw an empty 5th column (this reminds me something).
When Total_Cnt MOD 4 <> 0 the index is correct.
I confess, I have to implement your advice (as is, I do not understand how it can work), I will do it after a nap (for max brain possible).
Now, maybe if i use a different code design to populate a Row at a time (using a step in the Loop… I think I have the source FolderItems in an Array.
What’s important is the final result; one Row/Many line in it vs Many Rows, I will take the one that always show the correct solution.
Maybe extracting that code from the main Method allows me to better “see” (or feel) the code…
BTW: my example show variations I have not seen yesterday…
Now, I understand… I already falled into that trap (with Tab Character added at the end of a String Concatenation). Usually, I remove the unwanted last Tab (last char of the string) after the loop.
I remember (how is it possible ?) that I tried => and <= variations, but that failed… when I added the html index to my html code generation.
This works better; I simplified it a bit. You had too many variables doing the counting. You would need some CSS to get the content of the last column to be aligned properly. It’s too long since I did html so I’ll leave that to you.
Var i, ColNum, RowNum, RowLim, TotalCnt As Integer, Sommaire_List As String
ColNum = 1
RowNum = 1
TotalCnt = 86
RowLim = (TotalCnt \ 4) + 1
Sommaire_List = "<!doctype html><head></head><body><table><tr><td>Column " + ColNum.ToString + "<br>"
For i = 1 To TotalCnt
// Display the file entries
Sommaire_List = Sommaire_List + i.ToString + ". This is a file entry.<br>"
RowNum = RowNum + 1
// If the Column is full, add a brand new one (unless at the end)
If (RowNum=RowLim and i<TotalCnt) Then
// Place the end of Column / Start of column tags "</td><td>"
ColNum = ColNum + 1
Sommaire_List = Sommaire_List + "</td><td>Column " + ColNum.ToString + "<br>"
// Reset the Row Count
RowNum = 1
end if
Next
// Report the result
Sommaire_List = Sommaire_List + "</td></tr></table></body></html>"
HTMLViewer1.LoadPage (Sommaire_List, Nil)
In any case, as @Rick_Araujo said, you should be filling as many <ROW>s as you need, not using <BR>. I’ll try to change my example to do that.
Here it is done with many rows. This does mean that the entries are numbered across rather than down, which may not be what you want.
Note that with both methods, you avoid the blank column (or row) with the extra test in the if.
Var i, ColNum, ColLim, TotalCnt As Integer, Sommaire_List As String
ColNum = 1
ColLim = 4
TotalCnt = 83
Sommaire_List = "<!doctype html><head></head><body><table><tr>"
for i = 1 to ColLim
Sommaire_List = Sommaire_List + "<td>Column " + i.ToString + "</td>"
next
Sommaire_List = Sommaire_List + "</tr>"
For i = 1 To TotalCnt
// Display the file entries
Sommaire_List = Sommaire_List + "<td>" + i.ToString + ". This is a file entry.</td>"
ColNum = ColNum + 1
// If the Row is full, add a brand new one (unless at the end)
If (ColNum>ColLim and i<TotalCnt) Then
// Start a new row
Sommaire_List = Sommaire_List + "</tr><tr>"
// Reset the Row Count
ColNum = 1
end if
Next
// Report the result
Sommaire_List = Sommaire_List + "</td></tr></table></body></html>"
HTMLViewer1.LoadPage (Sommaire_List, Nil)
Here it is with the numbers increasing down each column. Slightly more complex.
Var i, j, ColNum, ColLim, RowNum, RowCnt, TotalCnt, DodCols As Integer, Sommaire_List As String
ColNum = 1
RowNum = 1
ColLim = 4
TotalCnt = 88
RowCnt = (TotalCnt \ ColLim) + 1
DodCols = TotalCnt mod ColLim
Sommaire_List = "<!doctype html><head></head><body><table width=100%><tr>"
for i = 1 to ColLim
Sommaire_List = Sommaire_List + "<td>Column " + i.ToString + "</td>"
next
Sommaire_List = Sommaire_List + "</tr>"
For i = 1 To TotalCnt
j = RowNum + ((ColNum-1) * (RowCnt-1)) + (if (ColNum>DodCols, DodCols, (ColNum-1)))
// Display the file entries
Sommaire_List = Sommaire_List + "<td>" + j.ToString + ". This is a file entry.</td>"
ColNum = ColNum + 1
// If the Row is full, add a brand new one (unless at the end)
If (ColNum>ColLim and i<TotalCnt) Then
RowNum = RowNum + 1
Sommaire_List = Sommaire_List + "</tr><tr>"
// Reset the Column Count
ColNum = 1
end if
Next
// Report the result
Sommaire_List = Sommaire_List + "</td></tr></table></body></html>"
HTMLViewer1.LoadPage (Sommaire_List, Nil)
Public Function GetItemsToPopulateTable() As String()
// Just simulate some content for tests
Var itemsRead() As String
Var lastItem As Integer = TF.Text.Trim.Val() -1 // get the mumber of items from UI
For i As Integer = 0 to lastItem
itemsRead.Add "Subject " + i.ToString
Next
Return itemsRead
End Function
Public Function GetHTMLTableFromArray(items() As String, numColumns As Integer = 4, tableWidth As String = "90%") As String
// Create a HTML table string using the parameters and return it
Var numItems As Integer = items.Count
Var numRows As Integer = numItems \ numColumns + If(numItems mod numColumns > 0, 1, 0)
Var numItemsGrid As Integer = numRows * numColumns // includes empty cells at end
Var htmlTable As String = "<table style=""width:"+tableWidth+""">" + EndOfLine // Start the html table
For row As Integer = 0 to numRows - 1
htmlTable = htmlTable + "<tr>"
For Col As Integer = 0 to numColumns - 1
Var mappedItem As Integer = row + col * numRows // transpose into columns
htmlTable = htmlTable + "<td>" + If(mappedItem >= numItems, " ", items(mappedItem)) + "</td>"
Next
htmlTable = htmlTable + "</tr>" + EndOfLine
Next
Return (htmlTable + "</table>" + EndOfLine) // end the html table and return it
End Function
Sub Action() Handles Action
// Run
Var html As String = GetHTMLTableFromArray( GetItemsToPopulateTable() )
TA.Text = html
HTMLViewer1.LoadPage(html, FolderItem.TemporaryFile)
End Sub