0

Consider Below Excel:

Id    Col1     Col2     Col3   Col4
25     s        p         n    
11     a        t         x     g
17                        r     t
10     a                  a     e
66     a                  a

Suppose I have an array which contains the Id numbers
e.g. Arr=(25,11,66)

Is it possible to delete all the rows where the Id number is in that array at once?

Do I need to select them first?

CODE:

 Option Explicit

 Dim arr,objExcel1,strPathExcel1,objSheet1


 Set objExcel1 = CreateObject("Excel.Application")'Object for Condition Dump
 strPathExcel1 = "D:\VA\Test.xlsx"
 objExcel1.Workbooks.open strPathExcel1

 Set objSheet1 = objExcel1.ActiveWorkbook.Worksheets(4)


 arr = Array(5,11,66)

 objSheet1.Range("A" & Join(arr, ",A")).EntireRow.Delete

ERROR "Unknown Runtime Error" -- I am getting

Community
  • 1
  • 1
arun_roy
  • 601
  • 6
  • 16
  • 42

3 Answers3

2

EDIT: Further optimize the code

EDIT: Using dictionary to avoid nested loop to increase performance

Please also note that since the tag is vba & vbs, so the answer presented here is compatible in both.

And this solution is deleting the whole row instead of the range containing data only.

EDIT: Updated code to match column A's value with the value inside Arr

Assume that the values from Row 2 onward are Numeric

You can use record macro function provided by Excel to observe how the Range Object is like

http://spreadsheets.about.com/od/advancedexcel/ss/080703macro2007.htm

Sub t()
    Dim str
    Dim arr
    Dim i
    arr = Array(1, 2, 4)
    Dim row
    Dim height
    Dim found
    Dim dataArray
    Dim d
    height = Cells(Rows.Count, 1).End(-4162).row
    ReDim dataArray(height - 2, 0) ' -1 for 0 index, -1 for the first row as header row, excluded
    str = ""
    dataArray = Range(Cells(2, 1), Cells(height, 1)).Value
    Set d = CreateObject("scripting.dictionary")
    For i = LBound(arr) To UBound(arr)
        If Not d.exists(arr(i)) Then
            d(arr(i)) =  0
        End If
    Next
    For i = LBound(dataArray, 1) To UBound(dataArray, 1)
        If d.exists(dataArray(i, 1)) Then
            'found in column 1
            str = str & i & ":" & i & ","
        Else
            'found = False
        End If
    Next
    If Len(str) > 0 Then
        str = Mid(str, 1, Len(str) - 1)
        Range(str).Delete

    End If

End Sub
Larry
  • 2,764
  • 2
  • 25
  • 36
2

Here is a brute force method. UPDATED AS PER OP'S LATTER COMMENTS

Code:

Option Explicit


Sub overWriteRows()
Dim d As Object
Dim wkSheet As Worksheet
Dim myRange As Range
Dim myArray As Variant
Dim deleteArray As Variant
Dim finalArray As Variant
Dim upBound As Long, i As Integer
Dim j As Integer, k As Integer, m As Integer

Set d = CreateObject("scripting.dictionary")
Set wkSheet = Sheets("Sheet1") '-- set your own sheet e.g. Sheet2
Set myRange = wkSheet.Range("B3:F8") '-- set your own range e.g. "B2:E5"

'-- validate if range is null or not
If myRnage is nothing then
  Exit Sub
End if

myArray = Application.WorksheetFunction.Transpose(myRange)
'-- now if you do not have delete range in a sheet range then
    '-- you may populate the dictionary right away manually so
        '-- you do not need deleteArray
deleteArray = Application.WorksheetFunction.Transpose(Range("G3:I3"))
'-- if you are populating dictionary manually then
    '-- you may set upBound = Ubound(myArray,2) - d.Count
upBound = UBound(myArray, 2) - UBound(deleteArray)
ReDim finalArray(LBound(myArray, 2) To upBound, LBound(myArray) To UBound(myArray))

'-- replace this with your manual dictionary population code
For i = LBound(deleteArray) To UBound(deleteArray)
    If Not d.exists(deleteArray(i, 1)) Then
        d.Add deleteArray(i, 1), i
    End If
Next i

    k = 1

    For j = LBound(myArray, 2) To UBound(myArray, 2)
        If Not d.exists(myArray(1, j)) Then
        '-- if you want to remove even duplicate records then u can use this
           'd.Add myArray(1, j), k 
                For m = LBound(myArray) To UBound(myArray)
                    finalArray(k, m) = myArray(m, j)
                Next m
                k = k + 1
        End If
    Next j

'-- you may use following code to flush old row data
    'myRange.Value = ""
