only read certain columns from a csv - vb.net

so I have a csv file that has extra commas in it. I know I won't ever need anything after a specific column. So basically any information after column 12 I won't need. I don't have a say on how the csv looks when it gets to me, so I can't change it there. I was wondering if there is a way to just read the first 12 columns and ignore the rest of the csv file.
this is what the code looks like now.
thank you for any help
Private Sub GetData(ByVal Path As String, ByRef DG As DataGridView, Optional ByVal NoHeader As Boolean = False)
Dim Fields(100) As String
Dim Start As Integer = 1
If NoHeader Then Start = 0
If Not File.Exists(Path) Then
Return
End If
Dim Lines() As String = File.ReadAllLines(Path)
Lines(0) = Lines(0).Replace(Chr(34), "")
Fields = Lines(0).Split(",")
If NoHeader Then
For I = 1 To Fields.Count - 1
Fields(I) = Str(I)
Next
End If
dt = New DataTable()
For Each Header As String In Fields
dt.Columns.Add(New DataColumn(Header.Trim()))
Dim desiredSize As Integer = 11
While dt.Columns.Count > desiredSize
dt.Columns.RemoveAt(desiredSize)
End While
Next
For I = Start To Lines.Count - 1
Lines(I) = Lines(I).Replace(Chr(34), "")
Fields = Lines(I).Split(",")
Dim dr As DataRow = dt.Rows.Add()
For j = 0 To Fields.Count - 1
dr(j) = Fields(j).Trim()
Next
Next
DG.DataSource = dt
End Sub

Really all you need to do is, in the for loop where you iterate through Fields at the bottom, replace For j = 0 to Fields.Count - 1 with For j = 0 to 11.

Related

Split output result by n and loop

I'm solving an issue where I need to create one PDF form.
That PDF form has 8 sections where I need to put info about and looks like shown on picture (only 4 shown).
The point is that my query will return 0 - n different results. So I need to split by 8 and post on different pages.
I tried like shown below but that seems not to work since I always load a new document. Does anyone have some advice how to make it?
Try
Dim sCommand As OleDb.OleDbCommand
sCommand = New OleDb.OleDbCommand("SELECT a,b,c Query to fetch n results ", _dbCon)
sCommand.CommandTimeout = 0
Dim _dbREADER As OleDb.OleDbDataReader
_dbREADER = sCommand.ExecuteReader
Dim dt As DataTable = New DataTable()
dt.Load(_dbREADER)
Dim totalPages As Integer = dt.Rows.Count / 8
Dim currentPage As Integer = 1
Dim rowCounter As Long = 0
If dt.Rows.Count > 0 Then
For Each row In dt.Rows
rowCounter += 1
If rowCounter = 8 Then
currentPage += 1
rowCounter = 0
End If
_pdfDocumentOutput = System.IO.Path.GetTempPath() & "MailingForm_" & currentPage & ".pdf"
SaveFromResources(_pdfDocument, My.Resources.template)
Using reader As New PdfReader(_pdfDocument)
Using stamper As New PdfStamper(reader, New IO.FileStream(_pdfDocumentOutput, IO.FileMode.Create))
Dim fontName As String = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "SCRIPTIN.ttf")
Dim bf As BaseFont = BaseFont.CreateFont(fontName, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED)
Dim pdfForm As AcroFields = stamper.AcroFields
pdfForm.AddSubstitutionFont(bf)
pdfForm.SetField(rowCounter - 1 & "0", row("Customer")) 'Checks the top radiobutton of the VrPr4 field
pdfForm.SetField(rowCounter - 1 & "1", row("Address"))
pdfForm.SetField(rowCounter - 1 & "2", row("Location"))
stamper.FormFlattening = True
End Using
End Using
Next
End If
Status.Text = "Store info loaded ! "
Catch ex As Exception
Status.Text = ex.Message
End Try
I found the solution by splitting tables
Private Shared Function SplitTable(ByVal originalTable As DataTable, ByVal batchSize As Integer) As List(Of DataTable)
Dim tables As List(Of DataTable) = New List(Of DataTable)()
Dim i As Integer = 0
Dim j As Integer = 1
Dim newDt As DataTable = originalTable.Clone()
newDt.TableName = "Table_" & j
newDt.Clear()
For Each row As DataRow In originalTable.Rows
Dim newRow As DataRow = newDt.NewRow()
newRow.ItemArray = row.ItemArray
newDt.Rows.Add(newRow)
i += 1
If i = batchSize Then
tables.Add(newDt)
j += 1
newDt = originalTable.Clone()
newDt.TableName = "Table_" & j
newDt.Clear()
i = 0
End If
If row.Equals(originalTable.Rows(originalTable.Rows.Count - 1)) Then
tables.Add(newDt)
j += 1
newDt = originalTable.Clone()
newDt.TableName = "Table_" & j
newDt.Clear()
i = 0
End If
Next
Return tables
End Function
And after that loop through each one of table . And put all results to one file
Dim tables = SplitTable(dt, 8)

