Изучив ваш код, я не думаю, что коллекции или словари подходят. Я дал ответ, который, как я буду решать ваши требования. Я добавлю что-то на сборниках и словарях, если вы спросите, но я подозреваю, что этот ответ содержит достаточно, чтобы вы могли учиться на данный момент.
Сначала мне нужны были некоторые данные испытаний. Начиная с пустой таблицы, я заполнил строки от 1 до 10001 и столбцы с 1 по 155 с уникальными значениями. Я устанавливаю столбец B для повторений значений «A» на «Z». Я экспортировал эти данные в виде CSV-файла с именем «Import.csv».
Не изобретайте велосипед. Excel имеет совершенно адекватную процедуру для импорта CSV-файла, поэтому нет необходимости кодировать свою собственную процедуру в VBA. Я редко работаю с CSV-файлами, поэтому не помню синтаксис VBA операторов, необходимых для вызова процедуры импорта. Я включил макросъемку, импортировал CSV-файл (кроме первых 4 строк) и выключил макрорекордер. Я прибрал код макрорекордера, чтобы сформировать первую часть моей программы.
Макро-рекордер создает синтаксически правильный код, но не код хорошей практики. Он не знает ваших целей, поэтому записывает именно то, что вы делаете, когда делаете это. Я сомневаюсь, что у вас 155 столбцов, и вы можете указать для некоторых столбцов формат, отличный от «Общие». Вам придется переделать ручной импорт с вашими данными и убрать код так, как я.
Начало кода, записанного для меня было:
With ActiveSheet.QueryTables.Add(Connection:= _
"TEXT;C:\Users\Admin\Desktop\Import.csv", Destination:=Range("A1"))
.Name = "Import"
Я убирала это, чтобы получить:
With WshtIn
.Cells.EntireRow.Delete ' Delete existing content
With .QueryTables.Add(Connection:="TEXT;" & ActiveWorkbook.Path & _
"\Import.csv", Destination:=.Range("A1"))
.Name = "DataIn"
Лучше всего, чтобы избежать использования ActiveSheet
. Я указал переменную типа Worksheet, WshtIn
и установил ее на рабочий лист, который я хочу использовать.
Исходная строка связи TEXT;C:\Users\Admin\Desktop\Import.csv
- это единственный литерал, который я заменил выражением.
У меня почти всегда есть мои книги и файлы, которые они обрабатывают в одной папке. ActiveWorkbook.Path
дает мне папку с моей книгой. Используя это как имя моей папки, я могу переместить файлы в новую папку, и код все еще работает.
Destination:=Range("A1")
полагается на пункт назначения, находящийся в пределах ActiveSheet
. Добавление периода в начале для создания .Destination:=Range("A1")
означает, что пункт назначения находится на рабочем листе, определяемом With WshtIn
.
Наконец-то я заменил .Name = "Import"
на .Name = "DataIn"
, потому что мне не нужен рабочий лист, названный в качестве файла CSV.
Оставшийся код я сохранил без изменений, за исключением дополнительного End With
в конце. Как я уже сказал, вам придется заменить код импорта кодом, соответствующим вашим потребностям. Я предлагаю вам импортировать CSV-файл с включенным макрорекордером. Начните новый макрос на основе записанного кода и поиграйте с ним, пока не получите макрос, чтобы импортировать CSV так, как вы хотите, прежде чем смотреть на следующий бит моего кода.
Вы хотите только 11 столбцов этого CSV-файла. Поэтому я закодировал цикл, который перемещает эти 11 столбцов на новый рабочий лист «DataKeep». Макро-рекордер не выполняет петли, поэтому не будет никакой помощи; вы должны знать соответствующий синтаксис, чтобы закодировать это. Я использовал массив для определения столбцов для перемещения. Я считаю, что я правильно указал столбцы, но вам нужно будет проверить. Вероятно, лучше всего добавить этот код в свой макрос и получить его правильно, прежде чем двигаться дальше.
Рабочий лист «DataKeep» содержит только нужные данные. Я отбросил первые 4 строки, когда я их импортировал, и я оставил только интересующую колонку. Далее по порядку вы найдете:
Data = .Range(…).Value
Это загружает содержимое диапазона в Data как двухмерный массив. Для большинства массивов соглашение состоит в том, чтобы иметь столбцы как первое измерение, а строки - как второе, так что ReDim Preserve
может использоваться для увеличения количества строк. Однако для массивов, загружаемых с листа или подготовленных для загрузки на рабочий лист, первое измерение относится к строкам, а второе относится к столбцам. Это полезно, потому что оно соответствует синтаксису для ячейки: Cells(RowNumber, ColNumber).
В конце моего макроса я покажу, как получить доступ к данным, отобразив первые 20 строк. Это не список коллекций, которые у вас есть в коде, но я считаю, что вы найдете массив более удобным.
Самый простой способ получить список уникальных значений для столбца Set - использовать расширенный автофильтр. Опять же, это то, что я не использую очень часто, и у меня нет правильного синтаксиса на моих кончиках пальцев. Я использовал макрорекордер для захвата необходимого кода, когда создал список уникальных наборов. Я скопировал видимые ячейки в этом списке как массив до UniqueSets
. Я считаю, что мой код будет соответствовать вашим требованиям без поправок, но вы можете попытаться создать этот код с помощью макрорекордера в качестве учебного упражнения.
Проработайте мой код, обновив его при необходимости и изучите, как я достиг эффекта, который у меня есть. В коде есть больше инструкций и объяснений. Вернитесь с вопросами по мере необходимости, но чем больше вы сами поймете, тем быстрее вы будете развиваться.
Option Explicit
' Constants allow you to name columns rather than use numbers or letters that
' may change. If the position of a column changes, amend the Const statement and
' the code is fully updated. Searching code for the old column number so it can
' be updated to the new can be a nightmare.
' I have guessed names for the columns based on your code. Change as necessary.
' ColKeepSet is the only one I use.
Const ColKeepSN As Long = 1
Const ColKeepSet As Long = 2
Const ColKeepFF As Long = 3
Const ColKeepVHCC As Long = 4
Const ColKeepVHCCMID As Long = 5
Const ColKeepVHCVMID As Long = 6
Const ColKeepVHCV1 As Long = 7
Const ColKeepVHCV2 As Long = 8
Const ColKeepVHCV3 As Long = 9
Const ColKeepVHCV4 As Long = 10
Const ColKeepVHCV5 As Long = 11
Sub Import()
Dim ColInCrnt As Variant
Dim ColKeepCrnt As Long
Dim ColKeepLast As Long
Dim ColWidths() As Long
Dim Data As Variant
Dim Headings As Variant
Dim RngFilter As Range
Dim RngUnique As Range
Dim RowKeepCrnt As Long
Dim RowKeepLast As Long
Dim RowKeepSetLast As Long
Dim RowUnqCrnt As Long
Dim UniqueSets As Variant
Dim WshtIn As Worksheet
Dim WshtKeep As Worksheet
' Change the names of the worksheets as necessary
Set WshtIn = Worksheets("DataIn")
Set WshtKeep = Worksheets("DataKeep")
' Import the CSV file. Change "Import.csv" to your filename. Change folder if necessary.
With WshtIn
.Cells.EntireRow.Delete ' Delete existing content
With .QueryTables.Add(Connection:="TEXT;" & ActiveWorkbook.Path & "\Import.csv", Destination:=.Range("A1"))
.Name = "DataIn"
.FieldNames = True
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.TextFilePromptOnRefresh = False
.TextFilePlatform = 850
.TextFileStartRow = 5
.TextFileParseType = xlDelimited
.TextFileTextQualifier = xlTextQualifierDoubleQuote
.TextFileConsecutiveDelimiter = False
.TextFileTabDelimiter = False
.TextFileSemicolonDelimiter = False
.TextFileCommaDelimiter = True
.TextFileSpaceDelimiter = False
.TextFileColumnDataTypes = Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, _
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
.TextFileTrailingMinusNumbers = True
.Refresh BackgroundQuery:=False
End With
End With
' Copy the required columns from worksheet "DataIn" to "DataKeep"
WshtKeep.Rows.Delete ' Discard any reviosu data
ColKeepCrnt = 1
For Each ColInCrnt In Array(1, 2, 15, 97, 98, 99, 100, 135, 136, 137, 138)
WshtIn.Columns(ColInCrnt).Copy Destination:=WshtKeep.Cells(1, ColKeepCrnt)
ColKeepCrnt = ColKeepCrnt + 1
Next
' Delete contents of Worksheet "DataIn" which are no longer needed
WshtIn.Rows.Delete
With WshtKeep
RowKeepSetLast = .Cells(Rows.Count, ColKeepSet).End(xlUp).Row
Set RngFilter = .Range(.Cells(1, ColKeepSet), _
.Cells(RowKeepSetLast, ColKeepSet))
.Columns(ColKeepSet).AutoFilter
RngFilter.AdvancedFilter Action:=xlFilterInPlace, Unique:=True
' One copy of each unqiue set will be visible. In addition row 1 will be visible
' because Excel assumes it is a header row.
Set RngUnique = .Range(.Cells(2, ColKeepSet), _
.Cells(RowKeepSetLast, ColKeepSet)).SpecialCells(xlCellTypeVisible)
Debug.Print RngUnique.Address
UniqueSets = RngUnique.Value
.Columns(ColKeepSet).AutoFilter ' Clear
' There are various methods of finding the last used row and column. Above I have used
' .End(xlUp) which is the easiest method of finding the last row of a column. Your data
' is almost certainly rectangular so I could have assumed that the last row of the Set
' column is the last row of all columns. Since I have saved selected columns, I could
' have deduced the last column from that. However, I have decided to show a different
' technique.
' Both of the following statements use Find to locate the last cell contaning a value.
' Both start the search "After" cell A1 and the search direction is "xlPrevious".
' The previous cell from A1 is the bottom, right cell so both searches got up and across
' until they find a cell with a value. In the first the search order is "xlByRows" and
' the second it is "xlByColumns". So the first find the first row with a value and the
' second the first column with a value. If the data is arranged in a neat rectangle, the
' last row and the last column will be for the same cell. But if the data is not a neat
' rectangle these statements will still the correct results.
RowKeepLast = .Cells.Find(What:="*", After:=.Range("A1"), LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
ColKeepLast = .Cells.Find(What:="*", After:=.Range("A1"), LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Data = .Range(.Cells(1, 1), _
.Cells(RowKeepLast, ColKeepLast)).Value
End With
' Output all the unique sets
Debug.Print "Unique Sets"
For RowUnqCrnt = 1 To UBound(UniqueSets, 1)
Debug.Print UniqueSets(RowUnqCrnt, 1)
Next
' Output the first 20 rows of the data
' This will ReDim Headings as an array with a lower bound of 0
Headings = VBA.Array("SN", "Set", "FF", "VHCC", "VHCCMID", "VHCVMID", _
"VHCV1", "VHCV2", "VHCV3", "VHCV4", "VHCV5")
ReDim ColWidths(1 To UBound(Data, 2))
' Caluclate maximum width of each column
For ColKeepCrnt = 1 To UBound(Data, 2)
ColWidths(ColKeepCrnt) = Len(Headings(ColKeepCrnt - 1))
Next
For RowKeepCrnt = 1 To 20 ' Replace 20 by Ubound(Data, 1) to include all rows
For ColKeepCrnt = 1 To ColKeepLast
If ColWidths(ColKeepCrnt) < Len(Data(RowKeepCrnt, ColKeepCrnt)) Then
ColWidths(ColKeepCrnt) = Len(Data(RowKeepCrnt, ColKeepCrnt))
End If
Next
Next
' Output data
Debug.Print "Data"
Debug.Print "|";
For ColKeepCrnt = 1 To ColKeepLast
Debug.Print PadR(Headings(ColKeepCrnt - 1), ColWidths(ColKeepCrnt)) & "|";
Next
Debug.Print
For RowKeepCrnt = 1 To 20
Debug.Print "|";
For ColKeepCrnt = 1 To ColKeepLast
Debug.Print PadR(Data(RowKeepCrnt, ColKeepCrnt), ColWidths(ColKeepCrnt)) & "|";
Next
Debug.Print
Next
End Sub
Function PadR(ByVal Str As String, ByVal PadLen As Long) As String
If Len(Str) >= PadLen Then
' Do not truncate over length strings
PadR = Str
Else
PadR = Left$(Str & Space(PadLen), PadLen)
End If
End Function
Посмотрите словари. Коллекции не требуют уникальных ключей, поэтому словари, которые будут лучше соответствовать вашему требованию.Кроме того, словари быстрее с большим количеством записей, потому что к ним обращается хэшированный ключ, в то время как коллекции доступны секвенциально с самого начала. –