Страницы: 1
RSS
Как узнать количество не пустых элементов в массиве
 
Добрый день!    
Имеется динамический массив Mass(), размер которого меняется (цикл). Из данного массива некоторые элементы попадают в динамический массив mass2(). Как узнать кол-во элементов в массиве mass2()?    
как вариант можно выгружать на лист и оттуда снова вносить в массив. Но хотелось бы узнать, есть ли другие методы.  
 
Читал http://www.planetaexcel.ru/forum.php?thread_id=8548, там описано про коллекции, но я не понял как с ними работать. Хотелось бы пример.
 
>>> Хотелось бы пример.  
 
умгу...    
хотелось бы.    
ваш пример и зачем оно вам надо выгружать и менять.
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
Если кратенько. Есть статистика за несколько лет, собраная в книгах (срезы за период). Её необходимо обработать, для этого нужно объединять несколько книг в одну. Часть данных переносится (типа "название" и т.п.), часть суммируется(сумма). Проблема в том что эксель 2003. И просто собрать данные на 1 лист я не могу. делаю следующее:  
1. Открываю книги по очереди  
2. Лист в массив загоняется  
3. Массив обрабатывается, в результате имеем динамический массив  с невыясненым кол-вом элементов  
4. Эти массивы объединяются в один и вставляются в книгу. И тут желательно, чтобы пустых строк не было, т.к. не влезет и некрасиво.  
Файл прилагаю. Его можно размножить (копированием) и посмотреть итог. код:  
 
Sub массив3()  
Dim aa(), bb() As Variant, cc() As Variant, dd(), ee(), gg(), hh(), ll(), a%, b%, c%, e%, f%, g%, h%  
ReDim dd(1 To 1000, 1 To 4)  
h = 1  
With Application  
v = .GetOpenFilename("Excel Files (*.xl*),*.xl*,All Files (*.*),*.*", , "Выберите файлы", , True)  
If Not IsArray(v) Then Exit Sub  
.EnableEvents = False  
.ScreenUpdating = False  
.DisplayAlerts = False  
   For Each x In v  
   Application.Workbooks.Open (x)  
       d = 1  
 
         
           aa = [a1].CurrentRegion.Value
           cc = [a1].CurrentRegion.Value
           ReDim bb(1 To UBound(aa), 1 To 4)  
               For a = LBound(aa) To UBound(aa)  
                   For b = a + 1 To UBound(aa)  
                       If aa(a, 1) = aa(b, 1) And aa(a, 2) = aa(b, 2) Then aa(b, 1) = ""  
                   Next b  
               Next a  
       For c = LBound(aa) To UBound(aa)  
           If aa(c, 1) <> "" Then: bb(d, 1) = aa(c, 1): bb(d, 2) = aa(c, 2): d = d + 1  
       Next c  
   For e = LBound(bb) To UBound(bb)  
       For f = LBound(cc) To UBound(cc)  
           If (bb(e, 1) = cc(f, 1)) And (bb(e, 2) = cc(f, 2)) Then bb(e, 3) = cc(f, 3): bb(e, 4) = bb(e, 4) + cc(f, 4)  
       Next f  
   Next e  
    ActiveWorkbook.Save  
ActiveWorkbook.Close  
   .Workbooks.Add  
   [a1:d1].Resize(UBound(bb)) = bb
   ee = [a1].CurrentRegion.Value
   ActiveWorkbook.Close  
      For g = LBound(ee) To UBound(ee)  
     dd(h, 1) = ee(g, 1)  
     dd(h, 2) = ee(g, 2)  
     dd(h, 3) = ee(g, 3)  
     dd(h, 4) = ee(g, 4)  
     h = h + 1  
       Next g  
         
Next  
 
.Workbooks.Add  
 
[a1:d1].Resize(UBound(dd)) = dd
 
d5 = 1  
 
 
gg = [a1].CurrentRegion.Value
ll = [a1].CurrentRegion.Value
ReDim hh(1 To UBound(gg), 1 To 4)  
For a5 = LBound(gg) To UBound(gg)  
   For b5 = a5 + 1 To UBound(gg)  
       If gg(a5, 1) = gg(b5, 1) And gg(a5, 2) = gg(b5, 2) Then gg(b5, 1) = ""  
   Next b5  
Next a5  
   For c5 = LBound(gg) To UBound(gg)  
           If gg(c5, 1) <> "" Then: hh(d5, 1) = gg(c5, 1): hh(d5, 2) = gg(c5, 2): d5 = d5 + 1  
   Next c5  
For e5 = LBound(hh) To UBound(hh)  
For f5 = LBound(ll) To UBound(ll)  
   If (hh(e5, 1) = ll(f5, 1)) And (hh(e5, 2) = ll(f5, 2)) Then hh(e5, 3) = ll(f5, 3): hh(e5, 4) = hh(e5, 4) + ll(f5, 4)  
