2016-07-18 6 views
2

Angenommen, ich habe einen großen nicht zusammenhängenden Bereich definiert, vielleicht . Wie würde ich die Indizierung in den Bereich einbeziehen, um sie so zu behandeln, als wäre sie zusammenhängend?Indizierung in großen nicht zusammenhängenden Bereichen

z. Ich möchte so etwas wie

Set myRange = Range("B:B,E:E,F:F") 
v = myRange.ContiguousIndex(5, 3).Value 'retrieves the value in cell F5 (row 5 col 3) 

Jede Methode, die ich bin mir dessen bewusst tun, der im Bereich basierend auf der ersten Zelle versetzt wird („B1“) und wird gerne außerhalb der Grenzen dieses Bereichs gehen, Verschütten in den Rest des Inhalts des Arbeitsbuchs. Das heißt, wenn Sie versuchen, auf Zeile 5, Spalte 3 zuzugreifen, erhalten Sie D5, als ob die Spalten C und D in dem Bereich liegen, den ich zu indexieren versuche.

Ich habe versucht Range.Cells, Range.Offset und Range.Range, aber alle scheinen die gleichen Spillover zu zeigen.

Der andere Ansatz, den ich im Sinn hatte, war es, die Werte einer Variante Array und manuell Index von dort zuweisen, aber dies sehr schnell wie

weil ein einfaches Snippet
Dim v() As Variant 
v = myRange 

nur den ersten Bereich zuweisen kompliziert wird der nicht zusammenhängenden Bereich in das Array, verlassen Sie mich mit einem (20^20-1) x1 Array und vollständig ignoriert den Rest von myRange. Es ist also wahrscheinlich möglich, die gesamte myRange in ein Array zu bringen, wenn ich alle Bereiche durchlaufe und sie einzeln einem Array zuweiße, das ich immer wieder neu zuweisst. Aber es ist alles andere als einfach und ich benutze ein Array, das viel mehr Speicher verbraucht als ich möchte (es sei denn, ich gebe mehr Aufwand in das Beschneiden ein oder wähle willkürlich eine kleinere Anzahl von zu kopierenden Zeilen).

An diesem Punkt wäre es viel effizienter und einfacher, die Bereiche manuell zu durchlaufen und die Indexierung selbst durchzuführen, ohne die Kosten für das Einfügen von Objekten in ein Array aufzuwenden. Dieser letzte Ansatz ist, was ich gerade mache.

Die Frage

Gibt es eine bestehende Methode oder Trick, den ich myRange behandeln kann, als ob es in der Art und Weise zusammenhängende war ich beschrieben und Index in myRange in eine Weise, dass die Diskontinuitäten ignoriert?

TL; DR Wenn ich

Set myRange = Range("B:B,E:E,F:F") 
v = myRange.ContiguousIndex(5, 3).Value 

haben möchte ich einige Verfahren ContiguousIndex Bereich zurückzukehren („F5“) Wert, ohne die ganze Arbeit der manuellen Überprüfung Range.Areas zu tun zu haben und die Handhabung der alle Indizierung. .


Bonus-Frage

Say myRange waren Range("E:E,B:B,F:F") (die verschiedenen Spaltenreihenfolge bemerken). Gibt es eine schöne Möglichkeit, E als erste Spalte, B als zweites und F als die dritten, so dass

Set myRange = Range("E:E,B:B,F:F") 
v = myRange.ContiguousIndex(5, 2).Value 'retrieves the value in cell B5 

gibt den Wert von B5 zu behandeln? Dies ist eine Eigenschaft der Methode, die ich gerne weiter verwenden würde.

Noch einmal, die Funktion habe ich funktioniert, aber ich vermute, dass es eine Art wunderbare Methode oder Trick versteckt in allen Excels Macken, die noch besser wäre.

+2

Es gibt keine eingebaute Methode dafür. –

+0

Das ist bedauerlich. – Mikegrann

+0

Warum sollte ich jemals den Rest des Blattes indexieren wollen, wenn ich eine Methode für einen bestimmten Bereich verwende? Es scheint, dass das Range-Objekt ein besseres Konzept der enthaltenen Zellen haben sollte, anstatt das Problem scheinbar auf das übergeordnete Blatt zu werfen und nur mit einem relativen Offset zu rechnen. Ich weiß, dass das meistens nur eine Beschwerde ist, aber es scheint wie ein schlechtes Design. – Mikegrann

Antwort

0

Wie wäre:

