Excel vba удалить массив строк

 

Oleg

Пользователь

Сообщений: 112
Регистрация: 16.01.2013

Добрый день, коллеги,
Подскажите, пожалуйста, эффективный код на удаление строки из двумерного массива (последующие строки «смещаются» вверх на 1).
На сколько я знаю, redimpreserve можно использовать только не выше двумерного массива?

 

LVL

Пользователь

Сообщений: 903
Регистрация: 01.01.1970

#2

03.07.2014 10:03:11

Цитата
Oleg пишет:
На сколько я знаю, redimpreserve можно использовать только не выше двумерного массива?

Не верное утверждение. Redim не удаляет элементы, он изменяет размер массива, при чем только по последней(!) размерности. Вообще именно удалить «строку» массива будет проблематично (если вообще возможно), но проблему можно решить по другому. Допустим так — скопировать в другой массив только нужные строки.

Код
Sub DeletingEmptyValuesFromArray(ByRef vaBasic() As Variant, Optional ByVal ColumnNum As Long = 1)
'Удаление пустых строк в массиве
'Передаваемый массив должен быть динамическим, двумерным
'ColumnNum - номер столбца по которому проверяется пустое значение

Dim i As Long, j As Long, n As Long
Dim vaTemp() As Variant
'Копируем массив во временную переменную
vaTemp = vaBasic
'Считаем количество непустых строк в массиве
n = 0
For i = 1 To UBound(vaTemp, 1)
    If Not IsEmpty(vaTemp(i, ColumnNum)) Then n = n + 1
Next

If n = 0 Then Erase vaBasic: Exit Sub

'Определяем новый размер массива
ReDim vaBasic(n, UBound(vaTemp, 2))
'Заполняем массив непустыми строками
n = 0
For i = 1 To UBound(vaTemp, 1)
    If Not IsEmpty(vaTemp(i, ColumnNum)) Then
        n = n + 1
        For j = 1 To UBound(vaTemp, 2)
            vaBasic(n, j) = vaTemp(i, j)
        Next
    End If
Next
End Sub

Изменено: LVL03.07.2014 10:05:01

 

Oleg

Пользователь

Сообщений: 112
Регистрация: 16.01.2013

LVL, спасибо, в коде разобрался. Единственное что озадачило — я думал в массивах с 0, а не с 1 элемента идет счет если задавать так redim (5), а не так redim (1 to 5).
А не быстрее будет перезаписать строку i (удаляемую) строкой i+1, а следующую i+1 заменить i+2 и до конца. Потом «обрезать» массив на 1?
Хотя, наверное, при большом кол-ве удаляемых строк, это будет хуже работать. Изначально я все эти процедуры выполнял с данными листа, но очень уж медленно выходит.

 

JeyCi

Пользователь

Сообщений: 3357
Регистрация: 27.11.2013

#4

04.07.2014 11:13:34

Цитата
Oleg пишет: А не быстрее будет перезаписать строку … . Потом «обрезать» массив на 1?

создать пустой массив и в него перекинуть нужное — всегда быстрее чем перезаписывать все строки в нём со смещением…
его потом обрезать, думаю, не представляется возможным… сначала — обрезать (что надо) т е ReDim задать новую размерность — потом перекидывать…
имхо (насколько мне позволили мои знания)… или знающие поправят

Изменено: JeyCi04.07.2014 11:13:44

чтобы не гадать на кофейной гуще, кто вам отвечает и после этого не совершать кучу ошибок — обратитесь к собеседнику на ВЫ — ответ на ваш вопрос получите — а остальное вас не касается (п.п.п. на форумах)

 

LVL

Пользователь

Сообщений: 903
Регистрация: 01.01.1970

#5

04.07.2014 11:30:15

Цитата
Oleg пишет: я думал в массивах с 0, а не с 1 элемента идет счет

По умолчанию да, но в зависимости от ситуации можно считать и от одного, в самом начале модуля пишите Option Base 1 и отсчёт будет начинаться с 1. Соответственно это справедливо при инициализации массива, если явно не указаны его границы.

 

