Get all columns from Excel except selected - excel

I have excel files converted to txt. In some files, some columns are skipped. That is controlled by database:
file | remove_column
=======+===============
file1 | CASE NOTE
-------+---------------
file2 | Description
-------+---------------
file3 | Item | Address
Remove_Column has the header (1st row). If several columns should be skipped, they are delimited with '|'
I have to compare converted txt file with original excel file if they match. How can I read all columns except those showed in DB table?
I am using UFT 12.5. Reading Excel through Excel.Application or ADO.
Thnx)
UPD: Code I use:
I have columns hard-coded:
Select Case OrigFileName 'file names come from database
Case "Fees mm-yy.xls"
ColumnNames = Split("1,2,3,4,5,6,7,8,9,10,11,12,13", ",")
Case "Exp mm-yy.xls"
ColumnNames = Split("1,2,3,4,5,6,7,8,9,12,13,14,15,16,19,20", ",")
End Select
But there are 50 files, and the business might ask to remove or to add back any columns; also, new files are coming...(((
Dim fsox : Set fsox = CreateObject("Scripting.FileSystemObject")
Dim TargFileRead : Set TargFileRead = fsox.OpenTextFile(targetFile)
Dim OrgExcel : Set OrgExcel = CreateObject("Excel.Application")
OrgExcel.Workbooks.Open(originalfile)
Set vSheet = OrgExcel.WorkSheets(TabUse) 'excel sheet name, comes from database
print vSheet.UsedRange.Rows.Count
For rc = 1 To vSheet.UsedRange.Rows.Count
For coc = 0 To UBound(ColumnNames) 'column names hard-coded
cc = cInt(ColumnNames(coc))
vtext = vSheet.cells(rc,cc)
If NOT(vtext=ChrW(9)) Then
If vstring="" Then
vstring=vtext
Else
vstring = vstring&vbTab&vtext
End If
End If
If len(vstring)>0 Then
TargFileText = TargFileRead.ReadLine
Do
If Left(TargFileText, 1)=ChrW(9) Then
TargFileText = MID(TargFileText, 2)
Else
Exit Do
End If
Loop
Do
If RIGHT(TargFileText, 1)=ChrW(9) Then
TargFileText= mid(TargFileText,1,len(TargFileText)-1)
Else
Exit Do
End If
Loop
TargFileStr = Trim(TargFileText)
If trim(vstring) = trim(TargFileStr) Then
' print "match"
Else
print "-=Not Match=-"&VBNewLine&"txt:::"&trim(TargFileStr)&VBNewLine&"xls:::"&trim(vstring)
End If
End If
Next

I would suggest to replace the Switch statement with a function call that gives you the relevant columns for the sheet as an array. The logic which column is allowed is then put in another function. That should make the logic more flexible than fixed columns.
Function getColumns(OrigFileName as String) As String()
Dim lastCol As Integer
Dim ColumnNumbers As String
lastCol = Sheets(OrigFileName).UsedRange.Columns.Count
For col = 1 To lastCol
If isColumnAllowed(OrigFileName, Sheets(OrigFileName).Cells(1, col)) Then
ColumnNumbers = ColumnNumbers & IIf(Len(ColumnNumbers) = 0, "", ",") & col
End If
Next
getColumns = Split(ColumnNumbers, ",")
End Function
Function isColumnAllowed(ByVal OrigFileName As String, columnName As String) As Boolean
Select Case OrigFileName
Case "file1"
Forbidden = Split("CASE NOTE", "/")
Case "file2"
Forbidden = Split("Description", "/")
Case "file3"
Forbidden = Split("Item/ Address", "/")
End Select
isColumnAllowed = (UBound(Filter(Forbidden, columnName)) = -1)
End Function

This is what I have now and is working:
If LEN(ColumnToRemove)>0 Then
ColumnToRemoveCol = split(ColumnToRemove, "|") 'set collection of header strings to skip column
For L = 1 To vSheet.UsedRange.Columns.Count
For x = 0 to UBound(ColumnToRemoveCol)
AddCol = 0 'ColumnToRemoveCol can have more than 1 item, that may cause any column to be added more than once; we will use the true/false logic via 0 and 1 to avoid that doubling
If vSheet.cells(1, l)=ColumnToRemoveCol(x) Then
AddCol = AddCol + 1
End If
Next
If AddCol =0 Then ColumnNumbers = ColumnNumbers&","&L
Next
Else
For L = 1 To vSheet.UsedRange.Columns.Count
ColumnNumbers = ColumnNumbers&","&L
Next
End If
If LEFT(ColumnNumbers, 1)="," Then ColumnNumbers=MID(ColumnNumbers, 2)
If RIGHT(ColumnNumbers, 1)="," Then ColumnNumbers=MID(ColumnNumbers, 1, LEN(ColumnNumbers)-1)
Printing the columns for first excel file in my case gives the next line:
ColumnNumbers: 1,2,3,4,5,6,7,8,10,11,12,15,16,17
Further usage:
getColumns = Split(ColumnNumbers, ",")
For rc = 1 To vSheet.UsedRange.Rows.Count
For coc = 0 To UBound(getColumns)
cc = cInt(getColumns(coc))
vtext = vSheet.cells(rc,cc)
.....
Next
Next

Related

VBA: Multi-select Listbox to a single cell but without duplicates

I haven't used VBA in about 10 years until needing it this week, so my recall is not that great right now - appreciate any advice you are able to give!
I have a User form where there is a multiple selection listbox option that inserts the selected items into a single cell separated by a comma. The list referenced for the listbox has 2 columns - a GROUP and a PROJECT name.
Multiple projects can fall under the same group. I have the group column going to one cell and the project to another, but if users multi-select projects from the same group they will get the same group name repeated.
How can I adjust this to allow the group name to only appear once in a cell?
Adding grouping to Excel sheet:
For X = 0 To Me.listbox_group.ListCount - 1
If Me.listbox_group.Selected(x) Then
If varGroup = "" Then
varGroup = Me.listbox_group.List (x,0)
Else
varGroup = varGroup & ", " & Me.listbox_group.List(x,0)
End If
End If
Next x
Specifying cell location for the selection to go to:
Sheets("Data").Range("Data_Start").Offset(TargetRow, 0).Value = UCase(varGroup)
In order to get only unique values you could use a dictionary
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
For x = 0 To Me.listbox_group.ListCount - 1
If Me.listbox_group.Selected(x) Then
dict(listbox_group.List(x, 0)) = listbox_group.List(x, 0)
' If varGroup = "" Then
' varGroup = Me.listbox_group.List(x, 0)
' Else
' varGroup = varGroup & ", " & Me.listbox_group.List(x, 0)
' End If
End If
Next x
Dim s As Variant
s = Join(dict.Keys, ",")
Sheets("Data").Range("Data_Start").Offset(TargetRow, 0).Value = UCase(s)
I only assign values because there is a kind of extra feature: If the Key does not exist it automatically adds the Key and Item to the dictionary.
Upper ander lower case pitfall: The above code will a consider groups with the name G1 and g1 as different. If you do not want that use
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
For x = 0 To Me.listbox_group.ListCount - 1
If Me.listbox_group.Selected(x) Then
Dim selElement As String
selElement = UCase(listbox_group.List(x, 0))
dict(selElement) = selElement
' If varGroup = "" Then
' varGroup = Me.listbox_group.List(x, 0)
' Else
' varGroup = varGroup & ", " & Me.listbox_group.List(x, 0)
' End If
End If
Next x
Dim s As Variant
s = Join(dict.Keys, ",")
Sheets("Data").Range("Data_Start").Offset(TargetRow, 0).Value = s

Transpose irregular data from .txt import to a table in Excel

I have imported a bunch of data from tables in Word -> .txt -> Excel, but in the conversion to .txt the format of the table is lost and I am now trying to recover it in Excel.
I would just make a simple Copy/Paste Macro based on the Cell Range, but the cell ranges are not the same across each imported .txt file so this won't work as the same data could be in A8 in one sheet then A10 in another.
You could almost move every other row into column B, but the "Date Due" field throws it out of sync.
I want to transpose the copied date into a more functional table format - see picture for example - which I can then analyze. I need to do this for hundreds of sheets but I'm hoping that if I can get it working for one then I can adapt it to work across many.
Each sheet may have multiple products, each new product is preceded by an integer (e.g. 1. Product1; 2. Product2 etc...)
deleted old code that I'd tried to do
EDIT2:
screenshot of typical notepad file
There is a load of text above this, but the first product always starts with QUOTATION MACHINE SCHEDULE, then 1. xxx
EDIT3:
Tried to add in the column I to have Quote Ref: and find this in the text file, but it didn't work. Code change from CDP1802 current solution below
'results sheet
Set ws = ThisWorkbook.Sheets("Results")
ws.Range("A1:I1") = Array("Item", "Date Due", "Type", "Serial Number", "Standard", "Mode", "Range", "Location", "Quote Ref:")
r = 1 ' output row
Select Case s
' match word to set column
Case "type": c = 3
Case "serial number": c = 4
Case "standard": c = 5
Case "mode": c = 6
Case "range": c = 7
Case "location": c = 8
Case "Quote Ref:": c = 9
Case Else: c = 0
End Select
New Screenshot for Quote Ref:
Read the text file into an array and then scan for key words using Select Case. Reading the data into an array allows you to select the value from the row below the key word.
update - Quote Ref added
Option Explicit
Sub ProcessTextFiles1()
Const FOLDER = "C:\temp\SO\Data\" ' folder where the text files are
Dim ws As Worksheet, sFilename As String, sQuoteRef As String
Dim n As Integer, i As Long, r As Long, c As Long
Dim fso As Object, ts As Object, ar() As String, s As String
Set fso = CreateObject("Scripting.FileSystemObject")
' results sheet
Set ws = ThisWorkbook.Sheets("Results")
ws.Range("A1:I1") = Array("Item", "Date Due", "Type", "Serial Number", _
"Standard", "Mode", "Range", "Location", "Quote Ref")
r = 1 ' output row
' scan each file in folder
sFilename = Dir(FOLDER & "*.txt")
Do While Len(sFilename) > 0
n = n + 1
sQuoteRef = ""
' open file and read into array
Set ts = fso.OpenTextFile(FOLDER & sFilename)
s = ts.readAll
s = Replace(s, vbLf, "")
ar = Split(s, vbCr)
ts.Close
'MsgBox sFilename & "=" & UBound(ar)
' parse the strings in the array
i = 0
Do While i <= UBound(ar)
s = LCase(Trim(ar(i)))
'Debug.Print s
If Len(s) > 0 Then ' skip blanks
If Left(s, 10) = "quote ref:" Then
sQuoteRef = ar(i + 1)
End If
If Left(s, 2) Like "#." Or Left(s, 3) Like "##." Then
' new product
r = r + 1
ws.Cells(r, 1) = ar(i)
ws.Cells(r, 9) = sQuoteRef
Else
Select Case s
' match word to set column
Case "type": c = 3
Case "serial number": c = 4
Case "standard": c = 5
Case "mode": c = 6
Case "range": c = 7
Case "location": c = 8
Case Else: c = 0
End Select
' take value below
If c = 4 Then
ws.Cells(r, 4) = ar(i + 1)
ws.Cells(r, 2) = ar(i + 2) ' due date
i = i + 2
ElseIf c > 1 Then
ws.Cells(r, c) = ar(i + 1)
i = i + 1
End If
End If
End If
i = i + 1
Loop
sFilename = Dir
Loop
MsgBox n & " Files processed", vbInformation
End Sub

Excel VBA - Loop a specific column in structured table and get corresponding cells values from other columns

I start using structured table with VBA code, but here I face a problem which I do not manage by myself.
I have a structured table in which I loop through 1 column and need to get values from other columns depending on some criteria:
if the cells value is "Finished"
then I take 3 dates (dateScheduled, dateRelease and dateReady) from 3 other columns and perform some calculations and tests based on these dates
the problem is that I can get the values of the date columns (they are well formatted and have values in it), so none of the next actions triggered by the first if is working.
Here is part of the whole code of my macro, I hope this is sufficient to figure out what is wrong.
For Each valCell In Range("thisIsMyTable[Task Status]").Cells
If valCell = "Finished" Then
dateScheduled = Range("thisIsMyTable[End Date]").Cells
dateRelease = Range("thisIsMyTable[Release Date]").Cells
dateReady = Range("thisIsMyTable[Date Ready]").Cells
totalFinishCat = totalFinishCat + 1
daysToFinished = daysToFinished + DateDiff("d", dateReady, dateRelease)
If Range("thisIsMyTable[Time Spent]").Cells = "" Then
timeTotalFinished = timeTotalFinished + Range("thisIsMyTable[Time estimate]").Cells + Range("thisIsMyTable[Extra hours]").Cells
Else
timeTotalFinished = timeTotalFinished + Range("thisIsMyTable[Time Spent]").Cells
End If
If dateRelease >= dateStartReport Then
monthFinished = monthFinished + 1
timeMonthFinished = timeMonthFinished + Range("thisIsMyTable[Time Spent]").Cells
daysToFinishedMonth = daysToFinishedMonth + DateDiff("d", dateReady, dateRelease)
If dateRelease > dateScheduled Then
afterDue = afterDue + 1
diff = DateDiff("d", dateScheduled, dateRelease)
afterDay = afterDay + diff
Else
beforeDue = beforeDue + 1
diff = DateDiff("d", dateRelease, dateScheduled)
beforeDay = beforeDay + diff
End If
End If
End If
Next valCell
I have tried out by adding .value or .value2 like so:
dateScheduled = Range("thisIsMyTable[End Date]").Cells.value
or
dateScheduled = Range("thisIsMyTable[End Date]").Cells.value2
but it does not work better. I have checked by adding .select like so:
dateScheduled = Range("thisIsMyTable[End Date]").Cells.select
and this will select the entire column, not the cells as I expect. So it appears that my method to just get the cells value is not appropriate.
Any help is welcome
If you create a lookup of column names to column number, you can loop through the rows of the table and extract the value using Range(1, columnno). For example
Option Explicit
Sub MyMacro()
Dim ws As Worksheet, tb As ListObject
Dim r As ListRow
Dim sStatus As String, dEnd As Date, dRelease As Date, dReady As Date
' table
Set ws = Sheet1
Set tb = ws.ListObjects("thisIsMyTable")
' lookup column name to number
Dim dict As Object, c As ListColumn
Set dict = CreateObject("Scripting.Dictionary")
For Each c In tb.ListColumns
dict.Add c.Name, c.Index
Next
' scan table rows
For Each r In tb.ListRows
sStatus = r.Range(1, dict("Task Status"))
If sStatus = "Finished" Then
dEnd = r.Range(1, dict("End Date"))
dRelease = r.Range(1, dict("Release Date"))
dReady = r.Range(1, dict("Date Ready"))
Debug.Print dEnd, dRelease, dReady
End If
Next
End Sub

excel vba formula determining highest value based on text

Hi currently i'm having a problem regarding the displaying of the most significant text among 4 rows in one column . What I have here is remarks of clients which is excellent,good,fair and bad ..and i would like to display the word excellent on a cell if it is present in that column , otherwise if good is the highest value present then it should display it ,if fair then fair or and lastly if bad then display bad
enter image description here
Hope this is not too late to answer your question. Try the following formula:
=INDEX({"Bad","Fair","Good","Excellent"},MATCH(1,(MATCH({"Bad","Fair","Good","Excellent"},B2:E2,0)),0))
See the image for reference:
It's not a formula, but the main trouble, as I see, is not to grade four known values you listed above, but to exclude empty and unknown values. Moreover, when such happened, user must be informed about it and make the right decision...
'''''''
Private Sub sb_Test_fp_Grade3()
Debug.Print fp_Grade3(Selection, 1, True)
End Sub
Public Function fp_Grade3(pRng As Range, _
Optional pUnkMod& = 0, _
Optional pEmpDen As Boolean = False) As String
' pUnkMod - Mode of UnKnown grades handling
' 0-Ignore; 1-Info only; 2-Deny
' pEmpDen - Deny or not empty values. If Deny, then empty treated as Unknown
' according pUnkMod setting
Const S_BAD As String = "BAD"
Const S_FAI As String = "FAIR"
Const S_GOO As String = "GOOD"
Const S_EXC As String = "EXCELLENT"
Const S_UNK As String = "UNK" ' UNKNOWN
Dim rCell As Range
Dim lVal&, lMax&, lUnk&
Dim sGrades$(0 To 4), sRet$, sVal$
sGrades(0) = S_UNK
sGrades(1) = S_BAD
sGrades(2) = S_FAI
sGrades(3) = S_GOO
sGrades(4) = S_EXC
lMax = 0
lUnk = 0
sRet = vbNullString
For Each rCell In pRng
sVal = rCell.Value
If (LenB(sVal) > 0 Or pEmpDen) Then
Select Case UCase(rCell.Value)
Case S_BAD: lVal = 1
Case S_FAI: lVal = 2
Case S_GOO: lVal = 3
Case S_EXC: lVal = 4
Case Else: lVal = 0
End Select
Select Case (lVal > 0)
Case True ' Known values
If (lVal > lMax) Then
lMax = lVal
If (lMax = 4) Then
If (pUnkMod = 0) Then Exit For
End If
End If
Case False ' UnKnown values
Select Case pUnkMod
Case 0 ' ignore them
' do nothing
Case 1 ' info about them
lUnk = lUnk + 1
Case Else ' 2 & any others - stop
lMax = 0
Exit For
End Select
End Select
End If
Next
If (lUnk > 0) Then sRet = " & " & lUnk & "x" & S_UNK
sRet = sGrades(lMax) & sRet
fp_Grade3 = sRet
End Function
'''

How to change the name of a column?

I want to change the names of the columns in an Excel sheet.
I want to rename the column headers as in the image.
You don't say if there is more than one area with these headers, so my solution will work for several. Unfortunately it will work for a maximum of 26 columns, one for each letter of the alphabet. You can expand it if you want.
I've included workbook and worksheet names, these are particular to me, you'll obviously have to change them.
The code looks for areas of data and steps through them, changing the headers on each one. You can adapt the code to just do one area of course. The code starts in the top left of the area, and steps across one cell at a time until it runs out of data, then it moves onto the next area.
Sub NewColumnNames()
Dim FruityColumnNames(26) As String
Dim a As Integer
Dim Alphabetical As Integer
FruityColumnNames(1) = "Apple"
FruityColumnNames(2) = "Banana"
FruityColumnNames(3) = "Cherry"
FruityColumnNames(4) = "Damson"
FruityColumnNames(5) = "Elderberry"
FruityColumnNames(6) = "Fig"
FruityColumnNames(7) = "Gooseberry"
FruityColumnNames(8) = "Hawthorn"
FruityColumnNames(9) = "Ita palm"
FruityColumnNames(10) = "Jujube"
FruityColumnNames(11) = "Kiwi"
FruityColumnNames(12) = "Lime"
FruityColumnNames(13) = "Mango"
FruityColumnNames(14) = "Nectarine"
FruityColumnNames(15) = "Orange"
FruityColumnNames(16) = "Passion fruit"
FruityColumnNames(17) = "Quince"
FruityColumnNames(18) = "Raspberry"
FruityColumnNames(19) = "Sloe"
FruityColumnNames(20) = "Tangerine"
FruityColumnNames(21) = "Ugli"
FruityColumnNames(22) = "Vanilla"
FruityColumnNames(23) = "Watermelon"
FruityColumnNames(24) = "Xigua"
FruityColumnNames(25) = "Yumberry"
FruityColumnNames(26) = "Zucchini"
With Workbooks("TestBook.xlsx")
With .Worksheets("Destination")
With .UsedRange.SpecialCells(xlCellTypeConstants)
For a = .Areas.Count To 1 Step -1
Alphabetical = 1
With .Areas(a)
While (.Cells(1, Alphabetical) <> "" And Alphabetical <= 26)
.Cells(1, Alphabetical).Value = FruityColumnNames(Alphabetical)
Alphabetical = Alphabetical + 1
Wend
End With
Next a
End With
End With
End With
End Sub
Example call (with reduced code):
Sub ChangeHeaderNames()
Dim headers: headers = Split("Apple,Banana,Cherry", ",")
Sheet1.Range("A1").Resize(1, UBound(headers) + 1) = headers ' << change to sheet's Code(Name)
End Sub

Resources