Hindre ekstra elementer i VBA dynamiske matriser

stemmer
1

Som tittelen sier, er det en måte å unngå ekstra elementer fra å dukke opp i VBA dynamiske matriser når de er ikke-null basert?

For eksempel, når du bruker kode som ligner på følgende:

While Cells(ndx, 1).Value <> vbNullString
    ReDim Preserve data(1 To (UBound(data) + 1))
    ndx = ndx + 1
Wend

Du har en ekstra tom array element ved slutten av behandlingen. Selv om dette kan elimineres med følgende:

ReDim Preserve data(1 To (UBound(data) - 1))

Dette ser ikke ut som den beste måten å løse dette problemet.

Som sådan, er det en måte å hindre at ekstra element skapes i første omgang? Fortrinnsvis noe som ikke krever ytterligere logikk på innsiden av løkken.

Publisert på 17/10/2008 klokken 12:41
kilden bruker
På andre språk...                            


6 svar

stemmer
1

Visual Basic arrays er null-basert. Dette kan endres med Option Baseuttalelsen, though.

Med arrays, de ekstra elementene er fordi du gjør en UBound() + 1, vil UBound gi deg riktig nummer allerede. Hvis tabellen har 5 Elements, vil UBound være 5. Men den siste indeksen vil være fire, så ReDim å UBound vil gi deg en rekke størrelse en.

Som arrays er vondt å bruke uansett (i VBA, som er) og ReDim Preserveacually er en rekke kopieringen til en ny fast størrelse array, vil jeg anbefale deg å bruke samlinger hvor du kan. De er mye lettere å gjenta ( For Each ... In ...) og mye mer effektiv ved å legge til, finne og fjerne elementer.

' creation ' 
Dim anyValue as Variant
Dim c as New Collection

' and adding values '
c.Add anyValue, strKey

' iteration '
For Each anyValue in c
  Debug.Print anyValue
Next c

' count values '
Debug.Print c.Count

' element deletion '
c.Delete strKey

(Du kan bruke Scripting.Dictionary fra VBScript for ekstra komfort, men du trenger å referere det først.)

Du kan også ha flere dimensjoner av samlinger ved å plassere dem i hverandre.

Svarte 17/10/2008 kl. 12:54
kilden bruker

stemmer
3

ReDim Preserveer et relativt dyrt drift og sannsynligvis ikke noe å gjøre på hver iterasjon. Bedre å ReDimmatrisen til en øvre grense større enn du trenger (kanskje i biter) deretter redusere array til de nødvendige øvre grenser når du er ferdig.

Dessuten kan det være lurt å undersøke andre måter å lese en Excel Range i en liste f.eks

  Dim a()
  With Sheet1
    a = .Range(.Range("A1"), .Range("A1").End(xlDown)).Value
  End With
  Debug.Print a(1, 1)

Looping er ofte svært treg :)

Svarte 17/10/2008 kl. 13:19
kilden bruker

stemmer
1

VB6 og COM bruke en blanding av 0- og 1 basert indeksering (arrays er 0-basert bortsett fra når du endrer dette med Option Base eller erklære dem ellers eksplisitt).

COM samlinger er vanligvis en basert tidligere COM objektmodeller, men noen ganger 0-basert ...

Dim / Redim data (1 til N) er en gruppe med N elementer indeksert fra 1 til N

Dim / Redim data (0 til N-1) er en gruppe med N elementer indeksert fra 0 til N-1

Dim / Redim data (N) er en gruppe med N + 1 elementer indeksert fra 0 til N (hvis tillegg Basis er 0)

Det siste tilfellet er den som noen ganger forvirrer, data (N) betyr vanligvis data (0 til N), som er en gruppe med N + 1-elementer.

Personlig har jeg erklærer alltid arrays eksplisitt som (0 For å N-1) og ikke stole på Option Base, som er mer kjent for utviklere som bruker mer enn ett språk.

Det er en kant sak: VBA støtter ikke null-lengde matriser, må du alltid ha minst ett element (for hver dimensjon i flerdimensjonale arrays). Så den minste matrisen du kan erklære er data (0 til 0) eller data (1 til 1) med ett element.

