Split up strings into RTF table cells and max total width - vb.net

VB2010. May be a bit hard to understand but I have a list of classes and one field is a string message. I'm outputting these messages to an RTF document but want to maximize use of horizontal space so am trying to dynamically create a table and fit as many messages in one row as possible and then another row. This while I maintain a max width possible for a row.
Public Class TripInfo
Public SysId As String = ""
Public CreateDate As DateTime
Public OutMessage As String = ""
Public OutMessageWidth As Integer = 0 'the width of the message in char count up to first LF
End Class
Dim myTrips1 as New List(Of TripInfo)
Dim myTrips2 as New List(Of TripInfo)
So as I iterate through the lists I want to create rows that are themselves no longer than 45 characters. Something like:
---------------------------------------------
|"Message1 |"Message2 |"Much longer message |
| Trip1 "| Trip2" | with two lines" |
---------------------------------------------
|"message is even longer than the others" |
---------------------------------------------
|"trip is ok |"trip was cancelled due to CW |
| enroute" | must log to system" |
---------------------------------------------
|"Message3 |"Message4 |"Message5 |"Message6"|
| Trip3 "| Error" | Stop" | |
---------------------------------------------
*Note that the message itself can span more than 1 line with LFs to display a multi-line message
I have scratch code to write the RTF code for the tables and have substituted fake messages with multiple embedded LFs and the output looks good.
Dim sbTable As New StringBuilder
sbTable.Append("\pard \trowd\trql\trgaph108\trleft36\cellx1636\cellx3236\cellx4836\intbl R1C1\cell R1C2\cell R1C3\cell\row \pard")
sbTable.Append("\pard \trowd\trql\trgaph108\trleft36\cellx4642\intbl R1C1\cell\row \pard")
sbTable.Append("\pard \trowd\trql\trgaph0\trleft36\cellx4642\cellx5500\intbl R1C1\cell R1C2\cell\row \pard")
However I just cant seem to get my head around how to even start this to do it dynamically. It seems like I may need to do two iterations. One to break up the messages into rows and then another to actually write the RTF code.
I have so far pseudo code but need some help with my logic.
dim v as integer = 0 'total width of current row
For each t as TripInfo in myTrips1 and myTrips2
if (t.OutMessageWidth added to v) > 45 then
start new row and append
else
append to current row
endif
Next t

Without knowing the properties of your TripInfo class, I'm going to have to make some assumptions. If any property I assume doesn't exist, you can either create it or modify the code to get the same effect.
Dim t As TripInfo, AllTrips As New List(Of TripInfo)
For Each t In myTrips1
AllTrips.Add(t)
Next
For Each t In myTrips2
AllTrips.Add(t)
Next
If AllTrips.Count > 0 Then
For Each t In AllTrips
Dim NewRow() As String = t.Lines
Dim w As Integer = t.OutMessageWidth
Dim h As Integer = t.Lines.Count
For ItemHeight As Integer = h To 1 Step -1
For Each CompareTrip As TripInfo In AllTrips
If AllTrips.IndexOf(t) <> AllTrips.IndexOf(CompareTrip) _
And CompareTrip.Lines.Count = ItemHeight _
And w + CompareTrip.OutMessageWidth <= 45 Then
w += CompareTrip.OutMessageWidth
For l As Integer = 0 To h -1
NewRow(l) = NewRow(l).PadRight(w) & CompareTrip.Lines(l)
Next
AllTrips.Remove(CompareTrip)
End If
Next
Next
AllTrips.Remove(t)
'Write lines of NewRow to your RTF
Next
End If

Related

How to edit this loop?