'-- output the new array to sheet by over writing the old range
'-- you may use myRange instead of "B11" to overwrite old data with filtered data
    wkSheet.Range("B11").Resize(UBound(finalArray), _ 
         UBound(Application.Transpose(finalArray))) = finalArray

    Set d = Nothing
End Sub

Output

enter image description here

bonCodigo
  • 14,268
  • 1
  • 48
  • 91
  • @Tukai Rakshit here is code that will avoid nested loops with a dictionary ;) – bonCodigo Dec 18 '12 at 11:51
  • you are superbb!! Want you to be my VBS private tutor!! :-). Thanks.. Thanks to Stackoverflow also to bring BonCodigo here, Happy to Learn from you people. – arun_roy Dec 18 '12 at 11:53
  • @bonCodigo +1 for using the dictionary - so it's not entirely 'brute force'. – Ekkehard.Horner Dec 18 '12 at 11:55
  • @bonCodigo Can you tell me what the line "Set myRange = wkSheet.Range("B3:F8")" stands for? – arun_roy Dec 18 '12 at 12:01
  • @Ekkehard.Horner my original design was with a ditionary. But I still thought it is brute force due to the 2 arrays. However I couldn't think of any other way. `Intersect` doesn't seem to work here either in sheet `Range` level. I would be happy to see a better solution still, less code lines ;) – bonCodigo Dec 18 '12 at 12:03
  • 1
    @TukaiRakshit `Set myRange = wkSheet.Range("B3:F8")` this line is setting B3:F8 sheet range to a range object. It's more for readability and clean access within the code. It's not really important, coz you can directly use, `myArray = Application.WorksheetFunction.Transpose(wkSheet.Range("B3:F8"))` I have shown two ways to do the same. – bonCodigo Dec 18 '12 at 12:07
  • @bonCodigo Yes I understood that,But in my sample one it is "B2:E5", but your one is not matching.So Can I replace your one by my range there? – arun_roy Dec 18 '12 at 12:12
  • 1
    By all means, you need to adjust the code for **your sheet, your ranges, your values etc...** ;) Not to worry, coz code is dynamic enough to set the array sizes based on the ranges you use. The only thing, if ranges are null, we need to do a validation to `exist sub`. **I even suggest you put a button. Then its `on click` trigger will open two input boxes where you can input the ranges. These two ranges can be the parameters for myRange and deleteRange** ;) makes sense? let me if you want me to update the code with that too. – bonCodigo Dec 18 '12 at 12:20
  • @bonCodigo Your Concept is too new for me,So If you put the values as per my description array and and Excel snaps,I can Understand the Logic. Please help me to understand your algorithm – arun_roy Dec 18 '12 at 12:24
  • @bonCodigo I don't need such buttons,as I will call it as a Procedure from my main VBScript code.So I wan to have fare understanding where it does what and how. And one more important thing I want to use it in near future what I have learnt today from your code. – arun_roy Dec 18 '12 at 12:28
  • I am updating my answer with comments. – bonCodigo Dec 18 '12 at 12:29
  • @bonCodigo please use @ myname :-) so that I can see the notifications from you.. And thanks for your effort to improve my classical code to an advanced one. – arun_roy Dec 18 '12 at 12:31
  • @TukaiRakshit I have updated the code adding comments, do let me know if you have any doubts. – bonCodigo Dec 18 '12 at 12:39
  • @bonCodigo Why you use here "transpose" here? – arun_roy Dec 18 '12 at 12:45
1

Not an answer, but food for thought about optimizing @Larry's solution:

>> a = Array(1, 3, 5, 5, 3, 1)
>> b = Array(1, 2, 3, 4, 5, 6)
>> set c = CreateObject("Scripting.Dictionary")
>> for i = 0 To UBound(a)
>>   c(a(i)) = 0
>> next
>> for i = 0 To Ubound(b)
>>     if c.Exists(b(i)) then
>>        WScript.Echo "delete", i, b(i)
>>     end if
>> next
>>
delete 0 1
delete 2 3
delete 4 5
  1. Using dic(key)=value instead of checking via .Exists
  2. Avoiding extra variable (found) by exploiting the fact, that .Exists returns bool
Ekkehard.Horner
  • 38,498
  • 2
  • 45
  • 96
  • Can you give me such dictionary reference tutorial for reference,where I can read more of its – arun_roy Dec 18 '12 at 13:16
  • Can you also put some thought on my below post? http://stackoverflow.com/questions/13932673/can-we-filter-the-values-and-put-them-into-an-array-using-vbscript#comment19210044_13932673 – arun_roy Dec 18 '12 at 13:17