How to generate a reduced Excel when filtering by id in vb.net - excel

In vb.net I have lists that contains variables of an Excel file.
Example:
Lists first Excel:
ID
WEIGHT
NAME
CAU65469
1234
jkaufman
DEX74893
1234
jdee
Second one:
ID
COLOR
VOLUME
CAU65469
YELLOW
900
DEX743413
BLUE
1500
I want to generate a third file that looks like this:
ID
WEIGHT
NAME
COLOR
VOLUME
CAU65469
1234
jkaufman
YELLOW
900
My solution is to shorten the second Excel by filtering by id
so it would only show me the data if the ID is found in both files and then comparing by coordinates.
On vb.net, how can I save the position of an Excel (index) by an ID in order to shorten the Excel?
My code in vb.net, I have the values in lists like this:
Public ID As New List(Of String)
Public ID2 As New List(Of String)
Public COLOR As New List(Of String)
Public NAME As New List(Of String)
Public WEIGHT New List(Of String)
Public VOLUME As New List(Of String)
To just fill information in a new Excel I use this code.
I use a function like this to extract the information from the Excel files.
Function extraer_valores_planilla(ByRef ruta As String) As Boolean
ExcelPackage.LicenseContext = LicenseContext.NonCommercial
Try
Dim stream = System.IO.File.OpenRead(ruta)
Dim package = New OfficeOpenXml.ExcelPackage(stream)
'// Libro
Dim Workbook = package.Workbook
'// Hojas
Dim hojas = Workbook.Worksheets
' While (Workbook.Worksheets.Count >= aux)
Dim hojaUsuarios = Workbook.Worksheets(Workbook.Worksheets.Item(0).ToString)
Dim indice As Integer = 2
While (indice < 5000)
If (IsNothing(hojaUsuarios.Cells("A" & indice).Value) = False) Then
ID.Add(hojaUsuarios.Cells("A" & indice).Value)
End If
indice += 1
End While
indice += 1
Catch EX As Exception
MsgBox(EX.ToString)
Return False
End Try
Return True
and then I fill the third Excel like this:
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
ExcelPackage.LicenseContext = LicenseContext.NonCommercial
Dim path As String = seleccionardirectorio("Excel|.xlsx")
If (String.IsNullOrWhiteSpace(path) = False) Then
Dim excel = New ExcelPackage(New FileInfo(path))
excel.Workbook.Worksheets.Add("Hoja1")
Dim aux As Integer = 1
Dim Workbook = excel.Workbook
Dim hojas = Workbook.Worksheets
Dim dict As New Dictionary(Of String, String)
Dim hoja1 = Workbook.Worksheets("Hoja1")
'DAMOS NOMBRE A LAS COLUMNAS
INICIALIZAR_PLANILLA(hoja1)
While (aux <= ID.Count)
hoja1.Cells("C" & aux + 1).Value = COLOR.Item(aux - 1)
aux += 1
End While

One of the things we (should) do when we write programs in an object oriented language, is try and move away from thinking like "I have to store 5 bits of info about an object - id, color, name, weight, volume. I'll make 5 arrays, one for each thing, and relate them all positionally so the "ID1 BLUE JOHN 50KG 200m3" data is at array index 9 in each of the five arrays"
Instead we should be thinking "I'll make a class with 5 properties, and I'll create instances of it and fill them" - this way the items of data don't remain related purely because they "just happen to all be at position 9 in some arrays" but instead are all together and representing an object. Nearly line of code you write in VB uses something that obeys this notion - you should too
Public Class Whatever
Public Property ID As String
Public Property Color As String
Public Property Name As String
Public Property Weight As String
Public Property Volume As String
End Class
Next we use some collection that supports looking things up, like a Dictionary:
Function extraer_valores_planilla(ByRef ruta As String, isFirstExcel as Moolean) As Boolean
... 'put code that opens file here - i removed for clarity
Dim das New Dictionary(Of String, Whatever)
Dim indice As Integer = 2
While (indice < 5000)
If (IsNothing(hojaUsuarios.Cells("A" & indice).Value)) Then
indice += 1
Continue While
End If
Dim id = hojaUsuarios.Cells("A" & indice).Value
If Not d.ContainsKey(id) Then d(id) = New Whatever
d(id).ID = id
If isFirstExcel Then
d(id).Weight = hojaUsuarios.Cells("B" & indice).Value
d(id).Name = hojaUsuarios.Cells("C" & indice).Value
Else
d(id).Color = hojaUsuarios.Cells("B" & indice).Value
d(id).Volume = hojaUsuarios.Cells("C" & indice).Value
indice += 1
End While
The posted code does not show how ID2 Color, Volume, Weight, Name lists come to be populated with anything. I've assumed that WEIGHT/COLOR is in column B, and NAME/VOLUME is in column C
At the end of this operation you have a dictionary that has ID strings mapping to Whatever objects... And those objects Will have two or four properties filled in depending on if their ID was present in one file or the other

