Копирование значений из диапазона ячеек в массив и обратно с помощью VBA Excel. Простейшие примеры обмена значениями между диапазоном и массивом.
Как известно, VBA обрабатывает информацию в массивах значительно быстрее, чем в ячейках рабочего листа Excel. Поэтому, при работе с большими объемами данных, удобнее использовать массивы, чем наблюдать во время выполнения кода за мерцанием изображения на экране или просто смотреть в неизменную картинку, если обновление экрана отключено (Application.ScreenUpdating = False). Здесь обмен значениями между массивом и диапазоном ячеек будет вполне уместен.
Копирование значений из диапазона ячеек в массив
Чтобы скопировать значения из диапазона ячеек в массив, необходимо объявить переменную универсального типа (As Variant) и присвоить ей значения диапазона ячеек с помощью оператора присваивания (=):
Dim a As Variant a = Range(«A1:C3») |
VBA Excel автоматически преобразует объявленную переменную в двумерный массив, соответствующий размерности диапазона ячеек, в нашем случае в массив — a(1 To 3, 1 To 3), и заполняет его значениями. Нумерация измерений массивов, созданных таким образом, начинается с единицы (1).
Можно, в этом случае, объявить сразу динамический массив, чтобы изначально указать, что эта переменная будет массивом. Так как свойством диапазона ячеек по умолчанию в VBA Excel является значение (Value), его можно в коде явно не указывать, но, при желании, можно и указать. Получится такая конструкция, аналогичная первой:
Dim a() As Variant a = Range(«A1:C3»).Value |
Стоит отметить, что для копирования значений из диапазона ячеек в массив можно использовать только обычную переменную или динамический массив универсального типа (Variant). VBA Excel автоматически преобразовывает их в двумерный массив. Если объявить двумерный массив с указанной заранее размерностью, использовать его не получится, будет сгенерирована ошибка с сообщением: Can’t assign to array (Нельзя назначать массив).
Копирование значений из массива в диапазон ячеек
Значения в диапазон ячеек добавляются из массива с помощью оператора присваивания (=):
Range(«A6:F15») = a ‘или Range(«A6:F15»).Value = a ‘где a — переменная двумерного массива |
Обратите внимание, что вставить значения в диапазон ячеек можно только из двумерного массива. Размерность такого массива может начинаться с нуля (0). Количество элементов в измерениях массива должно совпадать с количеством строк и столбцов в диапазоне ячеек. Если вам нужно вставить значения в одну строку или в один столбец, укажите размерность единственной строки или единственного столбца как (0) или (1 To 1), если вы хотите использовать нумерацию измерений своего массива с единицы. Например, для записи десяти значений из массива в одну строку можно объявить такой массив — massiv(9, 0), или в один столбец — massiv(0, 9).
Для вставки значений в диапазон ячеек из массива идеально подойдет массив, созданный для копирования в него значений из диапазона. В этом случае, данные с рабочего листа Excel переносятся в массив, обрабатываются и, после обработки, вставляются обратно в ту же или другую таблицу на том же или другом рабочем листе.
Обмен значениями между двумя диапазонами
Обмен значениями можно осуществить в VBA Excel не только между массивом и диапазоном, но и между двумя диапазонами одинаковой размерности:
Range(«B2:D6») = Range(«G7:I11»).Value |
У диапазона, являющегося источником значений, обязательно должно быть указано свойство Value
.
Если диапазон ячеек, принимающий значения, по размеру меньше диапазона-источника, то он будет заполнен полностью:
Range(«B2:D6») = Range(«G5:L13»).Value |
Если принимающий диапазон ячеек по размеру больше передающего, то часть его будет заполнена значениями диапазона-источника, а остальные ячейки — значениями #Н/Д
:
Range(«B2:D6») = Range(«G7:H9»).Value |
Простейшие примеры обмена значениями
Эти примеры составлены так, чтобы вам не пришлось совершать лишних действий, просто скопируйте их в свой модуль любой книги Excel с поддержкой макросов и запустите по очереди на выполнение.
Пример 1
Заполнение двумерного массива значениями и и их присвоение диапазону ячеек на рабочем листе Excel:
Sub Test1() Dim a(2, 2) As Variant a(0, 0) = «телепузик» a(0, 1) = «журналист» a(0, 2) = «ящерица» a(1, 0) = «короед» a(1, 1) = «утенок» a(1, 2) = «шмель» a(2, 0) = 200 a(2, 1) = 300 a(2, 2) = 400 Range(«A1:C3»).Value = a End Sub |
В данном случае переменная массива не обязательно должна быть универсального типа (As Variant), например, если бы в нее записывались только текстовые данные, ее можно было бы объявить как строковую (As String), и все бы работало.
Пример 2
Объявление обычной переменной универсального типа, присвоение ей значений из диапазона ячеек «A1:C3», записанных кодом первого примера, и вставка этих значений из полученного двумерного массива в диапазон «D10:F12»:
Sub Test2() Dim a As Variant a = Range(«A1:C3») Range(«D10:F12») = a End Sub |
Естественно, указанные диапазоны ячеек расположены на активном листе.
Пример 3
Допустим, на рабочем листе «Лист1» в ячейках «A1:A5» записано количество какого-то товара, а в ячейках «B1:B5» — его цена. Необходимо к этой информации добавить сумму каждого товара, умножив количество на цену, и перенести данные на «Лист2».
Sub Test3() Dim a As Variant, i As Long a = Лист1.Range(«A1:C5») For i = 1 To 5 a(i, 3) = a(i, 1) _ * a(i, 2) Next Лист2.Range(«A1:C5») = a End Sub |
Массив создан сразу с размерностью 5×3 с элементами под суммы. Даже если на первом листе в ячейках «C1:C5» есть какие-то значения, в массиве они будут перезаписаны результатами вычислений.
Копирование значений из массива в массив
Этот пример показывает, как в VBA Excel можно скопировать значения из одного массива в другой:
Sub Test4() Dim arr1, arr2 arr1 = Range(«G7:I11») arr2 = arr1 Range(«B2:D6») = arr2 End Sub |
I have a repeating list of 145 categories, with 15 columns of data for each category.
I am consolidating this list by reducing the number of categories to 24 and adding the corresponding data.
For example,
If initially I had Categories A B C D E F G and I consolidated, I would add all the values in A and, say F, to get a new category.
Another issue is that all these 145 categories are repeated over 60 time periods. So I have to consolidate the data separately for each time period.
To do this, I am trying to use arrays.
Sub CategoriesToSectors()
Dim k As Integer
Dim j As Integer
Dim p As Integer
Dim Destination As Range
' p is just a filler/dummy variable until I later decide which categories go into which sector
Dim CategoryData(144, 14) As Long
Dim SectorData(23, 14) As Long
k = 0
' k should go Upto 60
' I first copy the data from a range in the first worksheet into the array CategoryData
' Then I move 145 rows down for the next time-period's data and repeat this whole process
While k < 60
Sheets("ReformattedData").Select
Range("B1:P145").Select
ActiveCell.CurrentRegion.Offset(k * 145, 0).Select
CategoryData = Selection.Value
For j = 0 To 14
SectorData(0, j) = CategoryData(1, j) + CategoryData(6, j) + CategoryData(8, j) + CategoryData(13, j)
For p = 1 To 23
SectorData(p, j) = CategoryData(15, j) + CategoryData(19, j) + CategoryData(31, j) + CategoryData(44, j)
Next p
Next j
' paste consolidated sectordata array one below another in SectorData worksheet
Sheets("SectorData").Select
Range("B2").Select
Set Destination = ActiveCell.Offset(k * 25, 0)
Destination.Resize(UBound(SectorData, 1), UBound(SectorData, 2)).Value = SectorData
Wend
End Sub
As you can see, what I am doing is first trying to copy the first range block into the CategoryData array. Then, I am combining the data into the sector array — I have just used repeated values to test it — the for loop with p should not exist. I will eventually use 24 different statements to create the SectorData Array.
Then I paste the Consolidated data onto another sheet. I Go back to the first sheet and move my selection down for the next range block (145 cells below the first cell) then I select this data and repeat.
This does not seem to work — error in entering data into the first array — CategoryData.
Help would be appreciated.
Thank you
Return to VBA Code Examples
In this Article
- Assign Range to Array
- Assign Value From a Single Column
- Assign value from multiple columns
This tutorial will demonstrate how to populate an array with a range of cells.
Assign Range to Array
We can easily populate a Variant array with a range of cells.
Assign Value From a Single Column
This example will loop through Range(“A1:A10”), assigning the the cell values to an array:
Sub TestArrayValuesSingle()
'Declare the array as a variant array
Dim arRng() As Variant
'Declare the integer to store the number of rows
Dim iRw As Integer
'Assign range to a the array variable
arRng = Range("A1:A10")
'loop through the rows - 1 to 10
For iRw = 1 To UBound(arRng)
'show the result in the immediate window
Debug.Print arRng(iRw , 1)
Next iRw
End Sub
The UBound is used to set the array upper bound (eg 10) so that the loop knows to loop 10 times.
The Debug.Print function will show you the value contained in the array in the immediate window.
Assign value from multiple columns
Sub TestArrayValuesMultiple()
'Declare the array as a variant array
Dim arRng() As Variant
'Declare the integer to store the number of rows
Dim iRw As Integer
'Declare the integer to store the number of columns
Dim iCol as Integer
'Assign range to a the array variable
arRng = Range("A1:C10")
'loop through the rows - 1 to 10
For iRw = 1 To UBound(arRng,1)
'now - while in row 1, loop through the 3 columns
For iCol = 1 to UBound(arRng,2)
'show the result in the immediate window
Debug.Print arRng(iRw, iCol)
Next iCol
Next iRw
End Sub
In the code above, we have populated the array with the values in Range(“A1:C10”).
The UBound is once again used – but this time it is needed twice – once to loop through the rows, and then again to loop through the columns.
The Debug.Print function will show you the value contained in the array in the immediate window.
VBA Coding Made Easy
Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!
Learn More!
VBA Range to 2D Array
These list of topics are covered in this page.
- Vba Code to initialize 2D Array with Range
- Vba to write Array data to worksheet
- Resize Array to fit worksheet.
This code converts worksheet data in specific range into an vba array. Lets see how it works.
1.Excel Macro – Convert Range To Array(2-Dimention)
Actually, it does not require any special function. It is almost easy like assigning a value to a variable.
Once you assign the range to the array, Excel automatically calculates & allocates array dimensions. You don’t have to specify the number of rows, columns or dimensions for the array.
Excel decides these parameters automatically & created a 2D Array by default. It will be clear from below example.
'-------------------------------------------------------------------------------- 'Visit https://officetricks.com to get more Free & Fully Functional VBA Codes '-------------------------------------------------------------------------------- Sub Range_To_Array() 'Declare Array as variant & Range Dim rArray() As Variant, rRange As Range, iRow As Double, iCol As Double 'Initialize Range to Reference a portion of Worksheet Set rRange = ThisWorkbook.Sheets("sheet1").Range("A1:D2") 'Assign Range to Array rArray = rRange.Value 'Read content of Array one by one For iRow = 1 To UBound(rArray, 1) For iCol = 1 To UBound(rArray, 2) Debug.Print rArray(iRow, iCol) Next Next End Sub
Once you convert the worksheet data to array, you get the advantage of using the Array function on the values. This way the code runs much faster than accessing worksheet data each time.
2. Convert Array to Range – Write to Worksheet
When we write values back to worksheet from array, you have to explicitly mention number of rows & columns the data should occupy.
For this first assign a cell to a range variable: set rng = Thisworkbook.Sheet(1).Range(“A1”) & then use range.resize as explained in this code.
Sub ArrayToRange() 'Declare Array as variant & Range Dim rArray() As Variant, rRange As Range, iRow As Double, iCol As Double Set rRange = ThisWorkbook.Sheets("sheet1").Range("A1:D2") rArray = rRange.Value 'Write values in Array back To a Range in worksheet Set rRange = ThisWorkbook.Sheets("sheet1").Range("F1:I2") rRange = rArray 'OR Resize Range as per the structure of Array iRow = UBound(rArray, 1) iCol = UBound(rArray, 2) Set rRange = ThisWorkbook.Sheets("sheet1").Range("F1") Set rRange = rRange.Resize(iRow, iCol) MsgBox rRange.Address(0, 0) rRange = rArray End Sub
To know how Range.resize works, read further in the next section.
3. Excel VBA – Resize Array to fit Worksheet
Once it is initialized, then it can be extended or resized as per the structure of the array.
'Get Number of Rows in Array rows = UBound(arr,1) 'Get number of columns in Array cols = UBound(arr,2) 'Resize array as per the array dimensions set rng = rng.resize(rows,cols) 'Write Array value to the Range rng = arr
So, the Excel VBA function Range.Resize holds the key here. It can be used to expand or compress the target destination range size.
I have often read that processing values from an array is much faster than processing values from Excel sheet. Though, I haven’t tested this. But, in case you want to move the values from worksheet or Table to an Array, then this code can be used.
External Reference: Here is another wonderful article from cpearson about arrays & worksheets.
I use this formula to copy unique records from Column A into Column B.
Range("A1", Range("A100").End(xlUp)).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("B1"), Unique:=True
Instead of copying it into Column B how do you put the filtered results into an array in Excel VBA?
asked Aug 16, 2012 at 21:33
2
It has been exactly a year since this question was asked but I ran into the same problem today and here is my solution for it:
Function copyFilteredData() As Variant
Dim selectedData() As Variant
Dim aCnt As Long
Dim rCnt As Long
Range("A1").CurrentRegion.SpecialCells(xlCellTypeVisible).Select
On Error GoTo MakeArray:
For aCnt = 1 To Selection.Areas.Count
For rCnt = 1 To Selection.Areas(aCnt).Rows.Count
ReDim Preserve SelectedData(UBound(selectedData) + 1)
selectedData(UBound(selectedData)) = Selection.Areas(aCnt).Rows(rCnt)
Next
Next
copyFilteredData = selectedData
Exit Function
MakeArray:
ReDim selectedData(1)
Resume Next
End Function
This will leave element 0 of the array empty but UBound(SelectedData) returns the number of rows in the selection
answered Aug 16, 2013 at 9:42
Johan GJohan G
3975 silver badges12 bronze badges
3
Just in case anyone ever looks at this again…
I created this function to work on a 1-D range but it will also write a higher dimension range to a 1-D array; it shouldn’t be too hard to modify to write a multiple dimension range to a «same shape» array. You need to have a reference to scrrun.dll to create the dictionary object. Scaling may be a problem since a «for each» loop is used but if you are using EXCEL this is likely nothing you are worried about:
Function RangeToArrUnique(rng As Range)
Dim d As Object, cl As Range
Set d = CreateObject("Scripting.Dictionary")
For Each cl In rng
d(cl.Value) = 1
Next cl
RangeToArrUnique = d.keys
End Function
I’ve tested this in this way:
Dim dat as worksheet
set dat = sheets("Data")
roomArr = Array("OR01","OR02","OR03")
dat.UsedRange.AutoFilter field:=2, criteria1:=roomArr, operator:=xlFilterValues
fltArr = RangeToArrUnique(dat.UsedRange.SpecialCells(CellTypeVisible))
Hope this helps someone out there!
answered Jan 20, 2016 at 15:26
Sub tester()
Dim arr
arr = UniquesFromRange(ActiveSheet.Range("A1:A5"))
If UBound(arr) = -1 Then
Debug.Print "no values found"
Else
Debug.Print "got array of unique values"
End If
End Sub
Function UniquesFromRange(rng As Range)
Dim d As Object, c As Range, tmp
Set d = CreateObject("scripting.dictionary")
For Each c In rng.Cells
tmp = Trim(c.Value)
If Len(tmp) > 0 Then
If Not d.Exists(tmp) Then d.Add tmp, 1
End If
Next c
UniquesFromRange = d.keys
End Function
answered Aug 16, 2012 at 22:15
Tim WilliamsTim Williams
150k8 gold badges96 silver badges124 bronze badges
You will want to Read this and it will point you in the right direction
It says:
- Use the AdvancedFilter method to create the filtered range in some unused area of a worksheet
- Assign the Value property of that range to a Variant to create a two-dimensional array
- Use the ClearContents method of that range to get rid of it
answered Aug 16, 2012 at 22:05
SorceriSorceri
7,8101 gold badge28 silver badges38 bronze badges
3
A simple way to store a filtered range in an array is to use the copy-paste trick. Create a worksheet and make it hidden or very hidden. Say its code name is sht_calc
. This function will give you a 2D array unless you only have one column and the filtered rows are only one, which in that case it will be a simple variant variable and not an array
Function GetArrayFromFilteredRange(rng As Range) As Variant
Dim arr As Variant
sht_calc.Cells.Clear
rng.Copy sht_calc.Range("A1")
arr = sht_calc.UsedRange.Value
GetArrayFromFilteredRange = arr
End Function
For example if you want to get the array of filtered rows in a table called Table1
in a worksheet with a code name of sht1
you can simply do this:
dim rng as range
arr = GetArrayFromFilteredRange(sht1.ListObjects("Table1").DataBodyRange.SpecialCells(xlCellTypeVisible))
arr=GetArrayFromFilteredRange(rng)
answered Jul 1, 2020 at 13:52
IboIbo
3,9716 gold badges46 silver badges63 bronze badges
Here’s another way to do it. If there are no results it just does nothing.
Public Sub filteredRangeToArray(rg As Range, arr As Variant)
Dim i As Long
Dim j As Long
Dim row As Range
'If 0 results in Filter just exit
If Not rg.SpecialCells(xlCellTypeVisible).Count > 0 Then Exit Sub
i = 1
Erase arr
ReDim arr(1 To rg.Columns.Count, 1 To _
rg.Columns(1).SpecialCells(xlCellTypeVisible).Count)
For Each row In rg.Rows
If Not row.Hidden Then
For j = LBound(arr, 1) To UBound(arr, 1)
arr(j, i) = row.Cells(j)
Next j
i = i + 1
End If
Next row
arr = WorksheetFunction.Transpose(arr)
End Sub
answered Apr 11, 2021 at 13:16
CharlioCharlio
3364 silver badges14 bronze badges
The following takes information from column A and gives a list. It assumes you have a «Sheet3» which is available for data input (you may wish to change this).
Sub test()
Dim targetRng As Range
Dim i As Integer
Set targetRng = Sheets(3).Range("a1")
Range("A1", Range("A999").End(xlUp)).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=targetRng, Unique:=True
Dim numbElements As Integer
numbElements = targetRng.End(xlDown).Row
Dim arr() As String
ReDim arr(1 To numbElements) As String
For i = 1 To numbElements
arr(i) = targetRng.Offset(i - 1, 0).Value
Next i
End Sub
answered Aug 16, 2012 at 21:58
enderlandenderland
13.7k17 gold badges100 silver badges152 bronze badges
I know you can easily take a range of cells and slap them into a Variant Array but I want to work with a string array (because it’s single-dimensional and takes less memory than a Variant array).
Is there any way to automatically convert a range into a string array?
Right now I am using a function that will take the range and save the values in a variant array, then convert the variant array to a string array. It works nice , but I’m looking for a way to go directly from the range to string array. Any help would be greatly appreciated.
Function RangeToArray(ByVal my_range As Range) As String()
Dim vArray As Variant
Dim sArray() As String
Dim i As Long
vArray = my_range.Value
ReDim sArray(1 To UBound(vArray))
For i = 1 To UBound(vArray)
sArray(i) = vArray(i, 1)
Next
RangeToArray = sArray()
End Function
UPDATE:
It’s looking like there is no way to skip the step of throwing the data into a variable array first before converting it to a single-dimensional string array. A shame if it’s true (even if it doesn’t take much effort, I like to ultra-optimize so I was hoping there was a way to skip that step). I’ll close the question in a few days if no solution presents itself. Thanks for the helpful comments, guys!
UPDATE2:
Answer goes to Simon who put in great effort (so did everyone else) and utlimately pointed out it’s indeed impossible to go from range to string array in one shot. Thanks, everyone.