I have written a small piece of code which doesn´t work like I want.
My goals:
I have a textbox with 3 lines. Each line = one product.
I have a second textbox with 2 lines. Each line = 1 variant of a product.
My result should be something like this:
Artificial grass | Red
Artificial grass.1 | Blue
Dark hole plate | Red
Dark hole plate.1 | Blue
Test hole plate | Red
Test hole plate.1 | Blue
I have this code (deleted all the unneccessary parameters for you):
For value1 As Integer = 0 To NumberOfArticles - 1
Dim Name As String = ProductTB.Lines(value1)
Dim Variants As String = VariantsTB.Lines(value1)
For value2 As Integer = 1 To VariantsCountTB.Text - 1 'TextBox with number of variants
DataGridView1.Rows.Add(New String() { VariantsTB.Lines(value2 - 1)})
DataGridView1.Rows.Add(New String() {VariantsTB.Lines(value2)})
Next
If value1 = NumberOfArticles Then
Exit For
End If
Next
Name is each line of the product textbox.
Variants is each line of the variants TextBox.
The problem: It works, but ONLY if there are as many variants as products. But I need for example only 2 variants, but 3 products. So, where to edit this loop?
Thank you very much! :)
Best regards
As jmcilhinney mentioned use lines() to save textbox text in a array and then do the looping on the same
''Get all Products in array
Dim products As String() = ProductTB.Lines()
''Get all attributes in array
Dim attrributes As String() = VariantsTB.Lines()
''Create as list for result
Dim resultlist As New List(Of String)
''Loop over product
For Each prod As String In products
''Loop over Attributes
For Each atr As String In attrributes
Dim resultprd = prod & "|" & atr
resultlist.Add(resultprd)
Next
'' if you want proiducts without attributes also add here
''resultlist.Add("Prod")
Next

How to count duplicate entries in OpenOffice/LibreOffice BASIC?

I have a gnarly amount of data across many sheets in LibreOffice -- an ADDRESS column and a DATA column -- and I'd like to count the number of times each address occurs, put into a NUM_ADDR column. E.g.:
ADDR | DATA | NUM_ADDR
00000000bbfe22d0 | 876d4eb163886d4e | 1
00000000b9dfffd0 | 4661bada6d4661ba | 1
00000000b9dfc3d0 | 5d4b40b4705d4b40 | 1
00000000b9def7d0 | 8f8570a5808f8570 | 1
00000000b9de17d0 | 63876d4eb163886d | 1
00000000b9dddfd0 | 6d4eb163886d4eb1 | 3
00000000b9dddfd0 | 705d4b40b4705d4b |
00000000b9dddfd0 | b4705d4b40b4705d |
00000000b7df83d0 | 40b4705d4b40b470 | 1
00000000b7d607d0 | 705d4b40b4705d4b | 1
...
When doing things manually I used the COUNTIF function on each address, but I've found that a macro would save time in the long run. Here's a snippet of what I have so far, given that a previous function has already determined the length (number of rows) of the data, stored in RowCounter:
Dim CountedAddr(RowCounter, RowCounter) as String
Dim CountedAddrPtr as Integer
Dim CurrentCell as Object
Dim i as Integer
CountedAddrPtr = 0
' Populate CountedAddr array
For i = 1 to RowCounter-1
CurrentCell = CurrentSheet.getCellByPosition(0, i)
If Not CurrentCell.String In CountedAddr(?) Then
CurrentSheet.getCellByPosition(2, i).Value = 1 ' for debugging
CountedAddr(CountedAddrPtr, 0) = CurrentCell.String
CountedAddrPtr = CountedAddrPtr + 1
Else
CurrentSheet.getCellByPosition(2, i).Value = 0 ' for debugging
EndIf
Next
' For each unique address, count number of occurances
For i = 0 to UBound(CountedAddr())
For j = 1 to RowCounter-1
If CurrentSheet.getCellByPosition(0, j).String = CountedAddr(i, 0) Then
CountedAddr(i, 1) = CountedAddr(i, 1)+1
EndIf
Next
Next
' Another function to populate NUM_ADDR from CountedAddr array...
So my first question is: how can we determine if an element (the address in the current cell) is in the CountedAddr array (see the (?) above)? Second, is there a much more efficient way to achieve the second block of code? Unfortunately sorting is out of the question, since the chronology of the addresses and data form something of a time base. Third, is the whole shebang a foolish way to attack this problem?
Many thanks from a hardware dood on a software task!
Dictionary-type objects such as a VB6 Collection are efficient for looking up items, because it finds the key directly rather than looping through a long array. Our countedAddrs collection below will store a count for each address.
Sub CountAddrs
Dim countedAddrs As New Collection
Dim oCurrentSheet As Object
Dim oCurrentCell As Object
Dim currentAddr As String
Dim i As Integer
Dim newCount As Integer
Dim rowCounter As Integer
Const ADDR_COL = 0
Const COUNT_COL = 2
oCurrentSheet = ThisComponent.CurrentController.ActiveSheet
rowCounter = 11
' Populate countedAddrs array.
For i = 1 to rowCounter - 1
oCurrentCell = oCurrentSheet.getCellByPosition(ADDR_COL, i)
currentAddr = oCurrentCell.String
If Contains(countedAddrs, currentAddr) Then
' Increment the count.
newCount = countedAddrs.Item(currentAddr) + 1
countedAddrs.Remove(currentAddr)
countedAddrs.Add(newCount, currentAddr)
oCurrentSheet.getCellByPosition(COUNT_COL, i).Value = newCount ' for debugging
Else
countedAddrs.Add(1, currentAddr)
oCurrentSheet.getCellByPosition(COUNT_COL, i).Value = 1 ' for debugging
EndIf
Next
End Sub
This code requires the following helper function. In most languages, dictionary objects have this functionality built-in, but Basic is rather simplistic.
' Returns True if the collection contains the key, otherwise False.
Function Contains(coll As Collection, key As Variant)
On Error Goto ErrorHandler
coll.Item(key)
Contains = True
Exit Function
ErrorHandler:
Contains = False
End Function