Get Distinct rows from datagridview1 and pass into datagridview2

I got this code where it select distinct values from datagridview1 and pass the result to datagridview2:
Further Details:
Data from datagridview1
Code Amount
Pro1 100.00
Pro2 300.00
Pro1 100.00
Pro1 100.00
Pro2 300.00
The result should be placed in datagridview2 like below;
Code Amount Count
Pro1 100.00 3
Pro2 300.00 2
CODE:
Dim dic As New Dictionary(Of String, Integer)()
Dim cellValue As String = Nothing
For i As Integer = 0 To dgvSubjectsEnrolled.Rows.Count - 1
If Not dgvSubjectsEnrolled.Rows(i).IsNewRow Then
If dgvSubjectsEnrolled(0, i).Value > 0 Then
cellValue = dgvSubjectsEnrolled(0, i).Value.ToString()
If Not dic.ContainsKey(cellValue) Then
dic.Add(cellValue, 1)
Else
dic(cellValue) += 1
End If
Else
End If
End If
Next
Dim sb1 As New StringBuilder()
Dim sb2 As New StringBuilder()
For Each keyvalue As KeyValuePair(Of String, Integer) In dic
sb1.AppendLine(String.Format("{0}", keyvalue.Key, keyvalue.Value))
sb2.AppendLine(String.Format("{1}", keyvalue.Key, keyvalue.Value))
Next
Me.dgvsub.Rows.Add(sb1.ToString, sb2.ToString())
The code executed successfully but the result is:
Code Count
Pro1Pro2 32
The data was concatenated with the same row. The next value should be at the next row.
Please edit this code.
One way to do it, with a dictionary instead of a list:
Private Sub copyDGVs()
Dim values As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)()
Dim i As Integer = 1
Dim tmp As String
Dim arr As String()
'Make a list of distinct values from DataGridView1, track the count.
For Each row As DataGridViewRow In dgv1.Rows
tmp = ""
For j As Integer = 0 To row.Cells.Count - 1
tmp &= row.Cells(j).Value & "-"
Next
If (Not values.ContainsKey(tmp)) Then
values.Add(tmp, 1)
Else
values(tmp) += 1
End If
Next
'Copy that list into DataGridView2
For Each guy As KeyValuePair(Of String, Integer) In values
arr = guy.Key.Split("-")
For q As Integer = 0 To arr.Length - 1
dgv2.Rows(i).Cells(q + 1).Value = arr(q)
If (q = arr.Length - 1) Then
dgv2.Rows(i).Cells(q + 2).Value = guy.Value
End If
Next
i += 1
Next
End Sub
It's not the pretties thing I've ever written, but it should do what you need, unless I goofed the indexes.
PER COMMENTS/EDIT:
I think the problem now is you're appending to your stringbuilders, never resetting them, and not writing inside the loop. Try something like this and see if it works out better:
Dim sb1 As StringBuilder()
Dim sb2 As StringBuilder()
For Each keyvalue As KeyValuePair(Of String, Integer) In dic
sb1 = New StringBuilder()
sb2 = New StringBuilder()
sb1.AppendLine(String.Format("{0}", keyvalue.Key, keyvalue.Value))
sb2.AppendLine(String.Format("{1}", keyvalue.Key, keyvalue.Value))
Me.dgvsub.Rows.Add(sb1.ToString, sb2.ToString())
Next

