Arrays og ranges i Excel VBA - slet rækker
De fleste, som kaster sig over VBA og makroer i Excel, lærer hurtigt at bruge ranges, for det er virkelig smart og forholdsvis nemt at gå til.
Denne side viser eksempler på, hvordan man lynhurtigt kan fjerne rækker i et range (en tabel) ved at tage arrays til hjælp, men det kan også være andre opgaver end fjernelse af rækker.
Man kan godt gennemløbe et range og fjerne eller indsætte rækker, men det er bøvlet, fordi ens range pludselig har ændret sig, og hvis man fx fjerner den række, man står i, står man pludselig i det, der før var rækken nedenunder. Det er både nemmere og hurtigere i et array!
Det første eksempel viser, hvordan man kan fjerne hveranden række, og det andet hvordan man kan fjerne rækker nedefra, hvis rækken ovenover har identiske værdier i udvalgte kolonner.
Man kan bruge andre kriterier, og målet behøver ikke være fjernelse af rækker - det er blot mine eksempler. Du kan også downloade et regneark med eksemplerne.
Fordelen ved arrays
Hvis man arbejder med store tabeller, kan man ofte få meget hurtigere kode, hvis man kopierer sit range over i et array og laver operationerne her for dernæst at sætte arrayet ind som en (ny) tabel i ét hug.
Hastighedsforøgelsen skyldes, at der er et meget stort overhead i skrivning til og fra regnearket, mens det går lynhurtigt i et array.
Metoden egner sig ikke til alle opgaver, men jeg tror, at mange tumler med meget store tabeller i Excel (fx logfiler eller rapporter) og ofte har brug for at luge ud i disse data efter helt bestemte retningslinjer.
Hvis det drejer sig om sortering eller filtrering, bør man altid anvende Excels indbyggede funktioner hertil (fx autofilter), da ingen VBA-kode kan måle sig med disse i hastighed; men har man særlige behov, kan VBA være en stor hjælp.
Fjern hveranden række
Det følgende eksempel er forholdsvis enkelt: Det fjerner hveranden række i et range. Om nogen har brug for det, ved jeg ikke, men det illustrerer teknikken uden for meget flimmer.
- Kopiér dit range til et todimensionel array i ét hug. Arrayet får automatisk de samme dimensioner som ranget (antal rækker og kolonner).
- Kopiér de udvalgte poster (rækker - her hveranden) til et nyt array.
- Sæt det nye array ind som en tabel i regnearket i ét hug - altså ikke noget dræbende langsomt gennemløb med celle for celle.
Eksemplet forudsætter, at regnearket med tabellen er aktivt, og at tabellen starter i celle A1. Du kan markere koden med musen, kopiere med CTRL+C og indsætte den i et VBA-modul med CTRL+V. Du kan også downloade et regneark med eksemplerne på denne side.
Sub FjernHveranden()
Dim MyArray() As Variant
Dim NewArray() As Variant
Dim rTable As Range
Dim lCount As Long
Dim lCount2 As Long
Dim lCount3 As Long
Dim lRows As Long
Dim lCols As Long
Dim lNewRows As Long
On Error GoTo ErrorHandle
Application.ScreenUpdating = False
If Len(Range("A1")) = 0 Then
MsgBox "Celle A1 er tom. Eksemplet forudsætter " & _
"at tabellen starter i celle A1."
GoTo BeforeExit
End If
Set rTable = Range("A1").CurrentRegion
MyArray = rTable.Value
With rTable
lRows = .Rows.Count
lCols = .Columns.Count
End With
If lRows Mod 2 = 0 Then
lNewRows = lRows / 2
Else
lNewRows = lRows / 2 + 1
End If
ReDim NewArray(lNewRows, lCols)
For lCount = 1 To lRows Step 2
lCount2 = lCount2 + 1
For lCount3 = 1 To lCols
NewArray(lCount2, lCount3) = MyArray(lCount, lCount3)
Next
Next
ReDim Preserve NewArray(lNewRows, lCols)
Sheets.Add
Set rTable = Range("A1").Resize(lNewRows, lCols)
rTable.Value = NewArray
BeforeExit:
Set rTable = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Fejl i procedure FjernHveranden."
Resume BeforeExit
End Sub
Sletning af identiske rækker i en tabel
Det næste eksempel er lidt mere kompliceret, men nok også mere brugbart. Det viser, hvordan man kan slette rækker ved at bruge bestemte kriterier.
Konkret bruger jeg makroen til at luge ud i døgnrapporter med kemiske analyser. Det er ikke alle produkter, der analyseres hver time, og hvis der ikke er kommet en ny analyse kl. xx, vil den sidste blive gentaget i døgnrapporten for den pågældende time.
Døgnrapporten har 24 rækker, en for hver time, og i min tabel kan jeg fx have døgnrapporter for et helt år.
For kun at have unikke analyser ønsker jeg altså at fjerne de rækker, som blot er en gentagelse af rækken ovenover. Hvis anlægget fx er stoppet, optræder den samme analyse måske i dagevis, og det er ikke smart, hvis man skal se på spredning og den slags.
I punktform sker der følgende:
- Tabellen defineres som et range (her: rTable). I eksemplet forudsættes, at tabellen er i det aktive faneblad, og at tabellen starter i celle A1.
- Ranget kopieres over i et array, MyArray, i ét hug.
- Brugeren bliver bedt om at udpege den eller de kolonner, som skal bruges i sammenligningen - altså hvilke kolonner er afgørende for, om rækken er identisk med rækken ovenover.
- Arrayet gennemløbes række for række nedefra, og hvis en række er en kopi, indsættes et mærke (her ordet "slet") i det der svarer til første celle i rækken.
- Et nyt array, NewArray, defineres.
- Alle elementer i MyArray som ikke indeholder ordet "slet" i første kolonne kopieres over i NewArray.
- Det nye array med den "rensede" tabel indsættes i ét hug på et nyt faneblad.
Du kan markere koden med musen, kopiere med CTRL+C og indsætte den i et VBA-modul med CTRL+V. Du kan også downloade et regneark med eksemplerne på denne side.
Here we go:
Sub FjernDubletter()
Dim bSame As Boolean
Dim MyArray() As Variant
Dim NewArray() As Variant
Dim rCell As Range
Dim rTable As Range
Dim rIsect As Range
Dim lRows As Long
Dim lCols As Long
Dim lCount As Long
Dim lCount2 As Long
Dim lCount3 As Long
Dim colColumns As Collection
On Error GoTo ErrorHandle
If Len(Range("A1")) = 0 Then
MsgBox "Celle A1 er tom. Eksemplet forudsætter " & _
"at tabellen starter i celle A1."
GoTo BeforeExit
End If
Set rTable = Range("A1").CurrentRegion
MyArray = rTable.Value
On Error Resume Next
Set rCell = Application.InputBox(prompt:="Markér de kolonner" & _
" som skal bruges i sammenligningen. Det er nok at markere " & _
"én celle i hver kolonne.", Type:=8)
If rCell Is Nothing Then
MsgBox "Der blev ikke valgt kolonner - programmet afbrydes."
GoTo BeforeExit
End If
On Error GoTo ErrorHandle
Application.ScreenUpdating = False
Set rIsect = Application.Intersect(rCell, rTable)
If rIsect Is Nothing Then
MsgBox "Det valgte område er uden for tabellen"
GoTo BeforeExit
End If
Set colColumns = New Collection
With rTable
For lCount = 1 To .Columns.Count
Set rIsect = Application.Intersect(rCell, .Columns(lCount))
If Not rIsect Is Nothing Then
colColumns.Add lCount
End If
Next
End With
With rTable
lRows = .Rows.Count
lCols = .Columns.Count
End With
lSlet = 0
For lCount = lRows To 2 Step -1
bSame = True
With colColumns
For lCount2 = 1 To .Count
If MyArray(lCount, .Item(lCount2)) <> _
MyArray(lCount - 1, .Item(lCount2)) Then
bSame = False
Exit For
End If
Next
If bSame Then
MyArray(lCount, 1) = "slet"
lSlet = lSlet + 1
End If
End With
Next
lCount2 = 0
lSlet = lRows - lSlet
ReDim NewArray(lSlet, lCols)
For lCount = LBound(MyArray) To UBound(MyArray)
If MyArray(lCount, 1) <> "slet" Then
lCount2 = lCount2 + 1
For lCount3 = 1 To lCols
NewArray(lCount2, lCount3) = MyArray(lCount, lCount3)
Next
End If
Next
ReDim Preserve NewArray(lSlet, lCols)
Sheets.Add
Set rCell = Range("A1").Resize(lSlet, lCols)
rCell.Value = NewArray
BeforeExit:
Set rCell = Nothing
Set rTable = Nothing
Set rIsect = Nothing
Set colColumns = Nothing
Application.ScreenUpdating = True
Exit Sub
ErrorHandle:
MsgBox Err.Description & " Fejl i procedure FjernDubletter."
Resume BeforeExit
End Sub
Relateret:
|