Next f5  
Next e5  
.Workbooks.Add  
[a1:d1].Resize(UBound(hh)) = hh
End With  
End Sub
 
а если собрать все листы из разных книг в одну книгу (НЕ на один лист), а потом применить к этому что-то вроде этого: http://www.planetaexcel.ru/tip.php?aid=233
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
Какая необычная идея. Подумаю как её лучше реализовать.
 
кстати, результаты SQL-запроса совсем необязательно запихивать в кэш сводной.  
его можно просто выгрузить на лист - конечно, если строк хватит.  
и, конечно же, в SQL-запросе можно применять отборы, группировки и функции-агрегаты.
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
Если данные такие, как в примере - то нужен один временный массив для данных (чтоб лист не перебирать) и словарь для ключей и сумм. Можно в item словаря тоже класть небольшой массив - если сумм больше чем одна.  
Никакой заботы о размере массива - Вы уже освоили, как брать данные с листа в массив.  
Перебираем файлы, в каждом первый попавшийся лист(!) берём в массив, цикл по массиву, собираем в словаре ключи и суммы.  
Когда всё закончено - выгружаем словарь на лист.  
Можно сперва в массив, потом массив на лист.  
Быстро, простого кода строк так 20-30, никаких циклов в цикле.  
Думаю Вам стоит изучить словарь.
 
Вот, проверьте - кода 32 строки, если удалить ненужные :)  
 
 
Option Explicit  
 
Sub massH()  
   Dim a(), v, i&, x, y As Byte, oDict As Object, t$, arr, kk  
 
   With Application  
       v = .GetOpenFilename("Excel Files (*.xl*),*.xl*,All Files (*.*),*.*", , "Выберите файлы", , True)  
       If Not IsArray(v) Then Exit Sub  
       .EnableEvents = False: .ScreenUpdating = False: .DisplayAlerts = False  
   End With  
 
   With CreateObject("Scripting.Dictionary")  
       .CompareMode = 1  
 
       For Each x In v  
           With Workbooks.Open(x)  
               a = .Sheets(1).[a1].CurrentRegion.Value
               .Close 0  
           End With  
 
           For i = LBound(a) To UBound(a)  
               t = a(i, 1) & "|" & a(i, 2) & "|" & a(i, 3)  
               If IsNumeric(a(i, 4)) Then .Item(t) = .Item(t) + a(i, 4) Else .Item(t) = a(i, 4)  
           Next  
       Next  
 
       ReDim a(1 To .Count, 1 To 4): i = 0  
       For Each kk In .keys  
           arr = Split(kk, "|")  
           i = i + 1  
           For y = 0 To 2: a(i, y + 1) = arr(y): Next  
           If IsNumeric(.Item(kk)) Then a(i, 4) = --.Item(kk) Else a(i, 4) = .Item(kk)  
       Next  
   End With  
 
   Workbooks.Add(1).Sheets(1).[a1].Resize(i, 4) = a
 
   With Application  
       .EnableEvents = True: .ScreenUpdating = True: .DisplayAlerts = True  
   End With  
 
End Sub  
 
 
Данные беру с первого листа открываемых книг - можно указать другой или уж ладно, активный - но я решил не пускать на самотёк.  
Ну и в конце включил, что в начале поотключали - это кстати добавило 3 строки :)
 
Да, забыл - если видим, что на один лист итоговая выгрузка не лезет (а это видно по  .Count), то данные из словаря легко можно раскидать по массивам/листам/книгам, код простейший.
 
Сравнил скорость (обработка трёх одинаковых файлов):  
 
L: 1,421875  
L: 1,953125  
L: 1,921875  
 
H: 1,0625  
H: 1,0625  
H: 1,171875
 
Забыл из кода убрать объявление  
oDict As Object  
Это было в первой версии, теперь не нужно - сотрите :(
 
>>>Забыл из кода убрать объявление  
Мешать будет? :)
Киса, я хочу Вас спросить, как художник — художника: Вы рисовать умеете?
 
Мешать не будет, но может сбивать с толку :)
 
{quote}{login=leo}{date=20.06.2012 06:57}{thema=}{post}Файл прилагаю. Его можно размножить (копированием) и посмотреть итог. код...{/post}{/quote}  
Код отдельно, файл отдельно... У них взаимная неприязнь?  
Зачем загромождать тему длинным листингом?
 
Огромное спасибо, Hugo! Словари рулят:)Производительность всегда в почете. Все разобрал, вот только в коде есть строчка:  
For Each kk In .keys      в ней CreateObject("Scripting.Dictionary").keys - это сам уже сформированный словарь?
 