How can I get String values rather than integer

How To get StartString And EndString
Dim startNumber As Integer
Dim endNumber As Integer
Dim i As Integer
startNumber = 1
endNumber = 4
For i = startNumber To endNumber
MsgBox(i)
Next i
Output: 1,2,3,4
I want mo make this like sample: startString AAA endString AAD
and the output is AAA, AAB, AAC, AAD
This is a simple function that should be easy to understand and use. Every time you call it, it just increments the string by one value. Just be careful to check the values in the text boxes or you can have an endless loop on your hands.
Function AddOneChar(Str As String) As String
AddOneChar = ""
Str = StrReverse(Str)
Dim CharSet As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Dim Done As Boolean = False
For Each Ltr In Str
If Not Done Then
If InStr(CharSet, Ltr) = CharSet.Length Then
Ltr = CharSet(0)
Else
Ltr = CharSet(InStr(CharSet, Ltr))
Done = True
End If
End If
AddOneChar = Ltr & AddOneChar
Next
If Not Done Then
AddOneChar = CharSet(0) & AddOneChar
End If
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim S = TextBox1.Text
Do Until S = TextBox2.Text
S = AddOneChar(S)
MsgBox(S)
Loop
End Sub
This works as a way to all the codes given an arbitrary alphabet:
Public Function Generate(starting As String, ending As String, alphabet As String) As IEnumerable(Of String)
Dim increment As Func(Of String, String) = _
Function(x)
Dim f As Func(Of IEnumerable(Of Char), IEnumerable(Of Char)) = Nothing
f = _
Function(cs)
If cs.Any() Then
Dim first = cs.First()
Dim rest = cs.Skip(1)
If first = alphabet.Last() Then
rest = f(rest)
first = alphabet(0)
Else
first = alphabet(alphabet.IndexOf(first) + 1)
End If
Return Enumerable.Repeat(first, 1).Concat(rest)
Else
Return Enumerable.Empty(Of Char)()
End If
End Function
Return New String(f(x.ToCharArray().Reverse()).Reverse().ToArray())
End Function
Dim results = New List(Of String)
Dim text = starting
While True
results.Add(text)
If text = ending Then
Exit While
End If
text = increment(text)
End While
Return results
End Function
I used it like this to produce the required result:
Dim alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Dim results = Generate("S30AB", "S30B1", alphabet)
This gave me 63 values:
S30AB
S30AC
...
S30BY
S30BZ
S30B0
S30B1
It should now be very easy to modify the alphabet as needed and to use the results.
One option would be to put those String values into an array and then use i as an index into that array to get one element each iteration. If you do that though, keep in mind that array indexes start at 0.
You can also use a For Each loop to access each element of the array without the need for an index.
if the default first two string value of your output is AA.
You can have a case or if-else conditioning statement :
and then set 1 == A 2 == B...
the just add or concatenate your default two string and result string of your case.
I have tried to understand that you are looking for a series using range between 2 textboxes. Here is the code which will take the series and will give the output as required.
Dim startingStr As String = Mid(TextBox1.Text, TextBox1.Text.Length, 1)
Dim endStr As String = Mid(TextBox2.Text, TextBox2.Text.Length, 1)
Dim outputstr As String = String.Empty
Dim startNumber As Integer
Dim endNumber As Integer
startNumber = Asc(startingStr)
endNumber = Asc(endStr)
Dim TempStr As String = Mid(TextBox1.Text, 1, TextBox1.Text.Length - 1)
Dim i As Integer
For i = startNumber To endNumber
outputstr = outputstr + ", " + TempStr + Chr(i)
Next i
MsgBox(outputstr)
The First two lines will take out the Last Character of the String in the text box.
So in your case it will get A and D respectively
Then outputstr to create the series which we will use in the loop
StartNumber and EndNumber will be give the Ascii values for the character we fetched.
TempStr to Store the string which is left off of the series string like in our case AAA - AAD Tempstr will have AA
then the simple loop to get all the items fixed and show
in your case to achive goal you may do something like this
Dim S() As String = {"AAA", "AAB", "AAC", "AAD"}
For Each el In S
MsgBox(el.ToString)
Next
FIX FOR PREVIOUS ISSUE
Dim s1 As String = "AAA"
Dim s2 As String = "AAZ"
Dim Last As String = s1.Last
Dim LastS2 As String = s2.Last
Dim StartBase As String = s1.Substring(0, 2)
Dim result As String = String.Empty
For I As Integer = Asc(s1.Last) To Asc(s2.Last)
Dim zz As String = StartBase & Chr(I)
result += zz & vbCrLf
zz = Nothing
MsgBox(result)
Next
**UPDATE CODE VERSION**
Dim BARCODEBASE As String = "SBA0021"
Dim BarCode1 As String = "SBA0021AA1"
Dim BarCode2 As String = "SBA0021CD9"
'return AA1
Dim FirstBarCodeSuffix As String = Replace(BarCode1, BARCODEBASE, "")
'return CD9
Dim SecondBarCodeSuffix As String = Replace(BarCode2, BARCODEBASE, "")
Dim InternalSecondBarCodeSuffix = SecondBarCodeSuffix.Substring(1, 1)
Dim IsTaskCompleted As Boolean = False
For First As Integer = Asc(FirstBarCodeSuffix.First) To Asc(SecondBarCodeSuffix)
If IsTaskCompleted = True Then Exit For
For Second As Integer = Asc(FirstBarCodeSuffix.First) To Asc(InternalSecondBarCodeSuffix)
For Third As Integer = 1 To 9
Dim tmp = Chr(First) & Chr(Second) & Third
Console.WriteLine(BARCODEBASE & tmp)
If tmp = SecondBarCodeSuffix Then
IsTaskCompleted = True
End If
Next
Next
Next
Console.WriteLine("Completed")
Console.Read()
Take a look into this check it and let me know if it can help

