2013-09-24 38 views
3

我有以下(它的表面上,簡單)任務:獲得從非連續範圍的聯合值到陣列VBA用一個簡單的命令(不循環)

拷貝從數字的值使用VBA將電子表格中的列轉換爲二維數組。

爲了讓生活更有趣,列不相鄰,但它們的長度都是相同的。顯然,人們可以通過循環遍歷每個元素來做到這一點,但這看起來很不雅觀。我希望有一個更緊湊的解決方案 - 但我很難找到它。

以下是我認爲「一種簡單的方法」的一些嘗試 - 爲了簡單起見,我將範圍設置爲A1:A5, D1:D5--總共10個單元格在兩個範圍內。

Private Sub testIt() 
    Dim r1, r2, ra, rd, rad 
    Dim valString, valUnion, valBlock 
    Set r1 = Range("A1:A5") 
    Set r2 = Range("D1:D5") 
    valString = Range("A1:A5,D1:D5").Value 
    valUnion = Union(r1, r2).Value 
    valBlock = Range("A1:D5").Value 
End Sub 

當我看着這些變量中,前兩個具有尺寸(1 To 5, 1 To 1)而最後一個具有(1 To 5, 1 To 4)。前兩次我期待得到(1 To 5, 1 To 2),但事實並非如此。

我會很高興,如果我可以在當時循環數據一列,並將一列中的所有值分配給數組中的一列 - 但我無法弄清楚如何做到這一點。像

cNames = Array("A", "D") 
ci = 1 
For Each c in columnNames 
    vals(, ci) = Range(c & "1:" & c & "5").Value 
    ci = ci + 1 
Next c 

但是,有什麼地方不正確的語法。我想要得到的結果可以通過

cNames = Array("A", "D") 
ci = 1 
For Each c in columnNames 
    For ri = 1 To 5 
    vals(ri , ci) = Range(c & "1").offset(ri-1,0).Value 
    Next ri 
    ci = ci + 1 
Next c 

但是這很醜陋。所以這裏是我的問題:

是否有可能獲得一個「複合範圍」(多個非連續塊)的值到一個數組中 - 一次全部或一次一列?如果是這樣,我該怎麼做?

要獲得額外的獎勵積分 - 任何人都可以解釋爲什麼testIt()返回的數組的尺寸爲Base 1,而我的VBA設置爲Option Base 0?換句話說 - 他們爲什麼不是(0 To 4, 0 To 0)?這僅僅是微軟的一個不一致嗎?

回答

10

如果在rng中的每個區域具有相同的行數,那麼這應該起作用。

Function ToArray(rng) As Variant() 
    Dim arr() As Variant, r As Long, nr As Long 
    Dim ar As Range, c As Range, cnum As Long, rnum As Long 
    Dim col As Range 

    nr = rng.Areas(1).Rows.Count 
    ReDim arr(1 To nr, 1 To rng.Cells.Count/nr) 
    cnum = 0 
    For Each ar In rng.Areas 
     For Each col In ar.Columns 
     cnum = cnum + 1 
     rnum = 1 
     For Each c In col.Cells 
      arr(rnum, cnum) = c.Value 
      rnum = rnum + 1 'EDIT: added missing line... 
     Next c 
     Next col 
    Next ar 

    ToArray = arr 
End Function 

用法:

Dim arr 
arr = ToArray(Activesheet.Range("A1:A5,D1:D5")) 
Debug.Print UBound(arr,1), UBound(arr,2) 

至於從rng.Value陣列是基於1 - 而不是爲什麼從零開始,我猜這是因爲映射更容易對實際行/列號而不是基於零的工作表。該Option Base x設置將被忽略

+0

這實際上是和我最後一段代碼一樣的東西 - 除了你使用'For Each',我明確索引了單元格;如果一個區域有多個相鄰的列,那麼你的代碼就可以工作。這是一個解決方案 - 並感謝您發佈它 - 但不是我所希望的。但它可能會成爲最好的人可以希望的?... – Floris

+1

我不知道任何你想做的一次性方法。 –

+0