{quote}{login=vikttur}{date=21.06.2012 02:58}{thema=Re: }{post}Зачем загромождать тему длинным листингом?{/post}{/quote}  
Спасибо, учту.
 
Да. Там выше, в самом начале есть строка  
With CreateObject("Scripting.Dictionary")  
 
Так вот, эта точка в .keys и есть тот словарь :)  
Ну как всегда - у Вас ведь  тоже With использовалось, правда маловато... :)
 
Спасибо:) Теперь буду словари осваивать.
 
{quote}{login=Hugo}{date=20.06.2012 08:51}{thema=}{post}Если данные такие, как в примере - то нужен один временный массив для данных (чтоб лист не перебирать) и словарь для ключей и сумм. Можно в item словаря тоже класть небольшой массив - если сумм больше чем одна.  
Думаю Вам стоит изучить словарь.{/post}{/quote}  
А как в item положить массив n-го размера, если сумма не одна?
 
Можно так - но тут довольно сложно и с оговорками (есть одно тонкое место) по причине обработки первой строки:  
 
Sub massH2()  
   Dim a(), i&, y As Byte, oDict As Object, t$, arr, kk  
   Dim tarr(1 To 2), b  
 
   a = Sheets(1).[a1].CurrentRegion.Value
 
   With CreateObject("Scripting.Dictionary")  
       .CompareMode = 1  
 
 
       For i = LBound(a) To UBound(a)  
           t = a(i, 1) & "|" & a(i, 2) & "|" & a(i, 3)  
           If IsNumeric(a(i, 4)) Then  
               If Not .exists(t) Then  
                   b = tarr  
                   b(1) = a(i, 4)  
                   b(2) = a(i, 5)  
                   .Item(t) = b  
               Else  
                   b = .Item(t)  
                   b(1) = b(1) + a(i, 4)  
                   b(2) = b(2) + a(i, 5)  
                   .Item(t) = b  
               End If  
           Else  
               b = tarr  
               b(1) = a(i, 4)  
               b(2) = a(i, 5)  
               .Item(t) = b  
           End If  
       Next  
 
       ReDim a(1 To .Count, 1 To 5): i = 0  
       For Each kk In .keys  
           arr = Split(kk, "|")  
           i = i + 1  
           For y = 0 To 2: a(i, y + 1) = arr(y): Next  
           If IsNumeric(.Item(kk)(1)) Then a(i, 4) = --.Item(kk)(1) Else a(i, 4) = .Item(kk)(1)  
           If IsNumeric(.Item(kk)(2)) Then a(i, 5) = --.Item(kk)(2) Else a(i, 5) = .Item(kk)(2)  
       Next  
   End With  
 
   Workbooks.Add(1).Sheets(1).[a1].Resize(i, 5) = a
 
End Sub  
 
Сейчас соображу попроще, без "суммирования" заголовка...
 
Ну вот так.  
tarr() отменил, не нужен. В первом варианте тоже можно было без него обойтись...  
 
Sub massH3()  
   Dim a(), i&, y As Byte, oDict As Object, t$, arr, kk  
 
   a = Sheets(1).[a1].CurrentRegion.Value
 
   With CreateObject("Scripting.Dictionary")  
       .CompareMode = 1  
 
       For i = LBound(a) + 1 To UBound(a)  
           t = a(i, 1) & "|" & a(i, 2) & "|" & a(i, 3)  
           If Not .exists(t) Then  
               'если нет в словаре, то создаём новый массив  
               ReDim b(1 To 2)  
               b(1) = a(i, 4)  
               b(2) = a(i, 5)  
               'помещаем его в словарь  
               .Item(t) = b  
           Else  
               'если уже есть в словаре,  
               'то извлекаем массив из словаря  
               b = .Item(t)  
               'дополняем его  
               b(1) = b(1) + a(i, 4)  
               b(2) = b(2) + a(i, 5)  
               'помещаем его в словарь  
               .Item(t) = b  
           End If  
       Next  
 
       ReDim a(1 To .Count + 1, 1 To 5): i = 0  
       i = i + 1  
       a(i, 1) = "код1": a(i, 2) = "код2": a(i, 3) = "название": a(i, 4) = "Сумма": a(i, 5) = "Сумма2"  
 
       For Each kk In .keys  
           arr = Split(kk, "|")  
           i = i + 1  
           For y = 0 To 2: a(i, y + 1) = arr(y): Next  
           'в 4ый и 5ый элемент помещаем суммы из массива из Item  
           a(i, 4) = --.Item(kk)(1): a(i, 5) = --.Item(kk)(2)  
       Next  
   End With  
 
   Workbooks.Add(1).Sheets(1).[a1].Resize(i, 5) = a
 
End Sub
 
Класс! Я и не думал, что можно вот так с массивом))) Огромное спасибо!
Страницы: 1
Читают тему
Наверх