Как различить несмежные диапазоны? - PullRequest
1 голос
/ 06 января 2020

Я пытаюсь выполнить несколько операций с динамическим c числом диапазонов, которые также имеют динамический размер. Каждый раз, когда программа запускается, число несмежных диапазонов может изменяться, а также размер диапазонов. Я определяю свои диапазоны, находя разделитель в столбце А, который является подчеркиванием. Прямо сейчас VBA распознает диапазоны, представленные ниже, как две разные области, но выбирает их одновременно при вызове, чтобы найти строки, содержащие разделитель, и это правильно. Я перемещаю свои области диапазона с одной страницы на другую. В первом столбце каждого диапазона, столбце A, есть имя, а остальная часть строки содержит различные числа. В каждом диапазоне имена строк различаются, но все они соответствуют именам строк в других диапазонах. Например, все первые имена строк в каждом диапазоне заканчиваются на _1. Имя второй строки в диапазоне (или ячейка в строке под первой строкой) заканчивается на _2 для каждого диапазона. Ниже приведено изображение:

enter image description here

Я знаю, что могу удалить пустую строку между каждым диапазоном, но я не думаю, что это лучшее решение для моего желаемого выхода. Я хотел бы взять первый ряд первой области (в этом примере A5: E5), транспонировать его и вставить на новый лист из (в этом примере) B5: B9. Затем я хотел бы go перейти к следующей области и сделать то же самое (например, скопировать A9: E9), транспонировать его и вставить его на тот же лист в следующем столбце (так, C5: C9). Затем я бы хотел go вернуться к своему первому диапазону, взять следующий ряд (A6: E6), перенести его и вставить в D5: D9, затем go в следующую область и взять (A10: E10). ) и так далее, пока все диапазоны не будут вставлены на новую страницу. Так что в идеале я бы хотел чередовать диапазоны и вставлять каждую строку в столбец непрерывно через каждый диапазон. Это мой желаемый результат:

enter image description here

Мне трудно получить данные так, как я хочу, и знаю, что у меня может не быть Возможность чередовать диапазоны, так что я готов услышать любые идеи. Опять же, количество диапазонов (или областей) может меняться с каждым прогоном, как и размер диапазонов. В этом примере у меня было два диапазона с 3 строковыми именами, но при следующем запуске у меня могло быть три диапазона с 4 строковыми именами, поэтому я ничего не могу жестко кодировать. Имена строк не всегда будут одинаковыми (input_x, output_x), поэтому я тоже не могу жестко их кодировать, но я могу найти разделитель, подчеркивание в имени строки, так как он всегда будет отформатирован в имя строки.

Мой текущий вывод ДОЛЖЕН копироваться и вставляться каждой ячейкой в ​​области, но это лучшее, что я получил с тем, что я пробовал:

enter image description here

Вот мой код (комментарии - это некоторые вещи, которые я пробовал):

Dim myRange as Range
Dim c as Range, a as Range
Dim t As Long, m as Long
Dim delimiterItem as Variant
Dim newSheetName as String

newSheetName = ActiveSheet.Name
delimiterItem = "_"
t = 2

    With Sheets.Add(After:=Sheets(Sheets.Count))
        .Name = "Final"


        If myRange Is Nothing Then
            MsgBox ("Value not present in this workbook.")
        Else
            For Each a In myRange.Areas

                For Each c In a.Rows
                    Worksheets(newSheetName).Activate
                    c.EntireRow.Copy
                    'For m = Cells(myRange.Rows.Count, 1).End(xlUp).Row To 2 Step -1
                    'If Split(InStrRev(myRange.Cells(m, 1).Text, delimiterItem))(0) = Split(InStrRev(myRange.Cells(m - 1, 1).Text, delimiterItem))(0) Then

                    Worksheets("Final").Activate
                    Cells(8, t).Select
                    Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
                          False, Transpose:=True

                    t = t + 1
                    'Sheets("Final").Range("B8").Offset(0, (t - 2) * 2).PasteSpecial xlPasteValues
                Next c
             Next a
        End If
    End With