Related

validate strings in an excel in vb.net

I have a question, in vb.net, how can i validate that 2 values are the same in an excel in vb.net
for example i have defined 3 list
Public NSPS As New List(Of String)
Public CONTAINER As New List(Of String)
Public CONTAINER2 As New List(Of String)
I have 2 excel files where CONTAINER and CONTAINER2 are id's
So i need to create a third excel file that filters only the id's that repeat themselves in the 2 excel
meaning if i have an id: CARU9891569 in the 2 files, only then it transfers to the generated excel
and the 2 excel's have some extra information, for example: excel 1 has the variables: DELIVERY, CONTAINER, VOLUME.
the second excel has the variables: NSPS, NPOS, PACKAGES, CONTAINER2
SO the generated excel needs to have all of the variables: DELIVERY, CONTAINER, VOLUME, NSPS, NPOS, PACKAGES. using CONTAINER as the filter
to just fill information in a new excel i use this code
i use a function like this to extract the information from the excel files
Function extraer_valores_planilla(ByRef ruta As String) As Boolean
ExcelPackage.LicenseContext = LicenseContext.NonCommercial
Try
Dim stream = System.IO.File.OpenRead(ruta)
Dim package = New OfficeOpenXml.ExcelPackage(stream)
'// Libro
Dim Workbook = package.Workbook
'// Hojas
Dim hojas = Workbook.Worksheets
' While (Workbook.Worksheets.Count >= aux)
Dim hojaUsuarios = Workbook.Worksheets(Workbook.Worksheets.Item(0).ToString)
Dim indice As Integer = 2
While (indice < 5000)
'Numero entrega'
If (IsNothing(hojaUsuarios.Cells("A" & indice).Value) = False) Then
NSPS.Add(hojaUsuarios.Cells("A" & indice).Value)
End If
indice += 1
End While
indice += 1
Catch EX As Exception
MsgBox(EX.ToString)
Return False
End Try
Return True
and then i fill the third excel like this
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
ExcelPackage.LicenseContext = LicenseContext.NonCommercial
Dim path As String = seleccionardirectorio("Excel|.xlsx")
If (String.IsNullOrWhiteSpace(path) = False) Then
Dim excel = New ExcelPackage(New FileInfo(path))
excel.Workbook.Worksheets.Add("Hoja1")
Dim aux As Integer = 1
Dim Workbook = excel.Workbook
Dim hojas = Workbook.Worksheets
Dim dict As New Dictionary(Of String, String)
Dim hoja1 = Workbook.Worksheets("Hoja1")
'DAMOS NOMBRE A LAS COLUMNAS
INICIALIZAR_PLANILLA(hoja1)
While (aux <= CONTAINER.Count)
hoja1.Cells("C" & aux + 1).Value = ENTREGA.Item(aux - 1)
aux += 1
End While
this is the same for all variables i just resume for you guys and this works just fine.
should i use 2 cicles to filter the excel, maybe a for each, sorry i am new to programing and i am stuck in this part
any ideas would be helpfull
Thanks in advance!
yes, use 2 for each loops.
for each item in list
for each otheritem in list2
if item = otheritem then
' These items match
end if
next
next
Replace the dummy variables with yours

Collection of Objects Passing a new UDT to each Object while Looping thru an Array

