Сразу прошу прощения за "стену текста", познаю VBA недавно. Хочется не только получить результат, но и понять логику разработки.
Есть "х" листов с разным количеством заполненных строк и одинаковым количеством заполненных столбцов. Т.е. первая размерность разная, вторая одинаковая. Необходимо собрать данные со всех листов в один массив
Должно получиться так (на примере двух листов): + : arr1 : : Variant/Variant(1 to 26, 1 to 200) - массив с первого листа + : arr2 : : Variant/Variant(1 to 29, 1 to 200) - массив со второго листа + : arr3 : : Variant/Variant(1 to 55, 1 to 200) - "массив массивов"
Для решения задачи (и подтверждения того, что "массив массивов" подойдет) был использован код с перебором значений (под спойлером). Работает, но проблема заключается в том, что нужно использовать не 2 листа, а, к примеру, 20 или 50 (количество постоянно меняется).
Скрытый текст
Код
Dim arr1 As Variant, arr2 As Variant, arr3 As Variant
Dim i As Long, j As Long
lrow1 = Sheets("1").Cells(Rows.Count, 1).End(xlUp).Row
lrow2 = Sheets("2").Cells(Rows.Count, 1).End(xlUp).Row
arr1 = Sheets("1").Range("A1:GR" & lrow1)
arr2 = Sheets("2").Range("A1:GR" & lrow2)
ReDim arr3(1 To (UBound(arr1) + UBound(arr2)), _
1 To Application.Max(UBound(arr1, 2), UBound(arr2, 2)))
For i = 1 To UBound(arr1)
For j = 1 To UBound(arr1, 2)
arr3(i, j) = arr1(i, j)
Next j, i
For i = 1 To UBound(arr2)
For j = 1 To UBound(arr2, 2)
arr3(i + UBound(arr1), j) = arr2(i, j)
Next j, i
На данном форуме удалось найти единственный, похожий на мою ситуацию, совет-ответ, к большому сожалению, без самого кода:
Цитата
The_Prist написал: Вообще, для сокращения кол-ва строк кода можно собирать в отдельные массивы данные с книг. Сами массивы помещать в один общий массив(в котором будут эти массивы). Иными словами создать массив массивов. А потом все это выгружать или объединить в один массив. Как алгоритм: 1. Создаем динамический массив массивов(avMain) 2. Идем циклом по книгам и собираем данные в промежуточные массивы(avBook_1, avBook_2 ... avBook_n). Попутно в отдельную переменную плюсуем размерность каждого массива: lResCnt = lResCnt + Ubound(avBook1,1) 3. Определяем размерность результирующего массива(avRes): ReDim avRes(1 to lResCnt, 1 to lColsCnt) 4. Осталось только пройтись циклом по avMain и по очереди выгрузить циклами данные из этих массивов в avRes.
Решение вроде понятное, но как его преобразовать в макрос не могу сообразить. 1) создать динамический массив массивов - разве его можно создать первым пунктом, без понимания размерности? 2) идем циклом по листам и собираем данные в промежуточные массивы - я понимаю, как задать цикл по листам, но как автоматически задать каждому листу новый-уникальный массив? 3) ...
К сообщению прикрепил файл с примером. Голые данные, без макросов. Если необходимо
Fylhtqq написал: но проблема заключается в том, что нужно использовать не 2 листа, а, к примеру, 20 или 50
макросу по-барабану сколько листов использовать: 2, 3, 50 или 50 тыс. листов если все решвется одним алгоритмом количество строк кода не увеличится от количества обрабатываемых листов
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
Ігор Гончаренко написал: макросу по-барабану сколько листов использовать: 2, 3, 50 или 50 тыс. листов
Согласен с вами. Одна из моих проблем заключается в том, что я "руками" присваивал каждому листу свою переменную. если делать это для 50 листов - капец :
ReDim arr3(1 To (UBound(arr1) + UBound(arr2)), 1 To Application.Max(UBound(arr1, 2), UBound(arr2, 2)))
Предполагал, что можно сделать примерно так:
Код
For Sh = 1 To Sheets.Count
Worksheets(Sh).Activate
lrow = Cells(Rows.Count, 1).End(xlUp).Row
arr = Range("A1:GR" & lrow)
но уперся в то, что не понял, как тогда присваивать уникальное название переменной массива листа. Т.е. лист 1 - arr1, лист 2 - arr2 и тп вероятно, что можно не формировать множество переменных массивов (для каждого листа), а просто чистить массив arr через "Erase". Но тогда опять не понятно, как формировать размерность массива массивов.
Не уверен, что вообще правильно понимаю и объясняю, но ничего лучше пока не придумал.
Скорее всего, я не правильно выразился. Я присваивал переменную массива каждому листу, т.е. arr1 это данные из "Лист1", arr2 - из "Лист2". И уже исходя из этих переменных формировал массив каждого листа.
Если проходить циклом по листам, как задать каждому листу свою переменную массива? и как потом такое кол-во переменных использовать при создании размерности массива массивов
Dim a, sh, i&
redim a(1 to worksheets.count)
for each sh in worksheets
i = i+1
a(i) = sh.usedrange
next
ВСЕ!!! в массиве а у вас массивы данных с листов (ели только размер а не вывалится за пределы возможностей адресного пространства операционной ссистемы или не превысит максимально допустимый разиер) т.е. вопрос как это сделать решен, но совершенно отрыт вопрос "а на кой это нужно"? (зачем они все вместе вам нужны?????)
Nordheim написал: Fylhtqq , А что вы дальше собираетесь делать с массивами?
Мне необходимо получить данные со всех листов в одном массиве. Для этого предполагалось, что с каждого листа будет "получен" отдельный массив и потом все массивы будут объединены в один. На сколько это удачный путь, не могу сказать, но выглядит логично.
только с вашей точки зрения с моей точки зрения - это абсурдно, а с учетом необходимости проверки не вылезло-ли все за пределы возможностей - вообще как бы это по-мягче сказать, о! - это не рационально! (просились другие слова, но "не рационально" - это корректное выражение для этой ситуации) если понимать чего вы добиваетесь - можно подсказать как этого достичь, а пока можно только недоумевать: "зачем все это нужно???"
Option Explicit
Option Private Module
'====================================================================================================
Sub AllSheetsInArray()
Dim sh As Worksheet
Dim arr, aRes(), r&, c&, nR&, cMax&
ReDim aRes(1 To 100000, 1 To 200) ' объявляем основной массив с количеством строк и столбцов "с запасом"
For Each sh In ActiveWorkbook.Worksheets ' цикл по всем листам книги
arr = sh.UsedRange.Value ' берём всю рабочую область листа в массив
If IsArray(arr) Then ' если было только одно значение (получен НЕмассив), то переходим к следующему листу
If UBound(arr, 2) > cMax Then cMax = UBound(arr, 2) ' вычисляем максимальное количество столбцов из всех массивов листов
For r = 1 To UBound(arr, 1) ' цикл по СТРОКАМ ОЧЕРЕДНОГО массива с листа
nR = nR + 1 ' увеличиваем счётчик строк НОВОГО/СБОРНОГО массива
For c = 1 To UBound(arr, 2) ' цикл по СТОЛБЦАМ ОЧЕРЕДНОГО массива с листа
aRes(nR, c) = arr(r, c) ' переписываем очередной элемент из очередного массива в сборник
Next c
Next r
End If
Next sh
Worksheets.Add ' добавляем новый лист
Cells(1, 1).Resize(nR, cMax).Value = aRes ' выгружаем на него только значимую часть нового массива (а не весь массив, объявленный с запасом)
End Sub
'====================================================================================================
вариант переполнения памяти из-за огромного количества данных, в примере никак не обходится. В СВОИХ макросах я выгружаю данные каждого листа сразу на ЛИСТ (и считаю количество строк, т.к. на лист тоже можно только немногим больше миллиона записать). И даже в этом случае, при 200стах столбцах на очередном листе можно переполнить память сразу при попытке получить массив из рабочей области. Особенно на версии Excel x32 (не путать с Win x32 - версия офиса может отличаться от винды) с ограничением используемой оперативной памяти
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Бежим по листам, определяем количество строк на каждом. При этом контролируем общее количество (если перевалило через миллион - пора тормозить). Эти значения можно сохранить в массив (какой-нибудь aRowsCount) для дальнейшего использования. По сумме определяем размерность массива aData для загрузки данных. Далее опять бежим по листам, но уже забирая данные и дополняя массив.
Этот вариант и вариант, предложенный выше, обратно противоположны. Этот быстрее, но "жрет" память по максимуму, Джек предложил вариант медленнее, но меньше забивающий оперативную память.
vikttur, вариант, но, если можно обойтись без 2ух циклов, то стараюсь делать это Если уж и делать второй цикл, то тогда я бы уже и массив массивов собирал, чтобы по листам второй раз не бегать
Fylhtqq, "массив массивов" это не такой же массив, но с большим количеством строк, как вы считаете, а массив, ЭЛЕМЕНТАМИ которого являются другие массивы. Для примера это был бы массив aSh(1 To ActiveWorkbook.WorkSheets.Count)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
vikttur: Этот вариант и вариант, предложенный выше, обратно противоположны. Этот быстрее, но "жрет" память по максимуму, Джек предложил вариант медленнее, но меньше забивающий оперативную память
наоборот — вариант "с запасом" (если ты про КОД) всегда оттяпывает много памяти и может выдать ошибку, где твой метод скальпеля её не выдаст. Плюс, 2 цикла не сильно медленнее будут, зато позволят сформировать итоговый массив точно и без запасов
Но, если ты про "мой" метод выгрузки на лист каждого массива, то всё верно - он заметно медленнее, но и самый надёжный - позволит забить лист "под завязку"
адепт так а сокращение кода не всегда его замедляет Ну а если ты всё также про выгрузку на лист, то надёжность/стабильность/качество результат для меня на 1ом месте, а скорость уже на 2ом
P.S.: вот ты меняешь текст без отметок о редактировании, а мои ответы потом глупо выглядят Хоть цитируй всё подряд
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
vikttur: Бежим … определяем количество строк … Этот вариант и вариант, предложенный выше, обратно противоположны
этот вариант быстрый, незаметно медленнее 1го цикла, зато лишён минуса оттяпывания памяти в сравнении с вариантом "с запасом" "Вариант выше" это вариант, который я предложил автору - вариант с 1 циклом и массивом "с запасом". Плох для больших объёмов, зато короче. Как следует из "сравнения методов" от vikttur, он "вариантом выше" называет "мой" метод "надёжного сбора" - с выгрузкой данных каждого листа на лист-сборник. Этот метод медленее всех, но и надёжный. По надёжности метод vikttur не хуже, однако он будет сложнее в управлении, если данные будем собирать из разных файлов (смежная задача).
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