Arraylist with Multi Dimentional for loops

Friends I have a serious issue at present and Honestly I have no idea as to why this isn't working as expected. I am more a custom to C# than VB can anyone help with the following Code Example
Please note that the String is passed from other classes and cannot be altered
The string arrL = "Someone#Something,200,First,50.00,60,Second,60.00,20,Third,70.00,120"
E.G where "Someone" is User, "Something" is an ID, "200" is Totaltime then frequency name "First" then frequency of "50.00" then its Time to run e.g 60 seconds. then next Item and so on.
Dim Sequence As New ArrayList
Sequence.AddRange(arrL.Split(","))
If Sequence.Count > 0 Then
RunFreq.ClientName = Sequence(0).ToString.Split("#")(0)
RunFreq.ClientProgramName = Sequence(0).ToString.Split("#")(1)
RunFreq.ClientProtocolTotalTime = Sequence(1).ToString
For i As Integer = 2 To Sequence.Count - 1
Dim g() = Sequence(i).ToString().Split(",")
Dim b As New ClassWave.ClassFrequency
b.Name = g(0) 'Here i get a Value
b.Frequency = CDbl(g(1)) '< HERE I get Index was outside the bounds of the array.
b.Time = CInt(g(2)) ' Same here Index was outside the bounds of the array.
Next
End If
I get Index Outside Bounds on g(1) and g(2) instead of 50.00 and 60. Any Ideas?
Preliminaries: You should turn on Option Strict, and consider using a List(of String) in place of the ArrayList.
Your second line, splits the string by "," into Sequence, so there is no need to split it again - you get the error because they cant be split further (and you didnt check the count). This works:
Dim arrl As String = "Someone#Something,200,First,50.00,60,Second,60.00,20,Third,70.00,120"
Dim Sequence As New ArrayList
Sequence.AddRange(arrl.Split(","c))
Dim a, b, c As String
If Sequence.Count > 0 Then
a = Sequence(0).ToString.Split("#"c)(0)
b = Sequence(0).ToString.Split("#"c)(1)
c = Sequence(1).ToString
Console.WriteLine("{0} - {1} - {2}", a, b, c)
For i As Integer = 2 To Sequence.Count - 1 Step 3
a = Sequence(i + 0).ToString
b = Sequence(i + 1).ToString
c = Sequence(i + 2).ToString
Console.WriteLine("{0} - {1} - {2}", a, b, c)
Next
End If
You could also use this for the split:
Sequence.AddRange(arrl.Split("#"c, ","c))
This would create 12 elements in the ArrayList, but since the first 3 parts go elsewhere, that doesnt have as much value.
Output:
Someone - Something - 200
First - 50.00 - 60
Second - 60.00 - 20
Third - 70.00 - 120
Note that .ToString() is required when fetching from the ArrayList because it is not typed - it only ever contains Object. A List(of String) would store the parts as string.

trying to to get specifically ordered data from .txt and then converting and storing it to double or string arrays at visual basic