Мотя

Пользователь

Сообщений: 3218
Регистрация: 25.12.2012

Навеяло…
Как математик и инженер решают одну и ту же задачу: вытащить из доски наполовину забитый гвоздь…
Инженер вытаскивает гвоздь.
Математик забивает его до конца и затем решает задачу в общем случае.

Изменено: Мотя04.07.2014 11:41:50

 

Максим Зеленский

Пользователь

Сообщений: 4646
Регистрация: 11.06.2014

Microsoft MVP

#7

04.07.2014 11:42:24

Цитата
Oleg пишет: Потом «обрезать» массив на 1?

Читайте внимательно. Строки двумерного массива — это первая размерность, столбцы — вторая. Вы можете изменять только вторую размерность, т.е. столбцы. Можете извратиться и транспонировать массив, если это уже так необходимо, тогда вторым измерением станут строки

F1 творит чудеса

 

Hugo

Пользователь

Сообщений: 23249
Регистрация: 22.12.2012

Вот сколько пишу макросы — никогда не нужно было удалять строку из двумерного массива… Зачем?
Всегда можно сделать как-то иначе. Например запомнить в коллекции/словаре номера нужных (или ненужных) строк, затем в финале переложить в новый массив только нужные строки.
Это если это удаление Вы хотите делать неоднократно в цикле. Если один раз (т.е. при одном анализе массива) — то просто перекладывайте нужные строки в аналогичный по размеру пустой массив, хвост которого откинете при выгрузке на лист.
Ну конечно если из миллиона нужно отобрать 100 — то это расточительно, тогда используйте коллекцию номеров :)

Изменено: Hugo04.07.2014 11:52:09

 

Skif-F

Пользователь

Сообщений: 181
Регистрация: 26.12.2012

#9

04.07.2014 12:41:15

Цитата
Максим Зеленский пишет: Строки двумерного массива — это первая размерность, столбцы — вторая. Вы можете изменять только вторую размерность, т.е. столбцы. Можете извратиться и транспонировать массив, если это уже так необходимо, тогда вторым измерением станут строки

А где написано, что первое измерение — это строки, а второе — столбцы? Тут каждый решает сам. В крайнем случае, можно сделать как-то так:

Код
Dim ArrBase(5) As Variant, ArrInSide(4) As String
ArrBase(1) = ArrInSide   'Заносим "строку" в массив
... 'ещё разный код по заполнению строк
For Row = 0 To UBound(ArrBase) 'Перебор строк
    For col = 0 To UBound(ArrBase(Row)) 'Перебор столбцов
          a = ArrBase(Row)(col) 'Получаем значение из ячейки строки
    Next col
Next Row

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

Изменено: Skif-F04.07.2014 12:49:15

 

Максим Зеленский

Пользователь

Сообщений: 4646
Регистрация: 11.06.2014

Microsoft MVP

#10

04.07.2014 17:02:50

Цитата
Skif-F пишет: А где написано, что первое измерение — это строки, а второе — столбцы?

да нет, конечно нигде, но, если не ошибаюсь, по умолчанию, при загоне данных с листа, создается двумерный массив, где первое измерение — строки, а второе — столбцы.
ArrBase(1) = ArrInSide ‘Заносим «строку» в массив — даже не подозревал, что так можно… пошел думать

F1 творит чудеса

  • Remove From My Forums
  • Question

  • Does anyone know how to delete a row r in an nxm array in VBA (not an EXCEL array)? I want to expunge a bad record if one column is bad.

