Наибольшее заселённое измерение массива vba - PullRequest
4 голосов
/ 06 мая 2010

Скажем, у меня есть массив одного измерения (чтобы было проще). Есть ли простой способ узнать, какой самый высокий индекс элемента, которому было явно присвоено значение? кроме того, чтобы перебрать и считать? Я знаю, что Ubound находит самое высокое измерение массива, но это не то, что мне нужно. Есть ли что-то вроде Ubound, но в нем учитываются только заполненные элементы массива или самый высокий заполненный индекс?

Мой массив содержит строки. Кроме того, что если массив является многомерным.

Я работаю в Excel VBA.

Ответы [ 5 ]

4 голосов
/ 08 мая 2010

Одним словом, нет встроенного механизма для этого, но если вы ищете способ взломать его, тогда читайте дальше.

Это зависит от того, как вы заполняете массив. Если это последовательно, самым простым будет счетчик.

Если он рандомизирован, тогда вам будет сложнее найти точный индекс последнего элемента. Одна из идей - начать с максимума и начать обратный отсчет.

Если массив инициализирован, вы можете посчитать значения по умолчанию и вычесть из ubound.

Есть и другие идеи, но вам нужно более четко определить проблему, чтобы дать нам больше шансов ответить. Желательно с примером.

2 голосов
/ 15 мая 2018

Наивысший заполненный элемент массива

Вы можете шагать назад по циклу, пока не найдете первый элемент массива, который не пуст.
Это эквивалент «последнего» заполненного элемента.

Краткий ответ:

Function HighestPopIdx(ByRef arr) As Long
    For HighestPopIdx = UBound(arr) To LBound(arr) Step -1
        If Not IsEmpty(arr(HighestPopIdx)) Then Exit Function
    Next
End Function

Более длинный ответ:

Чтобы получить Наивысший заполненный элемент массива , я бы использовал цикл For..Next с IsEmpty функция.

(Этот пример немного сложнее, ради демонстрации.)

Sub demo() '(Hit CTRL+G in the VBE to view the output in the Immediate window.)

    'Make a test ARRAY of 5 elements:
    Dim arr(1 To 5)

    Erase arr '(just to make sure the test is starting 'fresh')

    arr(1) = "X"
    arr(2) = 99
    arr(4) = "Y" '3 of the 5 elements have data (highest one is 4)

    Dim elIdx As Long, highestPopEl As Long
    For elIdx = LBound(arr) To UBound(arr)
        If Not IsEmpty(arr(elIdx)) Then highestPopEl = elIdx
    Next elIdx

    Debug.Print "The highest populated element index is " & highestPopEl 'Returns 4

End Sub

... или наоборот (первый пример может быть быстрее в зависимости от вашей ситуации.


Другие примечания:

В дополнение к ответу @ jtolle , ни Count, ни CountA функции листа не будут работать:

    With Application.WorksheetFunction
        Debug.Print "Array WS CountA", .CountA(arr) 'returns 5 (Counts cells)
        Debug.Print "Array WS Count", .Count(arr) 'returns 1 (Counts numbers)
    End With

... и далее к комментариям, показывающим, что массивы и диапазоны ячеек работают аналогично, но не одинаково:

    'Make a test RANGE of 5 cells
    Dim rge As Range
    Set rge = Range("A1:A5")

    rge.Clear '(maker sure we're starting fresh)

    Range("A1") = "X"
    Range("A2") = 99
    Range("A4") = "Y" '3 of the 5 cells have data

    With Application.WorksheetFunction        
        Debug.Print "Range WS CountA", .CountA(rge) 'returns 3 (Counts values)
        Debug.Print "Range WS Count", .Count(rge)   'returns 1 (Counts numbers)
    End With

   '... and the VBA [Range.Count] method behaves differently:
    Debug.Print "Range VBA .Count", rge.Count 'returns 5 (Counts cells)

С другой стороны, если бы моей «реальной» целью было сделать что-то для каждого элемента в массиве, я бы использовал цикл For..Each с функцией IsEmpty.

    'if my actual purpose is to ***do something*** to an ARRAY...
    Debug.Print: Debug.Print "Array [For..Next] :"
    Dim elCnt As Long, el

    For Each el In arr
        If Not IsEmpty(el) Then _
            elCnt = elCnt + 1: Debug.Print "#" & elCnt & ": Element value =", el
    Next el    ' (returns "X", 99, "Y")

    '...and the identical method can be used on a RANGE of cells
    Debug.Print: Debug.Print "Range [For..Next] :"
    Dim clCnt As Long, cl

    For Each cl In rge
        If Not IsEmpty(cl) Then _
            clCnt = clCnt + 1: Debug.Print "#" & clCnt & ": Cell value =", cl
    Next cl    ' (returns "X", 99, "Y")


End Sub

Полный вывод:

The highest populated element index is 4.

Array WS CountA              5 
Array WS Count               1 
Range WS CountA              3 
Range WS Count               1 
Range VBA .Count             5 

Array [For..Next] :
#1: Element value =         X
#2: Element value =          99 
#3: Element value =         Y

Range [For..Next] :
#1: Cell value =            X
#2: Cell value =             99 
#3: Cell value =            Y

Подробнее:

2 голосов
/ 06 мая 2010

Я бы предложил использовать ReDim Preserve при заполнении массива. Таким образом, UBound будет работать очень хорошо для вас. Только последнее измерение в многомерном массиве может быть переопределено.

1 голос
/ 10 мая 2010

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

0 голосов
/ 10 мая 2010

Примечание от 2018 : это неправильно в Excel 365 и, вероятно, неправильно в Excel 2000. См. Комментарии. Но я оставляю это как пример того, как использование Application.WorksheetFunction() из VBA не всегда делает то же самое, что и использование формулы в рабочем листе.


Конечно, в цикле нет ничего плохого, но ...

Скажем, у вас есть это:

Public Sub test()
    Dim a(1 To 3)

    a(1) = 1
    a(2) = "stuff"

    Debug.Print Application.WorksheetFunction.CountA(a)
End Sub

Будет напечатано '2' [* Нет, оно печатает 3 - см. Комментарий ***], так как третий элемент имеет значение по умолчанию «Пусто» , (Обратите внимание, что для того, чтобы это работало, ваш массив должен быть объявлен как тип Variant, но он может содержать элементы типа String. Если ваш массив имеет тип String, то здесь нет пустот!) И, конечно, это предполагает, что вы Знайте, что все ваши пустые в конце, так как он просто считает, а не возвращает индекс. По той же причине вам нужно знать распределение ваших значений в двумерном массиве, чтобы это имело смысл.

Я думаю, это то, что вы ищете, основываясь на ваших изменениях.

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