Любая помощь или указатели в правильном направлении будут высоко оценены. Спасибо! Извините за длину этого вопроса.

Ответы [ 2 ]

1 голос
/ 06 января 2020

Вот альтернативный пример, который работает с использованием массивов на основе памяти для скорости, а также помогает с транспозицией и правильным размером данных.

Этот алгоритм предполагает:

  1. У вас всегда будут наборы данных, начинающиеся с _1
  2. Номера наборов данных всегда увеличиваются последовательно, например, _1, _2, _3 и c
  3. Всегда будет одинаковое количество «групп» данных, поскольку есть наборы данных.

Мои данные для примера выглядят так:

enter image description here

Сначала я перемещаю данные в массив на основе памяти

Dim inData As Variant
inData = Sheet1.UsedRange.Value

Затем, основываясь на этих предположениях, для правильной сортировки результатов вам необходимо выяснить, как много наборов данных у вас есть. Поэтому я создал эту функцию, которая использует функцию Split для получения числового значения после подчеркивания:

Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
    '--- runs through the labels in the first column and separates
    '    the number value following the underscore to find the maximum
    '    count of data sets
    Dim max As Long
    Dim i As Long
    For i = LBound(data, 1) To UBound(data, 1)
        Dim tokens As Variant
        tokens = Split(data(i, 1), "_")
        If UBound(tokens) > 0 Then
            If max < tokens(1) Then max = tokens(1)
        End If
    Next i
    DetermineNumberOfSets = max
End Function

Итак, основная подпрограмма вызывает

Dim dataSetCount As Long
Dim columnCount As Long
dataSetCount = DetermineNumberOfSets(inData)

'--- this determines how many columns are in the output data
Dim allSetsCount As Long
allSetsCount = dataSetCount * dataSetCount

Путем вычисления allSetsCount таким образом, вы фактически пропускаете все пустые строки во входных данных.

Теперь создайте массив для хранения всех транспонированных данных

'--- this determines how many rows are in the output data
Dim maxDataPointsCount As Long
maxDataPointsCount = UBound(inData, 2)

Dim outData As Variant
ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)

И, наконец, просмотрите ваши данные в собирать наборы данных по порядку и переносить данные в выходной массив.

Dim setNumber As Long
For setNumber = 1 To dataSetCount
    '--- run through the data and pick out the data for this set
    Dim i As Long
    For i = LBound(inData, 1) To UBound(inData, 1)
        Dim thisSetNumber As Long
        thisSetNumber = WhatsTheDataSet(inData(i, 1))
        If thisSetNumber = setNumber Then
            '--- copy this set to the output
            Dim j As Long
            For j = 1 To maxDataPointsCount
                outData(j, outputColumn) = inData(i, j)
            Next j
            outputColumn = outputColumn + 1
        End If
    Next i
Next setNumber

Вот весь модуль для всего кода

Option Explicit
Option Base 0

Public Sub CollateData()
    Dim inData As Variant
    inData = Sheet1.UsedRange.Value

    Dim dataSetCount As Long
    Dim columnCount As Long
    dataSetCount = DetermineNumberOfSets(inData)

    '--- this determines how many columns are in the output data
    Dim allSetsCount As Long
    allSetsCount = dataSetCount * dataSetCount

    '--- this determines how many rows are in the output data
    Dim maxDataPointsCount As Long
    maxDataPointsCount = UBound(inData, 2)

    Dim outData As Variant
    ReDim outData(1 To maxDataPointsCount, 1 To allSetsCount)

    Dim outputColumn As Long
    outputColumn = 1

    Dim setNumber As Long
    For setNumber = 1 To dataSetCount
        '--- run through the data and pick out the data for this set
        Dim i As Long
        For i = LBound(inData, 1) To UBound(inData, 1)
            Dim thisSetNumber As Long
            thisSetNumber = WhatsTheDataSet(inData(i, 1))
            If thisSetNumber = setNumber Then
                '--- copy this set to the output
                Dim j As Long
                For j = 1 To maxDataPointsCount
                    outData(j, outputColumn) = inData(i, j)
                Next j
                outputColumn = outputColumn + 1
            End If
        Next i
    Next setNumber

    Dim outRange As Range
    Set outRange = Sheet2.Range("A1").Resize(UBound(outData, 1), UBound(outData, 2))
    outRange.Value = outData