I ditt tilfelle, jeg mistenker at du oppretter en matrise med ett element, og deretter legge et element hver gang gjennom løkken:

ReDim data(1 To 1)    
While Cells(ndx, 1).Value <> vbNullString    
    ReDim Preserve data(1 To (UBound(data) + 1))    
    ndx = ndx + 1
Wend

I stedet (og forlate til side for øyeblikket hensynet til effektiviteten i ringer ReDim Bevar i en loop), bør du være å bruke:

ReDim data(1 To 1)    
nStartIndex = ndx
While Cells(ndx, 1).Value <> vbNullString    
    ' On the first iteration this does nothing because
    ' the array already has one element
    ReDim Preserve data(1 To ndx - nStartIndex + 1)    
    ndx = ndx + 1
Wend
Svarte 17/10/2008 kl. 15:42
kilden bruker

stemmer
0

Riktignok det er en stund siden jeg har gjort klassisk VB6, og min VBA erfaring er enda rustier ... hvis memeory serverer, er array syntaks forskjellig for VB i mer enn bare foten som en i stedet for 0. syntaks også sier at "størrelse" som du spesifiserer ikke betegne det totale antall elementer i tabellen, men heller den siste indeksen skal være adresserbare.

Svarte 17/10/2008 kl. 15:54
kilden bruker

stemmer
1

Så dette har vist seg å være en irriterende lite problem som det ser ut som det er virkelig ikke en måte å hindre dette problemet fra å komme opp. Basert på svarene gitt av andre brukere, jeg lei følgende måter å løse problemet.

Ved hjelp av en Collection - Mens denne tilnærmingen fungerer ganske vil i situasjoner der du trenger å lese og lagre data, kan du ikke bruke brukerdefinerte typer med en opphenting. Å kunne definere elementet Nøkkelen er nyttig som du kan bruke den til kryssreferanse to samlinger; Men i VBA er det ingen måte å få listen over tastene i samlingen som kan være begrensende.

Lesing en Excel Range inn en Array - En annen svært hyggelig tilnærming, men det ser ut til å fungere best når du vet hva områdene skal være forut for sin tid. Hvis du må finne ut områdene på fly så kan du finne deg selv i en situasjon der en samling eller en mindre ReDim rekke lettere å jobbe med.

Bygging av Array på fly med ReDim Bevar - Selv om dette kan være en ganske grei operasjon, er det to problemer involvert med det. Det ene er at ReDim kan være en kostbar operasjon som Visual Basic faktisk skaper en ny rekke med gitt størrelse, kopierer gamle array, og deretter sletter den gamle array (eller frigjør det for Garbage Collector i Visual Basic .NET). Så du ønsker å minimere samtaler for å ReDim hvis du skal jobbe med ekstremt store matriser.

I tillegg er du sannsynligvis kommer til å kjøre inn i en situasjon som ligner på den i spørsmålet hvor du har et ekstra element ved starten av rekken eller på slutten av tabellen som er tom. Den eneste måten rundt dette synes å være å enten sjekke for å se om du må endre størrelsen før du gjør operasjonen, eller for å slette tomt element før du returnerer resultatene.

Svarte 17/10/2008 kl. 17:34
kilden bruker

stemmer
1

Det er en god stund siden dette spørsmålet har blitt spurt; men jeg hadde det samme problemet i dag, og løste det på denne måten:

Det er en måte å gjøre det, ved å definere et første element som aldri vil bli brukt. Hvis du trenger en null basert matrisen du vil definere en tom matrise som dette

Dim data()
Dim i as Long

ReDim data(-1 To -1) ' Empty array. We never use data(-1).

For i = 0 To UBound(data)
    ...
Next i

Hvis du trenger en en-basert matrisen du vil gjøre dette

ReDim data(0 To 0) ' Empty array. We never use data(0).

For i = 1 To UBound(data)
    ...
Next i
Svarte 19/03/2012 kl. 13:43
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more