My aMRecon array is 2500 rows x 65 columns. I need to evaluate up to 10+ columns within each single row and thus I believe I need to create an object representing each row. I have created a UDT and in a basic procedure below I am trying to create an object for each row with each object having an .EntityID property (which is the cell value in each row within Column B or Column 2).
Public Type BreakInfo
EntityID As String
IssueName As String
ValDate As Date
LedgerAMT As Long
NetAMTL As Long
PriceDiff As Long
End Type
Sub Fill_Objects()
Dim aMrow As Integer, aMcol As Integer
Dim BI As BreakInfo
For aMcol = LBound(aMRecon, 2) To UBound(aMRecon, 2)
For aMrow = LBound(aMRecon, 1) To UBound(aMRecon, 1)
If aMcol = 2 Then
Debug.Print aMRecon(aMrow, aMcol)
Set ObjLSL = New Collection
BI.EntityID = aMRecon(aMrow, aMcol)
End If
Next aMrow
Next aMcol
End If
End Sub
Do I need to somehow create a collection of objects? Could someone please show me an example to help. As of right now I think I only have one object and the .EntityID property keeps getting overwritten. Thank you
In Fact each row at will only have 1 property, basically each property is a Column Header. Am I going about this the most efficient way? Eventually I will need to evaluate each property within an object and categorize it.
Inserted a ClassModule entitle BreakInfo
'Public EntityID As String
Public EntityID As Variant
Public IssueName As String
Public ValDate As Date
Public LedgerAMT As Long
Public NetAMTL As Long
Public PriceDiff As Long
That's all that's in the class.
You need to first create (insert) a Class Module, name it BreakInfo, and give it Public members like this:
Option Explicit
Public EntityID As String
Public IssueName As String
Public ValDate As Date
Public LedgerAMT As Long
Public NetAMTL As Long
Public PriceDiff As Long
Then you can use something like this:
Sub Fill_Objects()
Dim aMrow As Integer, aMcol As Integer
Dim BI As BreakInfo
Dim ObjLSL As Collection
Dim key As Long
'Create the Collection instance.
Set ObjLSL = New Collection
For aMcol = LBound(aMRecon, 2) To UBound(aMRecon, 2)
For aMrow = LBound(aMRecon, 1) To UBound(aMRecon, 1)
If aMcol = 2 Then
'Instantiate a BreakInfo.
Set BI = New BreakInfo
BI.EntityID = aMRecon(aMrow, aMcol)
'...
key = key + 1
ObjLSL.Add BI, CStr(key)
End If
Next aMrow
Next aMcol
End Sub
Notice that the collection is instantiated once, before the loops. A collection can't ingest variables of user-defined types, but it will gladly gobble up object instances.
Edit
The question has changed. If you worry about efficiency, you could hardcode aMcol = 2 and do without the outer For and the If aMcol = 2. Other than that, I don't understand what you're trying to do with your values.

CSV to XLSX VB.NET

