2017-01-16 12 views
0

Я читал соответствующие темы в течение нескольких часов, но не могу найти решение. Ваша помощь будет очень признательна, спасибо.Excel vba: .find функция возвращает ошибку времени выполнения 91

Я пытаюсь найти максимальное значение в диапазоне, а затем найти, в какую строку он был. Этот код отлично подходит для моих первых 600 + строк чисел, затем сбой и дает мне Runtime Error 91. Кажется, что он сбой в том же месте, независимо от того, что я делаю.

Dim rSearchRange As Range 
Dim dMaxToFind As Double 
Dim rSolutionrange As Range 

Set rSearchRange = Sheets("MySheet").Range(Cells(672, 1), Cells(681, 1)) 

With Application.WorksheetFunction 
     dMaxToFind = .Max(rSearchRange) 
End With 

'This bit here returns "nothing" even though i found the max value in this range 
Set rSolutionrange = rSearchRange _ 
      .Find(What:=dMaxToFind, _ 
      LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, _ 
      SearchDirection:=xlNext, MatchCase:=False) 

Рассматривая мои данные, похоже, что они меняют формат в этих строках?

Row 670 - 0.000458587 
    Row 671 - 0.000458587 
    Row 672 - 9.80465E-05 
    Row 673 - 9.80465E-05 

Редактировать:

dMaxToFind возвращает 9.80465352566588E-05

клеток формула возвращает 0.0000980465352566588

это то же самое значение, в клетках между Range (Cells (672, 1), клетки (681, 1)

похоже, что vba не понимает, что эти два одинаковы?

Спасибо,

Все

+3

Try '... Lookin: = xlValues ​​...' ' – user3598756

+1

.Find' возвратит' Nothing' если нет матча критериев поиска.Ошибка 91 является * поднятой * (не «возвращена»), когда вы * позже * используете эту переменную объекта 'rSolutionRange' и предполагаете, что она установлена ​​в действительную ссылку (' Nothing' не является допустимой ссылкой на объект) - вам нужно проверить ' Если rSolutionRange Is Nothing', прежде чем использовать его. –

+1

Также 'Таблицы (« MySheet »). Диапазон (ячейки (672, 1), ячейки (681, 1))' взорвется, если 'Sheets (« MySheet »)' не является активным листом, потому что 'Cells (xxx, yyy) 'вызовы неявно ссылаются на' ActiveSheet', поэтому вам также нужно квалифицировать эти вызовы; используйте 'With Worksheets (« MySheet »)' then do 'Set rSearchRange = .Range (.Cells (672, 1), .Cells (681,1))' внутри блока 'With' (обратите внимание на точки). –

ответ

0

Вот функция, которая будет напрямую сказать вам номер строки. Это противоречит вашему методу использования встроенных функций, чтобы получить максимальное значение, а затем снова найти его!

Function GetMaxRow(ByRef myRange As Range) As Long 

    Dim aCell As Range 

    Dim maxVal As Double 
    Dim maxRow As Long 

    maxVal = Application.WorksheetFunction.Min(myRange) 
    maxRow = 0 

    For Each aCell In myRange 

     If aCell.Value > maxVal Then 

      maxRow = aCell.row 
      maxVal = aCell.Value 

     End If 

    Next aCell 

    GetMaxRow = maxRow 

End Function 

Вы можете добавить некоторые проверки ошибок кода в результате этой функции, например, когда функция возвращает 0 это будет означать, что вы не передать его диапазон с более чем 1 клетку.

Пример:

1 - 3.0 
2 - 5.2 
3 - 7.8 
4 - 2.2 
5 - 4.5 
6 - 3.6 

Dim rw as Long 
rw = GetMaxRow(ActiveSheet.Range("A1:A6")) 
' >> rw = 3 

Edit:

Вы опрошены использование петель, предположительно, из-за экономии времени. Ниже приведена таблица таймингов, конечно, это будет варьироваться между компьютерами, но вы можете увидеть, что это довольно быстрая процедура, если ваш набор данных не массивный ... Также порядок операции линейный, как и ожидалось, что означает удвоение диапазона примерно в два раза время.

2000 rows: 0.0050000s 
4000 rows: 0.0099219s 
8000 rows: 0.0196875s 
16000 rows: 0.0392969s 

Значения ячеек испытанные над случайными были двойники между 0 и 1.

Edit 2:

Вы достигли максимума мой интерес на скорости ... Так что вместо зацикливания в диапазоне (медленный) Я написал ту же функцию, но использовал массив. Диапазон преобразуется в массив, а затем зацикливается. Это улучшает скорость ~ 10x! См. Ниже код и тайминги по тем же данным.

Function GetMaxRow(ByRef myRange As Range) As Long 

    Dim i As Long 

    Dim maxVal As Variant 
    Dim maxRow As Long 

    maxVal = Application.WorksheetFunction.Min(myRange) 
    maxRow = 0 

    Dim myArray() As Variant 
    myArray = myRange 

    For i = 1 To myRange.Count 

     If myArray(i, 1) > maxVal Then 

      maxRow = i 
      maxVal = myArray(i, 1) 

     End If 

    Next i 

    GetMaxRow = myRange.Cells(maxRow).Row 

End Function 

Тайминги:

2000 rows: 0.0006250s 
4000 rows: 0.0018750s 
8000 rows: 0.0034375s 
16000 rows: 0.0068750s 
+0

Это выглядит хорошо - есть ли способ сделать то же самое без цикла? – Mark

+0

Спасибо, вы спрашиваете из-за таймингов? См. Мое редактирование ... – Wolfie

+0

@Mark, я добавил гораздо более быструю версию, см. Редактировать 2 – Wolfie