I am trying, for several days, to take specifically ordered data from a .txt file and then convert and store it to double or string arrays
the data is stored in the file in this way:
1 0 1 0 >= 15
0 1 0 1 >= 28
1 1 0 0 <= 30
0 0 3 1 <= 22
-1 0 2 0 <= 0
(one line after the other with no blank lines between them)
and my code for this goes like:
Using stream As System.IO.FileStream = System.IO.File.OpenRead("C:\Users\user\Desktop\test_new.txt")
Using reader As New System.IO.StreamReader(stream)
Dim lineCount = File.ReadAllLines("C:\Users\user\Desktop\test_new.txt").Length
Dim line As String = reader.ReadLine()
Dim aryTextFile() As String
Dim operator1() As String
Dim variables(,) As Double
Dim results() As Double
Dim counter3 As Integer
counter3 = 0
NRows = lineCount
While (line IsNot Nothing)
Dim columns = line.Split(" ")
aryTextFile = line.Split(" ")
line = reader.ReadLine()
NVars = columns.Length - 3
For j = 0 To UBound(aryTextFile) - 3
variables(counter3, j) = CDbl(aryTextFile(j))
Next j
For j = NVars To UBound(aryTextFile) - 2
operator1(counter3) = CStr(aryTextFile(j))
Next j
For j = UBound(aryTextFile) - 2 To UBound(aryTextFile) - 1
results(counter3) = CDbl(aryTextFile(j))
Next j
counter3 = counter3 + 1
End While
End Using
End Using
I'm getting warnings which result in errors ofc.
Variable 'variables' is used before it has been assigned a value. A null reference exception could result at
runtime. C:\Users\user\Documents\Visual Studio
2008\Projects\WindowsApplication1\WindowsApplication1\Form1.vb 230 25 WindowsApplication1
Variable 'operator1' is used before it has been assigned a value. A null reference exception could result at
runtime. C:\Users\user\Documents\Visual Studio
2008\Projects\WindowsApplication1\WindowsApplication1\Form1.vb 236 25 WindowsApplication1
Variable 'results' is used before it has been assigned a value. A null reference exception could result at
runtime. C:\Users\user\Documents\Visual Studio
2008\Projects\WindowsApplication1\WindowsApplication1\Form1.vb 243 25 WindowsApplication1
So what am I doing wrong and how can I fix it
note: data is saved from dynamic matrixes, so it can be a lot bigger than the displayed example (several lines with several columns), and that is the reason I'm trying to program it, in order to save me some lab time of copying and pasting it manually...
thanks in advance,
Viktor
p.s. if another member or admin can indicate an older post about my question, that would also be very helpful, but I am reading posts for the last 4 days in similar questions and I couldn't find something working for me
p.s.2 since is my first post, I have also tried to attach the project and I couldn't find a way :)
You need to define the dimensions of your arrays before trying to use them.
If you don't know what the size will be use a list instead.
'No defined size - Warning
Dim array1() As String
'Error when trying to access
array1(4) = "Testing"
'Defined size
Dim array2(10) As String
array2(5) = "Testing"

Parsing a text file Row after another row

I have this text file that I need to parse and put the parsed data in the database
Name Qty1 Qty2 Name Qty1 Qty2
ABC 1 2
BCD 2 3
EFG 7 9 PQR 56 97
DEF 3 18 RET 988 11
I have a table where I need to put the above data
The table structure is like this
Name, Qty1, Qty2,Col
so If I parse from left side then I can put the ABC,1,2, L in the table and if Parse from right side then I can put PQR, 56, 97, R in the same table.
My problem is how can I differentiate between left column and right columns. As soon as I start reading, I can read ABC,1,2 and then I don't know if there is a value in Right column and if I keep reading through my VB.net code then, I will start reading BCD,2,3 and at that point I don't know if BCD belongs to Right Column or left column so I am not sure whether I put L or R in the database. I am trying to parse this file in .net using substring and Indexof. This file is generated from the pdf document. below is the code to read the pdf document:
Public ReadOnly Property getParsedFile() As String
Get
Dim document As New PDFDocument(filePath)
Dim parsedFile As StringBuilder = New StringBuilder()
For i As Integer = 0 To document.Pages.Count - 1
parsedFile.Append(document.Pages(i).GetText())
Next
Return parsedFile.ToString()
End Get
End Property
any help will be greatly appreciated.
below is the answer
Public Function ExtractTextFromPdf(path As String) As String
Dim its As iTextSharp.text.pdf.parser.ITextExtractionStrategy = New iTextSharp.text.pdf.parser.LocationTextExtractionStrategy()
Using reader As New PdfReader(path)
Dim str As New StringBuilder()
For i As Integer = 1 To reader.NumberOfPages
Dim thePage As String = PdfTextExtractor.GetTextFromPage(reader, i, its)
Dim theLines As String() = thePage.Split(ControlChars.Lf)
For Each theLine As String In theLines
str.AppendLine(theLine)
Next
Next
saveTextFileOnComputer(str.ToString())
Return str.ToString()
End Using
End Function