v = myRange.Areas(2).Rows(5).Value 'retrieves the value in cell B5 

Dies ist für die beiden ursprünglichen und Bonus Fragen, solange jeder Teilbereich zu arbeiten scheint, ist eine einzelne Spalte. Sie könnten auch eine einfache Wrapper-Funktion ContiguousIndex(Row,Column) in VBA erstellen, um die von Ihnen beschriebene Schnittstelle zu erhalten.

Hoffe, dass hilft.

1

Etwas ist zu beachten, dass mit .Cells/.Rows/.Columns/._Default Sie Werte außerhalb des Bereichs erhalten kann:

die Werte
Set myRange = Range("E2:E4,C4:B2,F2:F4") ' C4:B2 gets B2:C4 
Debug.Print myRange.Areas(2)(1).Address ' $B$2 

Debug.Print myRange.Areas(2)(0, 0).Address    ' $A$1 
Debug.Print myRange.Areas(2).Cells(0, 0).Address  ' $A$1 
Debug.Print myRange.Areas(2).Rows(0).Columns(0).Address ' $A$1 

Wenn Sie stattdessen Index:

Debug.Print myRange.Areas(2).Value2(1, 1) ' value of B2 
Debug.Print myRange.Areas(2).Value2(0, 0) ' Run-time error '9': Subscript out of range 

Wird durch Chance, dass Sie Bereiche mit mehreren Spalten wie "E:E,A:B" haben, wird es ein bisschen einfacher sein, sie zu indizieren, wenn Sie jede Spalte als einen separaten Bereich angeben: "E:E,A:A,B:B"

2

Ich werde meine eigene Lösung posten, falls jemand anderes auf ein ähnliches Problem stößt. Dies ist der einzige, der für mich funktioniert hat, da die anderen Antworten und Kommentare darauf angewiesen sind, etwas über die Bereiche in der Reihe zu wissen (z. B. dass jeder Bereich eine ganze Spalte ist, was ich nicht garantieren konnte, da meine Bereiche benutzerdefiniert waren). Eingabe und könnte mehrere Spalten oder eine endliche Anzahl von Zeilen umfassen).

' Indexes into a discontiguous area as expected, ignoring cells not in Range r 
' and treating areas as concatenated (and top-aligned) in the order they are specified 
Public Function ContiguousIndex(r As Range, row As Long, col As Long) 
    Dim area As Range 

    For Each area In r.Areas 
     If col <= area.Columns.count Then 
      If row <= area.Rows.count Then 
       ContiguousIndex = area.Cells(row, col) 
       Exit Function 
      Else 
       Err.Raise vbObjectError + 9, , "Row Index out of bounds" 
      End If 
     Else 
      col = col - area.Columns.count 
     End If 
    Next 

    ' col argument > sum of all cols in all areas 
    Err.Raise vbObjectError + 9, , "Col Index out of bounds" 
End Function 

Es lohnt rementioning etwas, das ich in den Kommentaren bedeckt, aber vielleicht unerwartet sein: Dieser Code wird von oben ausrichten alle Bereiche, so dass die erste Zeile im Bereich 1 auf dem gleichen Index wie die erste Zeile in Bereich 2 ist das gleiche ... etc. Das führt zu einer Eigenart beim Aufruf so etwas wie ContiguousIndex(Range("A1:B7,A8:B10"), 9, 2). Obwohl es offensichtlich ist, dass dies B9 zurückgeben sollte, ist dies nicht der Fall - es wird tatsächlich versuchen, auf die 9. Zeile, 2. Spalte von A1:B7 zuzugreifen, was zu einem Fehler führt. Dies liegt daran, dass die zwei nicht zusammenhängenden Bereiche, obwohl sie von oben nach unten auf dem tatsächlichen Blatt angeordnet sind, so behandelt werden, als wären sie von Seite zu Seite. So ist B9 über den Befehl ContiguousIndex(Range("A1:B7,A8:B10"), 2, 4) (unintuitiv) erreichbar. Dieses Verhalten ist erforderlich, aber möglicherweise nicht das, was Sie erwarten. Um dies zu umgehen, können Sie die integrierten Methoden Application.Union oder Application.Intersect verwenden. Diese kollabieren zusammenhängende Regionen nach Möglichkeit automatisch. Alle folgenden Arbeiten:

' Every statement will print "A1:B10" - the areas are merged 

' Union of separate areas 
Debug.Print Union(Range("A1:B7"), Range("A8:B10")).Address 

