Обратный порядок для каждого цикла
Одна из самых сильных вещей в VB - это возможность циклического перемещения объектов в коллекции без ссылки на цикл index - for each
.
Мне очень полезно только удалить объекты из коллекции.
При удалении объектов из предопределенного типа строк на листе распространения код проще, если я использую индексирование и начинаю с самого большого и вернусь к первому. (Шаг -1 с итератором) (в противном случае требуется смещение, так как для каждого движется указатель перечислителя назад к предыдущему объекту после удаления активного)
например.
For intA = 10 to 1 step -1
' ...
Next
Как насчет использования для каждого | следующий
например.
For each rngCell in Selection.Cells
' ...
Next
Как я могу выполнить цикл назад с помощью синтаксиса цикла for each
?
Ответы
Ответ 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