Answers

  • We can use the Excel array to help us remove a row in the VBA array.  Say  the VBA arrray is 50 down by 100 across and we want to remove row #7:

    Sub RowKiller()
    Dim StartArray(1 To 50, 1 To 100) As Long
    Dim EndArray

    k = 1
    For i = 1 To 50
        For j = 1 To 100
            StartArray(i, j) = k
            k = k + 1
        Next
    Next

    Range(«A1:CV50») = StartArray
    Range(«A7»).EntireRow.Delete
    EndArray = Range(«A1:CV49»)

    End Sub

    The last three lines do all the work.


    gsnu201111

    • Edited by

      Saturday, November 5, 2011 8:41 PM
      typo

    • Marked as answer by
      danishani
      Sunday, January 29, 2012 8:42 PM

  • Thanks Gary, but unfortunately I have to use a Function, not a Sub. My Row and Col indicies are integers. The reason that I can’t use a Sub is that I have to pass arguments to the procedure.  this is not conenient with a Sub.  I pass,
    among other things, the EXCEL array and make a copy of it in the vba, and then work on that.

    I guess this is a new discussion related to the other one where you wanted a function to change the worksheet environment. ;-)

    If your data are already in Excel, there’s probably no need to do this work in VBA.  Use Excel’s built-in features: Filtering / Advanced Filtering / PivotTables / Data Query / formulas.

    Excel already has a host of features.  Leverage them and let Excel do the heavy lifting.


    Tushar Mehta (Technology and Operations Consulting)
    www.tushar-mehta.com (Excel and PowerPoint add-ins and tutorials)
    Microsoft MVP Excel 2000-Present

    • Marked as answer by
      danishani
      Sunday, January 29, 2012 8:42 PM

First, let me say categorically that there is nothing wrong with loops — they certainly have their place!

Recently we were presented with the below situation:

400000  |  Smith, John| 2.4   | 5.66|   =C1+D1
400001  |  Q, Suzy    | 4.6   | 5.47|   =C2+D2
400002  |  Schmoe, Joe| 3.8   | 0.14|   =C3+D3
Blank   |             |       |     |   #VALUE
Blank   |             |       |     |   #VALUE

The OP wanted to delete rows where Column A is blank, but there is a value in Column E.

I suggest that this is an example where we could make use of SpecialCells and a temporary Error Column to identify the rows to be deleted.

Consider that you might add a column H to try and identify those rows; in that row you could use a formula like below:

=IF(AND(A:A="",E:E<>""),"DELETE THIS ROW","LEAVE THIS ROW")

now, it is possible get that formula to put an error in the rows where I test returns True. The reason we would do this is a feature of Excel called SpecialCells.

In Excel select any empty cell, and in the formula bar type

=NA()

Next, hit F5 or CTRL+G (Go to… on the Edit menu) then click the Special button to show the SpecialCells dialog.

In that dialog, click the radio next to ‘Formulas’ and underneath, clear the checkboxes so that only Errors is selected. Now click OK