enter image description hereenter image description hereI am just learning VB.NET and unfortunately I have been tasked with something I do not have a clue how to do.
I need to create a quick windows based application to export csv files into an XLSX file.
Yes, I know that other posts may have a similar topic however this one I believe is unique.
The CSV file will have 5 headers, `Line, Component, Picked, Placed and Missed. We have part numbers in column 2 that would be placed under Component. I am understanding from the powers that be, this file sums the total part numbers i.e. 0-5490045 and the line JUKI 3 and totals Picked, Placed and Missed parts. I have provided a sample rows below. First row is the csv formatted, the second is the output. I am not sure which loop would be best a FOR loop, WHILE loop etc. I am assuming I will need a loop of some sort to get through all the data in the csv file.
The only code I have opens the dialog box and allows for file selection and attempts to read into a datatable. I am attempting to get this working and then restructure some code.
Imports Spire.Xls
Imports System.Windows.Forms
Imports System.Data
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dialog As OpenFileDialog = New OpenFileDialog
dialog.Filter="CSV document(*.csv)|*.csv"
Dim result As DialogResult = dialog.ShowDialog
If(result=DialogResult.OK) Then
Dim csvFile As String = dialog.FileName
Dim workbook As Workbook = New Workbook
workbook.LoadFromFile(csvFile,",")
Dim worksheet As Worksheet = workbook.Worksheets(0)
Dim dt As DataTable=worksheet.ExportDataTable
Me.dataGridView1.DataSource=dt
End If
End Sub
End Class
JUKI 3 0-5490045 96 96 3
Line Component Picked Placed Missed
JUKI 3 0-5490045 99 96 3
I hate to make a suggestion and not show how it would work. Below is an example using a custom object called Machine to hold the data. This class is a bare minimum for an object and is only used as an example to get you started. It has some fields that will come in handy when looping thru the list to do your computations. It is also here you could add some custom functions/subs to help in some task that involves “Machine” objects. Also here you could add some compare functions which will enable you to sort among other things. After you put all this together you should end up with a list of valid Machine objects.
It is this list you could use to help you move on to the computing/removing duplicates part of your task. In the process of computing the data you could create a final list of Machine objects that you could use to export to excel with headers or display it to a DataGridView. Hope this helps.
Machine Class
Public Class Machine
Private name As String
Private partNumber As String
Private inventoryIn As Integer
Private inventoryOut As Integer
Private inventoryMissing As Integer
Public Sub New(inName As String, inPartNum As String, inInvIn As Integer, inInvOut As Integer, InInvMis As Integer)
name = inName
partNumber = inPartNum
inventoryIn = inInvIn
inventoryOut = inInvOut
inventoryMissing = InInvMis
End Sub
Property GetName As String
Get
Return name
End Get
Set(value As String)
name = value
End Set
End Property
Public Overrides Function ToString() As String
Return "Name: " + name + " #: " + partNumber + vbTab + " In:" + inventoryIn.ToString() + " Out:" + inventoryOut.ToString() + " Miss:" + inventoryMissing.ToString()
End Function
End Class
Now to your issue of reading the file
I did not use anything involving excel. Since you have a simple csv file we will use it. Also we will use the Machine class above. Using your open file dialog we get the name of the file to read. A variable partsList is created to hold the Machine objects created when reading the file. Then a for each loop goes through the list and displays the results in a text box on the form.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dialog As OpenFileDialog = New OpenFileDialog
dialog.Filter = "CSV document(*.csv)|*.csv"
Dim result As DialogResult = dialog.ShowDialog
If (result = DialogResult.OK) Then
Dim csvFile As String = dialog.FileName
Dim partsList As List(Of Machine) = ReadText(csvFile)
For Each curMac As Machine In partsList
TextBox1.AppendText(curMac.ToString() + Environment.NewLine)
Next
End If
End Sub
Function to read the csv file
Private Function ReadText(filePath As String) As List(Of Machine)
Dim fileReader As System.IO.StreamReader
Dim data As List(Of Machine) = New List(Of Machine)
fileReader = My.Computer.FileSystem.OpenTextFileReader(filePath)
Dim curline As String = ""
While (Not curline Is Nothing)
curline = fileReader.ReadLine()
'' need to check for valid data
'' if anything is invalid simply ignore it... i.e. your bad rows
'' keep in mind this will also ignore good rows that have a single piece of data bad
If (StringOK(curline)) Then
Dim newMac = GetMac(curline)
data.Add(newMac)
End If
End While
Return data
End Function
A couple of helper functions to validate the data
Private Function StringOK(inString As String) As Boolean
If (String.IsNullOrEmpty(inString)) Then
Return False
End If
Dim splitArray() As String = inString.Split(",")
Try
If ((String.IsNullOrEmpty(splitArray(0))) Or (String.IsNullOrEmpty(splitArray(1)))) Then
Return False
End If
Dim value As Integer
If ((Not Integer.TryParse(splitArray(2), value)) Or
(Not Integer.TryParse(splitArray(3), value)) Or
(Not Integer.TryParse(splitArray(4), value))) Then
Return False
End If
Return True
Catch ex As Exception
Return False
End Try
End Function
Function GetMac(inString As String) As Machine
Dim splitArray() As String = inString.Split(",")
Dim value As Integer
Dim name As String = splitArray(0)
Dim number As String = splitArray(1)
Integer.TryParse(splitArray(2), value)
Dim invIn As Integer = value
Integer.TryParse(splitArray(3), value)
Dim invOut As Integer = value
Integer.TryParse(splitArray(4), value)
Dim invMis As Integer = value
Return New Machine(name, number, invIn, invOut, invMis)
End Function
If you are trying to accomplish how to import the data into a datatable below is a fast way of handling that. This will bring your whole csv into a datatable which you could then do logic on and create your xlsx file.
Friend Shared Function GetExcelFile(ByVal strFileName As String, ByVal strPath As String) As DataTable
Try
Dim dt As New DataTable
Dim ConStr As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strPath & ";Extended Properties=""Text;HDR=Yes;FMT=Delimited\"""
Dim conn As New OleDb.OleDbConnection(ConStr)
Dim da As New OleDb.OleDbDataAdapter("Select * from " & strFileName, conn)
da.Fill(dt)
Return dt
Catch ex As Exception
Return Nothing
End Try
End Function

How to speed up the comparision of 2 collections in VBA?

I have written a macro that gets a collection of collection and than takes two of the collections and gives me the similarity.
Now if I compare the two collections with a simple for loop it will take hours to compare all 854 collection that are contained in pCol.
Here is my code:
Function CompareCollections(ByVal pCol As Collection) As Collection
Dim outer As Long
Dim inner As Long
'collections that will be compared to each other
Dim inCol As Collection
Dim outCol As Collection
'collection used for return values
Dim retCol As Collection
'result of single comparison
Dim res As CompResult
'comparison variables
Dim iIdx As Long
Dim oIdx As Long
Dim same As Long
Set retCol = New Collection
For outer = 1 To pCol.Count - 1
Set outCol = pCol(outer)
For inner = outer + 1 To pCol.Count
Set inCol = pCol(inner)
Set res = New CompResult
res.LeftTable = outCol(1) 'index 1 contains a header
res.RightTable = inCol(1)
'compare the two collections <== PART I WANT TO SPEED UP
same = 0
For oIdx = 2 To outCol.Count 'starting with 2 to ignore the header
For iIdx = 2 To inCol.Count
If inCol(iIdx) = outCol(oIdx) Then same = same + 1
Next iIdx
DoEvents
Next oIdx
res.Result1 = same / (outCol.Count - 1)
res.Result2 = same / (inCol.Count - 1)
retCol.Add res
Set res = Nothing
Set inCol = Nothing
DoEvents
Next inner
Set outCol = Nothing
DoEvents
Next outer
Set CompareCollections = retCol
End Function
I really hope you guys can help me.
EDIT:
The CompResult class is a simple structure, because I could not add a custom type to the collection:
Private mLeftTable As String
Private mRightTable As String
Private mResult1 As Double
Private mResult2 As Double
Public Property Get LeftTable() As String
LeftTable = mLeftTable
End Property
Public Property Let LeftTable(value As String)
mLeftTable = value
End Property
Public Property Get RightTable() As String
RightTable = mRightTable
End Property
Public Property Let RightTable(value As String)
mRightTable = value
End Property
Public Property Get Result1() As Double
Result = mResult1
End Property
Public Property Let Result1(value As Double)
mResult1 = value
End Property
Public Property Get Result2() As Double
Result = mResult2
End Property
Public Property Let Result2(value As Double)
mResult2 = value
End Property
A first tip: try to precalculate outCol.Count, inCol.Count and pCol.Count in order to avoid unnecessary calculations.
Second tip: if in your object CompResult the res.Result1 and res.Result2 are integers, use "\" instead of "/".
Third tip: try to use integers instead of long values wherever you can.
Fourth tip: try to replace for loops by a "for each" loops when looping for every column. It seems a little faster.
A last tip might be transform collections (ranges) in arrays and iterate through them, as it seems faster than iterate through ranges.

dynamic textbox input in VBA

my workbook has two sheets, one which contains data and calculations, these data have been name using the name editor function. the second worksheet has a graph and a box consisting of a group of text boxes created using VBA control Active X option, this group of text boxes is to be imputted with values previously named abbove, below is a version of the code I usued, where label and TextBox are the neames of the text boxes and the other names the names of the defined cells.
This code reports a 438 message error and would not input the labeled data in the box at the wanted location. How could I make it work so that the textboxes would display the named cells they are linked too?:
Private Sub Worksheet_Activate()
Dim sType As String
Dim sUnit As String
Dim sWellname As String
Dim sDate As String
Dim sMD As String
Dim sTVD As String
Dim sMud As String
Dim sPressure As String
Dim sEMW As String
sType = ThisWorkbook.Worksheets("sheet1").cbztest.Value
sUnit = ThisWorkbook.Worksheets("sheet1").cbzPressure.Value
sWname = ThisWorkbook.Worksheets("sheet1").Range("Wname").Value
sDate = ThisWorkbook.Worksheets("sheet1").Range("date").Value
sMD = Format(ThisWorkbook.Worksheets("sheet1").Range("MD").Value, "Standard")
sTVD = Format(ThisWorkbook.Worksheets("sheet1").Range("TVD").Value, "Standard")
sMW = ThisWorkbook.Worksheets("sheet1").Range("M_W").Value
sPressure = Round(ThisWorkbook.Worksheets("sheet1").Range("P_bar").Value, 1)
sEMW = Format(ThisWorkbook.Worksheets("sheet1").Range("EMW").Value, "Standard")
sType = ThisWorkbook.Worksheets("sheet1").cbztest.Value
ActiveSheet("Label").Caption = sType
ActiveSheet("TextBox1").Text = sWname
ActiveSheet("TextBox2").Text = sDate
ActiveSheet("TextBox5").Text = sMD
ActiveSheet("TextBox6").Text = sTVD
ActiveSheet("TextBox7").Text = sMW
ActiveSheet("TextBox8").Text = sPressure
ActiveSheet("TextBox9").Text = sEMW
ActiveSheet("Label8").Caption = sType & " EMW :"
ActiveSheet("Label13").Caption = sUnit
End Sub
Try ActiveSheet.TextBox1.Text = sWname and so on

Resources