Несмежный цикл For Each для строки вместо столбца - PullRequest
0 голосов
/ 16 января 2019

У меня есть несмежное выделение, охватывающее строки и столбцы, и я хочу создать для него цикл For Each. Excel VBA делает это, зацикливая сначала столбец 1, затем 2,3 и т. Д .; но вместо этого я хочу, чтобы он сначала зацикливался на ряду.

(Мой лист выглядит примерно так, как показано на рисунке ниже, мне нужно по очереди выбрать каждый столбец (версию) и получить номер документа и другую информацию. Количество строк и столбцов версии в листе не исправлено).

Если не считать написания довольно большой функции Sort и создания массива ссылок, мне было интересно, есть ли «встроенный» способ сделать это?

Мне не нужен код, просто объяснение.

Non-contiguous selection

Ответы [ 3 ]

0 голосов
/ 16 января 2019

несмежная

Зацикливая сначала строки ( i ), вы получите последовательность By Row ', например. A1, B1, C1, ...

Код

Sub NonContiguous()

    Dim i As Long
    Dim j As Long
    Dim k As Long

    With Selection
        For k = 1 To .Areas.Count
            With .Areas(k)
                For i = .Row To .Rows.Count + .Row - 1
                     For j = .Column To .Columns.Count + .Column - 1

                         Debug.Print .Parent.Cells(i, j).Address & " = " _
                                 & .Parent.Cells(i, j)

                     Next
                Next
            End With
        Next
    End With

End Sub
0 голосов
/ 16 января 2019

Это основано на предложении urdearboy:

1. цикл по столбцам
2. внутри столбца обведите ячейки

Sub disjoint()
    Dim r As Range, rInt As Range
    Dim nLastColumn As Long
    Dim nFirstColumn As Long, msg As String
    Dim N As Long

    Set r = Range("C3,C9,E6,E13,E15,G1,G2,G3,G4")

    nFirstColumn = Columns.Count
    nLastColumn = 0
    msg = ""

    For Each rr In r
        N = rr.Column
        If N < nFirstColumn Then nFirstColumn = N
        If N > nLastColumn Then nLastColumn = N
    Next rr

    For N = nFirstColumn To nLastColumn
        Set rInt = Intersect(Columns(N), r)
        If rInt Is Nothing Then
        Else
            For Each rr In rInt
                msg = msg & vbCrLf & rr.Address(0, 0)
            Next rr
        End If
    Next N
    MsgBox msg
End Sub

enter image description here

0 голосов
/ 16 января 2019

Порядок, в котором For Each выполняет итерацию коллекции объектов, зависит от реализации (я обвиняю Excel, а не VBA), и, хотя он, вероятно, является детерминированным и предсказуемым, в его спецификации нет ничего, что гарантировало бы конкретный порядок итераций. Поэтому код VBA, написанный для итерации коллекции объектов, не должен быть написан с предположением о конкретном порядке итерации, поскольку это может очень хорошо меняться в зависимости от версии используемой библиотеки типов (в данном случае Excel).

Очень неясно, какова форма вашего Range / Selection, но если вам нужно перебрать выбранные ячейки в определенном порядке, то цикл For Each не следует использовать, по крайней мере, не для перебора. клетки как таковые.

Так как диапазоны не являются смежными, Range будет иметь кратное Areas; вам нужно будет итерировать Selection.Areas и для каждой выбранной области, итерировать ячейки в определенном порядке. For Each, безусловно, является наиболее эффективным способом итерации коллекции объектов , которой является Range.Areas.

Debug.Assert TypeOf Selection Is Excel.Range

Dim currentArea As Range
For Each currentArea In Selection.Areas
    'todo
Next

Вместо того, чтобы вкладывать циклы , создайте отдельную процедуру, которая принимает currentArea в качестве параметра - в этой процедуре вы будете повторять отдельные ячейки:

Private Sub ProcessContiguousArea(ByVal area As Range)
    Dim currentRow As Long
    For currentRow = 1 To area.Rows.Count
        Debug.Print area.Cells(currentRow, 1).Address
    Next
End Sub

Теперь внешний цикл выглядит так:

Debug.Assert TypeOf Selection Is Excel.Range

Dim currentArea As Range
For Each currentArea In Selection.Areas
    ProcessContiguousArea currentArea
Next

Процедура ProcessContiguousArea может делать все, что нужно с заданной смежной областью, используя цикл For для итерации диапазона по строкам, не заботясь о фактическом адресе выбранной области: используя Range.Cells(RowIndex, ColumnIndex), строка 1 / столбец 1 представляет верхнюю левую ячейку этого диапазона, независимо от того, где этот диапазон расположен на листе.

Невыбранные ячейки могут быть доступны с помощью Range.Offset:

        Debug.Print area.Cells(currentRow, 1).Offset(ColumnOffset:=10).Address

Строка верхней левой ячейки area на рабочем листе возвращается area.Row, а столбец верхней левой ячейки area на рабочем листе получается с area.Column.

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