End Sub

Private Function DetermineNumberOfSets(ByRef data As Variant) As Long
    '--- runs through the labels in the first column and separates
    '    the number value following the underscore to find the maximum
    '    count of data sets
    Dim max As Long
    Dim i As Long
    For i = LBound(data, 1) To UBound(data, 1)
        Dim tokens As Variant
        tokens = Split(data(i, 1), "_")
        If UBound(tokens) > 0 Then
            If max < tokens(1) Then max = tokens(1)
        End If
    Next i
    DetermineNumberOfSets = max
End Function

Private Function WhatsTheDataSet(ByVal label As String) As Long
    Dim tokens As Variant
    tokens = Split(label, "_")
    If UBound(tokens) > 0 Then
        WhatsTheDataSet = tokens(1)
    End If
End Function
0 голосов
/ 06 января 2020

Пожалуйста, попробуйте этот код. Он работает только в памяти и очень быстро. Я постараюсь объяснить для некоторых строк, которые могут показаться странными, что они делают. Он работает независимо от того, какой ("_") символ строк в столбце A: A. Это начальный лист («Области»): This is my starting sheet( И это лист результата («Окончательный»): enter image description here Sub testTransposeMyAreas () Dim sh As Рабочий лист, rngUR As Range, j As Long, ii As Long Dim rng As Range, usedR As Range, rA As Range, arVal как вариант Dim shFin As Worksheet, k As Long, i As Long, ArTr () как вариант

  Set sh = ActiveWorkbook.Worksheets("Areas") ' obviously the sheet keeping areas to be transposed
    If Sheets(Sheets.count).Name = "Final" Then
        Set shFin = ActiveWorkbook.Worksheets("Final")
        shFin.UsedRange.Clear' for testing reason only
    Else
        Set shFin = Sheets.Add(After:=Sheets(Sheets.count))
        shFin.Name = "Final"
    End If

  Set rngUR = sh.UsedRange

  'define all the (really used range) of the worksheet:
  Set usedR = sh.Range(rngUR.Cells(1, 1).Address & ":" & _
        rngUR.Cells(rngUR.Rows.count, rngUR.Columns.count).Address)

  'tricky way to separtate the areas...
  Set rng = usedR.SpecialCells(xlCellTypeConstants)

  k = 0      
  For Each rA In rng.Areas
    ReDim ArTr(rA.Columns.count) 'redim the array used to transfer data to the "Final" one
    arVal = rA.Value 'load the area range in ArVal array

     For i = 1 To rA.Rows.count
        For ii = 0 To rA.Columns.count - 1
            ArTr(ii) = arVal(i, ii + 1) 'fill the transfer array (diferently for each area row)
        Next ii
            'create the paste range and make the transfer:
            'to optimize the code, it does what you explain, but not in that suggested order
            'I mean, it firstly fill column B:B, then the column situated at how manu areas exists (once for each iteration)
            shFin.Range(Cells(5, 2 + k + j).Address & ":" & Cells(rA.Columns.count + 4, 2 + k + j).Address).Value = _
                                                    Application.WorksheetFunction.Transpose(ArTr) ': Stop
            k = i * rng.Areas.count ' used to define position of the next column to be filled
    Next i
    j = j + 1: k = 0
  Next
End Sub

Если что-то неясно, не стесняйтесь спрашивать разъяснения.

Этот код начинается с предположения, что все строки и номера столбцов в области равны.

В порядке чтобы лучше понять, как это работает, я бы предложил откомментировать команду Stop после ...Transpose(ArTr), уменьшить окно VBE и посмотреть, что происходит на рабочем листе, нажимая клавишу F5 после каждой остановки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...