你的代碼中缺少一行:在'arr(rnum,cnum)= c.Value'後面,你需要一個'rnum = rnum + 1'......我冒昧地將修正的代碼合併到另一個答案中,參考這個答案。再次感謝 - 我開始認爲確實沒有「更快」的方法來做到這一點,所以我會接受這是「正確的」答案(在您確認我提出的代碼更改是正確的之後)。 – Floris

0

添,

感謝您的示例代碼。我遇到了一些問題,不得不重寫它的一些部分。它沒有正確計算行和列。我測試這和它的工作100%

Function ToArray(rng As Range) As Variant() 
Dim arr() As Variant, r As Long, nr As Long 
Dim ar As Range, c As Range, cnum As Long, rnum As Long 
Dim col As Range 
Dim lastrow As Integer 
Dim saverow() As Integer 
Dim lastcolumn As Integer 
Dim templastcolumn As Integer 
For i = 1 To rng.Areas.Count 
    templastcolumn = (rng.Areas(i).Column + rng.Areas(i).CountLarge) - 1 
    If lastrow <> rng.Areas(i).Row Then 
     nr = nr + rng.Areas(i).Rows.Count 
     lastrow = rng.Areas(i).Row 
    End If 
    If lastcolumn < templastcolumn Then lastcolumn = templastcolumn 
Next i 
ReDim arr(1 To nr, 1 To lastcolumn) 
ReDim saverow(1 To lastrow) 
cnum = 0 
rnum = 0 
lastrow = 0 
For Each ar In rng.Areas 
    If lastrow <> ar.Row Then 
     lastrow = ar.Row 
     cnum = 0 
    End If 
    For Each col In ar.Columns 
     cnum = cnum + 1 
     For Each c In col.Cells 
      If saverow(c.Row) = 0 Then 
       rnum = rnum + 1 
       saverow(c.Row) = rnum 
      End If 
      arr(saverow(c.Row), cnum) = c.value 
     Next c 
    Next col 
Next ar 
ToArray = arr 
End Function 

Sub TestCopyArray() 
Dim arr As Variant 

arr = ToArray(ThisWorkbook.Sheets("MSS").Range("B1:D2,G1:J2,B4:D4,B6:D6")) 
ThisWorkbook.Sheets("Sheet1").Range("A1").Resize(UBound(arr, 1), UBound(arr, 2)) = arr 
End Sub 
1

它可以完成你想要什麼,如果你願意加入一個隱藏的工作表。我使用Excel 2010並創建了兩個工作表(Sheet1/Sheet2)來測試我的發現。下面是代碼:

Private Sub TestIt() 

    ' Src = source 
    ' Dst = destination 
    ' WS = worksheet 

    Dim Data As Variant 
    Dim SrcWS As Excel.Worksheet 
    Dim DstWS As Excel.Worksheet 

    ' Get a reference to the worksheet containing the 
    ' source data 
    Set SrcWS = ThisWorkbook.Worksheets("Sheet1") 

    ' Get a reference to a hidden worksheet. 
    Set DstWS = ThisWorkbook.Worksheets("Sheet2") 

    ' Delete any data found on the hidden worksheet 
    DstWS.UsedRange.Columns.EntireColumn.Delete 

    ' Copy the non-contiguous range into the hidden 
    ' worksheet. 
    SrcWS.Range("A1:A5,D1:D5").Copy DstWS.Range("A1") 

    ' Now all of the data can be stored in a variable 
    ' as a 2D array because it will be contiguous on 
    ' the hidden worksheet. 
    Data = DstWS.UsedRange.Value 

End Sub 
+0

感謝您花時間回答這個問題。我接受這種做法 - 但它並不真正符合我尋找的「最簡單的可能方式」標準。製作數組的幻影副本感覺不對。 – Floris

+1

@弗洛伊斯我明白你來自我的緩存方法。我的主要焦點是試圖解決您的問題的'一次性'部分和您的主題行中的'(無循環)'部分。和你一樣,我也希望有一個簡單的命令來完成它,但我能想到的最好的方法是用範圍對象進行一些操作。感謝您的反饋,歡迎您提供答案。 –

相關問題