Подтвердить что ты не робот

Обратный порядок для каждого цикла

Одна из самых сильных вещей в VB - это возможность циклического перемещения объектов в коллекции без ссылки на цикл index - for each.

Мне очень полезно только удалить объекты из коллекции.

При удалении объектов из предопределенного типа строк на листе распространения код проще, если я использую индексирование и начинаю с самого большого и вернусь к первому. (Шаг -1 с итератором) (в противном случае требуется смещение, так как для каждого движется указатель перечислителя назад к предыдущему объекту после удаления активного)

например.

For intA = 10 to 1 step -1 
    ' ...
Next

Как насчет использования для каждого | следующий например.

For each rngCell in Selection.Cells
    ' ...
Next

Как я могу выполнить цикл назад с помощью синтаксиса цикла for each?

4b9b3361

Ответ 1

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

В качестве альтернативы вы можете использовать цикл For i = a To 1 Step -1:

Sub reverseForEach()
    Dim i As Long, rng As Range

    Set rng = ActiveSheet.Range("A1:B2")

    For i = rng.Cells.Count To 1 Step -1

        Debug.Print rng.item(i).Address
        ' Or shorthand rng(i) as the Item property 
        ' is the default property for the Range object.
        ' Prints: $B$2, $A$2, $B$1, $A$1

    Next i

End Sub

Это работает со всеми коллекциями, которые имеют свойство Item. Например, рабочие листы, области или формы.

Примечание. Порядок цикла при использовании объекта Range - справа налево, затем вверх.

Ответ 2

Для встроенных коллекций (например, a Range) короткий ответ: вы не можете. Для пользовательских коллекций ответ, связанный с @VBlades, может оказаться полезным, хотя стоимость может перевесить выгоду.

Одна работа заключается в том, чтобы отделить идентификацию предметов, которые нужно удалить из фактического удаления. Например, для диапазона создайте новую переменную диапазона с помощью Union, затем обработайте эту переменную, например, удалите все строки за один раз. Для примера Range вы также можете использовать метод Variant Array для ускорения работы.

Независимо от того, будет ли это полезно, будет зависеть ваш фактический прецедент.

Ответ 3

Есть и другие хорошие ответы, но здесь есть другой альтернативный метод "отступить назад" через Range.


Функция для преобразования диапазона в массив

Эта функция возвращает "массив значений в обратном направлении", который можно использовать с For..Each:

Function ReverseRange(rg As Range) As Range()
    Dim arr() As Range, r As Long, c As Long, n As Long
    With rg
        ReDim arr(1 To .Cells.Count) 'resize Range Array
        For r = .Cells(.Rows.Count, 1).Row To .Cells(1, 1).Row Step -1
            For c = .Cells(1, .Columns.Count).Column To .Cells(1, 1).Column Step -1
                n = n + 1
                Set arr(n) = .Worksheet.Cells(r, c) 'set cell in Array
            Next c
        Next r
    End With
    ReverseRange = arr  'return Range Array as function result
End Function

Пример использования:

Sub test()
    Dim oCell
    For Each oCell In ReverseRange(ActiveSheet.Range("E5:A1"))

        Debug.Print oCell.Address 'do something here with each cell

    Next oCell
End Sub

Ответ 4

используйте вторую переменную, которая установлена в качестве желаемого счетчика, и используйте ее в своем коде

'ex: Loop from n = 19 to 16
For i = 0 To 3
   n = 19 - i
   'your code here using n as the counter
Next

Ответ 5

Только для коллекций Range. Они более сложны, если имеют более 1 зоны.

По сути, есть два цикла: первый хранит индекс всех ячеек в массиве, а второй создает объединение диапазонов от начала до конца

Option Explicit

Private Sub Main()
    Dim InvertedRange As Range
    Set InvertedRange = InvertRange(Application.Union(ActiveSheet.Range("A1:A2"), _
      ActiveSheet.Range("F6:F7"), ActiveSheet.Range("E4:F5"), ActiveSheet.Range("E1")))
    Dim ActualRange As Range
    For Each ActualRange In InvertedRange
        Debug.Print (ActualRange.Address(False, False) & " : " & ActualRange.Value)
    Next ActualRange
End Sub

Public Function InvertRange(ByVal rngRange_I As Range) As Range
    Dim RangesArray() As Long
    ReDim RangesArray(1 To rngRange_I.Count, 1 To rngRange_I.Count)
    Dim ActualArea As Range
    Dim ActualRange As Range
    Dim ArrayIndex As Long
    For Each ActualArea In rngRange_I.Areas
        For Each ActualRange In ActualArea
            ArrayIndex = ArrayIndex + 1
            RangesArray(ArrayIndex, 1) = ActualRange.Row
            RangesArray(ArrayIndex, 2) = ActualRange.Column
        Next ActualRange
    Next ActualArea

    Dim ActualRow As Long
    Dim ActualColumn As Long
    ActualRow = RangesArray(UBound(RangesArray, 1), 1)
    ActualColumn = RangesArray(UBound(RangesArray, 2), 2)
    With rngRange_I.Worksheet
        Dim InvertedRange As Range
        Set InvertedRange = .Cells(ActualRow, ActualColumn)
        For ArrayIndex = UBound(RangesArray, 1) To LBound(RangesArray, 1) Step -1
            ActualRow = RangesArray(ArrayIndex, 1)
            ActualColumn = RangesArray(ArrayIndex, 2)
            Set InvertedRange = Application.Union(InvertedRange, _
              .Cells(ActualRow, ActualColumn))
        Next ArrayIndex
    End With

    Set InvertRange = InvertedRange
End Function