CSV duplicate field names

I have a CSV file that we extract from a core system. A recent development change to the system has made there CSV file data contain duplicate column/field names.
So the fields appear more than once.
Creator First Name
Creator Last Name
Creator Salary Number
Revenue
Contact
Sales Team
Channel
This data we then upload via a SSIS/DTS package. The package won't actually run or work with Duplicate field names. So we need to remove or rename the duplicate fields.
So I was thinking of creating a C# or VB script that renames the duplicate field names with a post fix of 1 when it counts a field name of more than 1.
Is there a easy way of doing this through the Microsoft.VisualBasic.FileIO.TextFieldParser or something similiar? Not sure if someone has encountered a similiar issue.
I have added a Script Task that runs a Visual Basic 2008 code (below) WIP;
Public Sub Main()
'Check if file exist
Dim filename As String = Dts.Variables.Item("fileName").Value
If Not System.IO.File.Exists(filename) Then
Exit Sub
End If
Dim csvFileArray(1, 1) As String
Dim newCsvFileArray(1, 1) As String
Dim streamReader As StreamReader = File.OpenText(filename)
Dim strlines() As String
strlines = streamReader.ReadToEnd().Split(Environment.NewLine)
' Redimension the array.
Dim num_rows As Long
Dim num_cols As Long
'Dim counter As Integer = 0 '' >> Idea is to add this to the array as a 3rd dimension so we can count each field
Dim fieldCounter As Integer = 0
Dim strline() As String
num_rows = UBound(strlines)
strline = strlines(0).Split(",")
num_cols = UBound(strline)
ReDim csvFileArray(num_rows, num_cols)
ReDim newCsvFileArray(num_rows, num_cols)
' Copy the data into the array.
Dim fields As Integer
Dim rows As Integer
For rows = 0 To num_rows - 1
strline = strlines(rows).Split(",")
For fields = 0 To num_cols - 1
csvFileArray(rows, fields) = strline(fields)
Next
Next
Dim currentField As String = ""
Dim comparrisionField As String = ""
Dim newRows As Integer = 0
Dim newFields As Integer = 0
rows = 0
' Compare the current array to if they match anything in the new array
For rows = 0 To num_rows - 1
For fields = 0 To num_cols - 1
currentField = csvFileArray(rows, fields)
If rows = 0 Then
' If we are dealing with Fields i.e Row=0
For newFields = 0 To num_cols - 1
comparrisionField = newCsvFileArray(newRows, newFields)
If String.IsNullOrEmpty(currentField) Then
Else
If currentField.Equals(comparrisionField) Then
If currentField <> "" Then
fieldCounter = fieldCounter + 1
' if we have already added this column, then append a number
If fieldCounter >= 1 Then
currentField = currentField + " " + CStr(fieldCounter)
End If
End If
End If
End If
Next
Else
' This means we are dealing with the Rows i/e not row = 0
currentField = currentField
End If
newRows = 0
newFields = 0
fieldCounter = 0
' save currentField in the same layout as the initial file
newCsvFileArray(rows, fields) = currentField
Next
Next
' Amend the duplicate field names
Dim sw As New System.IO.StreamWriter(Left(filename, Len(filename) - Len(Right(filename, 4))) + "_amended.csv")
rows = 0
Dim currentLine As String = ""
For rows = 0 To num_rows - 1
For fields = 0 To num_cols - 1
' Save the data back to the filesystem
If fields = 0 Then
currentLine = newCsvFileArray(rows, fields)
Else
currentLine = currentLine + "," + newCsvFileArray(rows, fields)
End If
Next
If currentLine <> "" Then
sw.WriteLine(currentLine.Replace(vbCr, "").Replace(vbLf, ""))
End If
Next
sw.Close()
Dts.TaskResult = ScriptResults.Success
End Sub