Excel should have selected all the cells in the worksheet with an Error (#N/A) in them.

The code below takes advantage of this trick by creating a formula in column H that will put an #N/A in all the rows you want to delete, then calling SpecialCells to find the rows, and clear (delete) them…

    Sub clearCells()
    '
    Dim sFormula As String
    '
    ' this formula put's an error if the test returns true, 
    ' so we can use SpecialCells function to highlight the
    ' rows to be deleted!

Create a formula that will return #NA when the formula returns TRUE

sFormula = "=IF(AND(A:A="""",E:E<>""""),NA(),"""")"

Put that formula in Column H, to find the rows that are to be deleted…

Range("H5:H" & Range("E65536").End(xlUp).Row).Formula = sFormula

Now use SpecialCells to highlight the rows to be deleted:

Range("H5:H" & Range("E65536").End(xlUp).Row).SpecialCells(xlCellTypeFormulas, xlErrors).entirerow.select

This line of code would highlight just Column A by using OFFSET in case instead of deleting the entire row, you wanted to put some text in, or clear it

Range("H5:H" & Range("E65536").End(xlUp).Row).SpecialCells(xlCellTypeFormulas, xlErrors).Offset(0, -7).select

and the below line of code will delete thhe entire row because we can :)

Range("H5:H" & Range("E65536").End(xlUp).Row).SpecialCells(xlCellTypeFormulas, xlErrors).EntireRow.Delete shift:=xlup

' clean up the formula
Range("H5:H" & Range("E65536").End(xlUp).Row).Clear
'
End Sub

BTW, it’s also possible WITH A LOOP if you really want one :)

One more thing, before Excel 2010 there was a limit of 8192 rows (I think because this feature went all the way back to 8-bit versions of Excel maybe)

The VBA legend Ron de Bruin (on whose website I first picked up this technique, among others) has something to say about this

Philip

In this Article

  • Delete Entire Row or Column
    • Delete Multiple Rows or Columns
  • Delete Blank / Empty Rows
    • Delete Row if Cell is Blank
  • Delete Row Based on Cell Value
  • More Delete Row and Column Examples
    • Delete Duplicate Rows
    • Delete Table Rows
    • Delete Filtered Rows
    • Delete Rows in Range
    • Delete Selected Rows
    • Delete Last Row
    • Delete Columns by Number

This tutorial will demonstrate different ways to delete rows and columns in Excel using VBA.

Delete Entire Row or Column

To delete an entire row in VBA use this line of code:

Rows(1).Delete

Notice we use the Delete method to delete a row.

Instead of referencing the Rows Object, you can reference rows based on their Range Object with EntireRow:

Range("a1").EntireRow.Delete

Similarly to delete an entire column, use these lines of code:

Columns(1).Delete
Range("a1").EntireColumn.Delete

Delete Multiple Rows or Columns

Using the same logic, you can also delete multiple rows at once:

Rows("1:3").Delete

or columns:

Columns("A:C").Delete

Notice here we reference the specific row and column numbers / letters surrounded by quotations.

Of course, you can also reference the EntireRow of a range:

Range("a1:a10").EntireRow.Delete

Note: The examples below only demonstrate deleting rows, however as you can see above, the syntax is virtually identically to delete columns.

Delete Blank / Empty Rows

This example will delete a row if the entire row is blank:

Sub DeleteRows_EntireRowBlank()

Dim cell As Range

For Each cell In Range("b2:b20")
    If Application.WorksheetFunction.CountA(cell.EntireRow) = 0 Then
        cell.EntireRow.Delete
    End If
Next cell

End Sub

It makes use of the Excel worksheet function: COUNTA.

Delete Row if Cell is Blank

This will delete a row if specific column in that row is blank (in this case column B):

Range("b3:b20").SpecialCells(xlCellTypeBlanks).EntireRow.Delete

Delete Row Based on Cell Value

This will loop through a range, and delete rows if a certain cell value in that row says “delete”.

Sub DeleteRowswithSpecificValue()

Dim cell As Range

For Each cell In Range("b2:b20")
    If cell.Value = "delete" Then
        cell.EntireRow.Delete
    End If
Next cell

End Sub

More Delete Row and Column Examples

VBA Coding Made Easy

Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!

automacro

Learn More

Delete Duplicate Rows

This code will delete all duplicate rows in a range:

Range("b2:c100").RemoveDuplicates Columns:=2

Notice we set Columns:=2. This tells VBA to check both the first two columns of data when considering if rows are duplicates. A duplicate is only found when both columns have duplicate values.

If we had set this to 1, only the first row would’ve been checked for duplicate values.

Delete Table Rows

This code will delete the second row in a Table by referencing ListObjects.

ThisWorkbook.Sheets("Sheet1").ListObjects("list1").ListRows(2).Delete

Delete Filtered Rows

To delete only rows that are visible after filtering:

Range("b3:b20").SpecialCells(xlCellTypeVisible).EntireRow.Delete

VBA Programming | Code Generator does work for you!

Delete Rows in Range

This code will delete all rows in range:

Range("a1:a10").EntireRow.Delete

Delete Selected Rows

This code will delete all selected rows:

Selection.EntireRow.Delete

Delete Last Row

This will delete the last used row in column B:

Cells(Rows.Count, 2).End(xlUp).EntireRow.Delete

By changing 2 to 1, you can delete the last used row in column A, etc.:

Cells(Rows.Count, 1).End(xlUp).EntireRow.Delete

Delete Columns by Number

To delete a column by it’s number, use a code like this:

Columns (2).Delete

Удаление «пустых строк» из массива

Удаление «пустых строк» из диапазона ячеек при помощи макроса

Function DeleteBlankRows(ByVal arr As Variant, ByVal col As Long) As Variant
    ' осуществляет удаление пустых строк из массива
    ' получает в качестве параметров исходный массив, и номер столбца,
    ' по которому определяется, является ли строка постой
    ' возвращает новый массив (с меньшей размерностью по вертикали)
    If Not IsArray(arr) Then MsgBox "Это не массив!", vbCritical: Exit Function
    If col > UBound(arr, 2) Then MsgBox "Нет такого столбца в массиве!", vbCritical: Exit Function
    If col < LBound(arr, 2) Then MsgBox "Нет такого столбца в массиве!", vbCritical: Exit Function
 
    Dim iCount As Long    ' кол-во непустых строк
    For i = LBound(arr) To UBound(arr)
        iCount = iCount - (arr(i, col) <> "")
    Next i
 
    ReDim narr(LBound(arr, 1) To iCount + LBound(arr, 1) - 1, LBound(arr, 2) To UBound(arr, 2))
 
    iCount = LBound(narr)    ' счётчик записей
    For i = LBound(arr, 1) To UBound(arr, 1)
        If arr(i, col) <> "" Then
            For j = LBound(arr, 2) To UBound(arr, 2)
                narr(iCount, j) = arr(i, j)
            Next j
            iCount = iCount + 1
        End If
    Next i
 
    DeleteBlankRows = narr
End Function
Sub ПримерИспользования()
    On Error Resume Next
    arr = [a1:d15] ' считываем значения ячеек диапазона [a1:d15] в массив arr
    
    ' получаем массив arr2, в 5-м столбце которого нет пустых значений
    arr2 = DeleteBlankRows(arr, 5)
 
    [f1:z111].Clear ' очищаем диапазон ячеек [f1:z111] на листе
    
    ' вставляем массив без пустых строк обратно на лист
    [f1].Resize(UBound(arr2, 1), UBound(arr2, 2)).Value = arr2
End Sub
  • 30174 просмотра

Не получается применить макрос? Не удаётся изменить код под свои нужды?

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

i fell into a case where clearing the entire array failed with dim/redim :

having 2 module-wide arrays, Private inside a userform,

One array is dynamic and uses a class module, the other is fixed and has a special type.

Option Explicit

Private Type Perso_Type
   Nom As String
   PV As Single 'Long 'max 1
   Mana As Single 'Long
   Classe1 As String
   XP1 As Single
   Classe2 As String
   XP2 As Single
   Classe3 As String
   XP3 As Single
   Classe4 As String
   XP4 As Single
   Buff(1 To 10) As IPicture 'Disp
   BuffType(1 To 10) As String
   Dances(1 To 10) As IPicture 'Disp
   DancesType(1 To 10) As String
End Type

Private Data_Perso(1 To 9, 1 To 8) As Perso_Type

Dim ImgArray() As New ClsImage 'ClsImage is a Class module

And i have a sub declared as public to clear those arrays (and associated run-time created controls) from inside and outside the userform like this :

Public Sub EraseControlsCreatedAtRunTime()
Dim i As Long
On Error Resume Next
With Me.Controls 'removing all on run-time created controls of the Userform :
    For i = .Count - 1 To 0 Step -1 
        .Remove i
    Next i
End With
Err.Clear: On Error GoTo 0

Erase ImgArray, Data_Perso
'ReDim ImgArray() As ClsImage ' i tried this, no error but wouldn't work correctly
'ReDim Data_Perso(1 To 9, 1 To 8) As Perso_Type 'without the erase not working, with erase this line is not needed.
End Sub

note : this last sub was first called from outside (other form and class module) with Call FormName.SubName but had to replace it with Application.Run FormName.SubName , less errors, don’t ask why…

Понравилась статья? Поделить с друзьями:
  • Excel vba удалить листы кроме активного
  • Excel vba удалить имя диапазона
  • Excel vba удалить значения в ячейках
  • Excel vba удалить дубликаты в столбце
  • Excel vba удалить диапазон столбцов