' Union of range with a known subrange 
Debug.Print Union(Range("A1:B7,A8:B10"), Range("A1:B7,A8:B10").Cells(1, 1)).Address 

' Union of range with itself 
Debug.Print Union(Range("A1:B7,A8:B10"), Range("A1:B7,A8:B10")).Address 

' Intersect of range with itself 
Debug.Print Intersect(Range("A1:B7,A8:B10"), Range("A1:B7,A8:B10")).Address 

Wenn dies das gewünschte Verhalten ist, wenn die Indizierung, dann eines der aufgelisteten verschmilzt durchführen, bevor ContiguousIndex aufrufen. Beachten Sie, dass bei der Zusammenführung von Bereichen in der Vereinigungsoperation ihre relativen nicht zusammenhängenden Indizes unverändert bleiben. Z.B.

' Yields "A:A,F:F,C:D" not "A:A,C:D,F:F" as you might desire 
Debug.Print Union(Range("A:A,F:F,C:C,D:D"), Range("A:A,F:F,C:C,D:D")).Address 
1

Ich denke, ich verstehe Ihre Frage ein wenig besser nach dem Betrachten Ihres Beispiels.Es kann „vereinfachte“ ein klein wenig sein durch die Spalten anstelle der Bereiche aufzählt:

Public Function ContiguousIndex(r As Range, row As Long, col As Long) As Range 
    Dim column As Range 

    For Each column In r.Columns 
     If col > 1 Then 
      col = col - 1 
     ElseIf col = 1 Then 
      If row <= column.Rows.Count And row > 0 Then 
       Set ContiguousIndex = column.Rows(row) 
       Exit Function 
      End If 
      Err.Raise vbObjectError + 9, , "Row Index out of bounds" 
     ElseIf col < 1 Then 
      Err.Raise vbObjectError + 9, , "Column Index out of bounds" 
     End If 
    Next 
End Function 

ich keine Möglichkeit für den Zugriff auf den Enumerator direkt (zB
r.Columns.[_NewEnum].Item(col) funktioniert nicht)

finden konnten, aktualisieren

Gerade zum Beispiel

Public Function veryContiguousIndex(r As Range, row As Long, col As Long) As Range 
    Dim cell As Range, i As Long: i = col * row 

    For Each cell In r.Cells 
     If i = 1 Then Set veryContiguousIndex = cell: Exit Function 
     i = i - 1 
    Next 
End Function 

dann

Dim r As Range: Set r = [A1:B7,A8:B10] 
Debug.Print r.Cells.Count; r.Columns.Count; r.Rows.Count ' 20 2 7 
Debug.Print veryContiguousIndex(r    , 9, 2).Address(0, 0) ' B9 
Debug.Print veryContiguousIndex(r.EntireColumn, 9, 2).Address(0, 0) ' B9 
Debug.Print veryContiguousIndex(r.EntireRow , 9, 2).Address(0, 0) ' R1 
+0

Ich glaube nicht, dass dieser Code so funktioniert, wie Sie es vorhaben. Wenn ich beispielsweise den Testbereich 'A1: B7, A8: B10' in meinem neueren Beispiel anführe, bin ich ziemlich sicher, dass Ihr Code mir niemals erlauben würde, auf Zellen in' A8: B10' zuzugreifen. Dies liegt daran, dass 'r.Columns' effektiv wie' r.Areas (1) .Columns' funktioniert und sich nur auf Zellen aus dem ersten Bereich bezieht. Um selbst zu überprüfen, überprüfen Sie 'r.Columns.Zählen Sie für einen Bereich wie "A1: A5, B1: F1", der wie erwartet mit 1 statt mit 6 bewertet wird. – Mikegrann

+0

Von dem Update in Ihrer Antwort schien so, als ob "Dieses Verhalten ist, was ich benötigt" – Slai

+0

Sorry, wenn es unklar ist, aber das ist nicht so. Alle Zellen in diesem Bereich mussten weiterhin auf vorhersehbare Weise zugänglich sein. 'B9' ist immer noch zugänglich, wenn wir durch alle Bereiche iterieren - es ist nicht unbedingt der am besten geeignete Ort. Wenn wir uns auf den ersten Bereich beschränken würden, hätten wir auch 'r.Areas (1)' explizit verwenden können. Ich denke, die Idee war gut und ich war aufgeregt, als ich es sah, aber Excel behandelt nur "R.Columns" als "r.Areas (1) .Columns" und es ruiniert alles. – Mikegrann

Verwandte Themen