How I can randomize the content of a text file?

I need to randomize ALL the lines inside a text file and then save the unsorted lines by replacing the same text file.
How I can do all that?
Dim filepath as String = "text_path"
Dim arr() As String = File.ReadAlllines(filepath)
Dim a As Random
Dim b(str.Length) As Integer
Dim result=1, c As Integer
File.Delete(filepath)
Dim f As StreamWriter = File.AppendText(filepath)
For i = 0 To str.Length
while(result)
result = 0
c = a.Next(0, str.Length)
For j = 0 To b.Length
If b(j) = c Then result = 1
Next
end while
f.WriteLine(arr(c))
Next
f.Close()
Another take on it:
Imports System.IO
Module Module1
Sub CreateFile(destFile As String)
Using sw = New StreamWriter(destFile)
For i = 1 To 200
sw.WriteLine("Line " & i.ToString)
Next
End Using
End Sub
Function RandomList(nNumbers As Integer) As List(Of Integer)
' generate a List of numbers from 0..nNumbers-1 in a random order.
Dim ns As New List(Of Integer)
Dim rnd As New Random
For i = 0 To nNumbers - 1
ns.Insert(rnd.Next(0, i + 1), i)
Next
Return ns
End Function
Sub RandomiseFile(srcFile As String)
Dim lines = File.ReadAllLines(srcFile)
Dim nLines = lines.Count
Dim randomNumbers = RandomList(nLines)
' use a temporary file in case something goes wrong so that
' the original file is still there.
Dim tmpFile = Path.GetTempFileName()
' output the lines in a random order.
Using sw = New StreamWriter(tmpFile)
For i = 0 To nLines - 1
sw.WriteLine(lines(randomNumbers(i)))
Next
End Using
File.Delete(srcFile)
File.Move(tmpFile, srcFile)
End Sub
Sub Main()
Dim fileToUse As String = "C:\temp\makerandom.txt"
CreateFile(fileToUse)
RandomiseFile(fileToUse)
End Sub
End Module
Here is my take on it:
Dim linesList As New List(Of String)(IO.File.ReadAllLines("filepath"))
Dim newLinesList As New List(Of String)
Randomize()
While linesList.Count > 0
Dim randomIndex As Integer = Math.Floor(Rnd() * linesList.Count)
newLinesList.Add(linesList(randomIndex))
linesList.RemoveAt(randomIndex)
End While
IO.File.WriteAllLines("filepath", newLinesList.ToArray)