Страницы: 1
RSS
VBA. Вытягивание данных с закрытых книг их автоматическая группировка
 
Добрый вечер, уважаемые!

Стоит следующая задача - необходимо выгрузить данные из закрытых книг в целевую книгу. При выгрузке, книга открывается, из нее данные вставляются в строку целевого файла, книга закрывается, после чего в целевом файле происходит смещение на 1 строку вниз и цикл повторяется.
Некоторые книги могут иметь схожее название, отличающееся лишь идентификатором (например, А_2010, А_2011, Б_2010). Необходимо выгрузить данные так, чтобы книги с одинаковым общим названием выгружались в одну строку (смещения не происходило).

Выгрузка у меня реализована должным образом, но вот никак не могу сделать это проклятое объединение строк. Прикрепляю пример того, что нужно получить (при этом суммировать значения объединяемых данных не нужно)
Выглядит это все как задача извлечения уникальных значений массива (я уже вставил макрос, уважаемого The_Prist), но есть одна тонкость - в этом примере исходные данные уже введены, в то время как в моем случае они подгружаются по одному - не знаю как сделать пример, чтобы отражалась именно моя проблема.

В случае необходимости пришлю рабочий файл (здоровенный и туманный)
Спасибо!  
 
Подозреваю что при выгрузке можно запоминать в словаре (кстати и в коллекции тоже можно, но работать труднее) строку каждого такого общего названия, туда и догружать следующие данные.
Но файл совершенно ничем не помогает, его можно не скачивать.
 
Hugo, к сожалению, мне еще не доводилось сталкиваться ни со словарями, ни с коллекциями на практике, но буду копать в этом направлении, спасибо за наводку!

А пока я сделал упрощенный пример-шаблон, выгружающий имена книг, буду благодарен, если кто-нибудь подскажет как советы, ув. Hugo реализуются на практике.

Прилеплены: файл "пример" и три пустых загружаемых файла.
Изменено: cray - 26.04.2016 17:46:29
 
Цитата
cray написал: мне еще не доводилось сталкиваться ... с коллекциями на практике,
См тему, #11. Пример использования коллекции
Согласие есть продукт при полном непротивлении сторон
 
Sanja, я изучил Ваш пример, уже час с ним мучаюсь, пытаясь приладить под свои нужды, но особого понимания не добавилось - в Вашем примере было два сформированных массива, а в моем случае - ни одного (либо один, если за него принять еще пустой массив с выгруженными названиями). А как сделать сверку с названием новой выгружаемой книги с этой коллекцией, как вернуть номер совпадения этого названия (через application.match, же?), и самый главный вопрос, как в итоге прийти к уменьшению смещения в целевой книге с количества открываемых книг на количество их уникальных наименований?

Я не могу понять, какой должен быть алгоритм, снова смиренно прошу помощи.
Изменено: cray - 26.04.2016 19:31:09
 
Так в файле ф.xlsm (18.41 КБ) есть макрос с коллекцией - а говорите "еще не доводилось сталкиваться" :)
По файлам-примерам - если из открываемых ничего не берёте - их и открывать вообще незачем.
В общем пока задача непонятна, делать рано имхо...
Изменено: Hugo - 26.04.2016 19:43:47
 
Hugo, претензия принимается) Уточняю - не доводилось сталкиваться при личном написании кода - чужое-то использовать много ума не требует
Изменено: cray - 26.04.2016 19:59:40
 
На словаре алгритм такой:
1 открыли файл (или получили имя)
2 выделили постоянную составляющую
3 сверились с словарём -
3а если нет в словаре - увеличиваем индекс, пишем в словарь имя и индекс, по индексу пишем данные
3б если есть в словаре - извлекаем из словаря индекс, по индексу пишем данные (в примере просто ничего не делаем, да и соотв. индекс там писать нет нужды)
Всё.
 
Hugo, спасибо, теория стала чуть понятнее, кроме момента
Цитата
Hugo написал:
3 сверились с словарём
Что понимается под словарем в данном контексте? Диапазон уже выгруженных названий?  


А по поводу Вашего замечания по поводу постановки вопроса -
Цитата
Hugo написал:
По файлам-примерам - если из открываемых ничего не берёте - их и открывать вообще незачем.
В общем пока задача непонятна, делать рано имхо...
данные из книг вытаскиваются (в смысле, не из файлов для примера, а из моих рабочих - пример же нужен для упрощения).
Структура рабочих книг такова, что в каждом из них - данные по 2 годам, (например, А_2001 содержит данные за 2000-2001гг).
Целевой же файл содержит таблицу с диапазоном 2000-2014 гг, и в случае, если в конкретной открываемой книге нет данных нужного периода, данные ячейки просто не изменяются.
Сейчас выгрузка происходит "лесенкой", см. пример
При объединении в одну строку данные будут выгружаться корректно (при условии, что необходимую величину смещения получится загнать в переменную)  
 
Словарь - это практически как словарь и есть, возьмите любой с книжной полки и посмотрите :)
Там есть слово-ключ и перевод/ы-значения (key, item)

По задаче/примеру - не вполне понятны детали, но думаю можно делать так:
при переборе файлов как ключ в словарь кладём очередное сочетание название+год, в значение извлечённые из файла данные (или массив, или коллекцию, не суть)
ну и параллельно можно набрать словари или коллекции названий и годов отдельно, если нет уже готового списка/таблицы.
В финале цикл по названиям, в нём цикл по годам - если в словаре есть ключ, то есть и данные, которые можно извлечь на пересечение название/год.
Для ускорения можно всё делать через массивы, но если данных не десятки тысяч - то можно прямо на лист писать.
 
Hugo, после прочтения Вашего сообщения в моей душе поселилась печаль, ибо что нужно сделать - примерно понимаю, а вот как перенести это дело в код - нет.
Не встречалась ли Вам подобная задача с реализацией через эти самые словари/коллекции? Именно подобная, потому что, как показала практика в лице совета ув. Sanja из #4 примеры задач с использованием коллекций особой ясности не вносят.

Тем временем, переспав со своей проблемой, на утро на ум пришел простой вариант ее решения - через введение переменной LastRow и конструкции Select Case. Если у кого возникнет похожая проблема и кто также не разбирается в коллекциях, то я сделал так:
Первый Case - в случае если Application.Match названия новой книги по отношению к уже загруженному диапазону названий = 0 (т.е. название книги встречается впервые), то смещение  по строкам относительно первой ячейки происходит не на величину счетчика количества открываемых файлов, как раньше, а на LastRow (последняя заполненная строка диапазона имен открываемых книг). Дополнительное условие в этом Case - если счетчик открываемых книг = 1 (т.е. открыта самая первая книга), то смещение происходит как раньше - на этот счетчик открытий.
Второй Case -   Application.Match >0 (т.е. название уже встречалось), в котором смещение происходит на LastRow - 1

В принципе, свою задачу я решил и номинально тема закрыта, но мне интересна ее реализация через словари/коллекции. Так что если кто захочет реализовать идею ув. Hugo, буду благодарен. Для этого прикрепляю доделанные файлы-примеры, в которых на этот раз происходит некая выгрузка данных.
 
Очистку диапазона измените сами - тут выгрузка в диапазон I-N, шапка используется (но можно её и генерить, добавив ещё один словарь/коллекцию)
Код
Sub Vlookup2()

Dim WB As Workbook, FileName As String, CompanyName As String, GetFile As FileDialog, y As Long
Dim a, t$, tt$, ttt$, maindic As Object, bookdic As Object, i&, ii&, k

With Application
    .ScreenUpdating = False
    .DisplayAlerts = False
    .EnableEvents = False
End With

Set WB = ThisWorkbook

'блок окна выбора файлов
Set GetFile = Application.FileDialog(msoFileDialogFilePicker)
With GetFile
    .AllowMultiSelect = True
    .Title = "Выберите файлы"
    .Filters.Clear
    .Filters.Add "Excel files", "*.xls*;*.xla*", 1
    .FilterIndex = 1
    .InitialView = msoFileDialogViewDetails
    
    If GetFile.Show = 0 Then Exit Sub
    
    Set maindic = CreateObject("Scripting.Dictionary"): maindic.CompareMode = 1
    Set bookdic = CreateObject("Scripting.Dictionary"): bookdic.CompareMode = 1
    
    t = Trim(Range("B7").Value)

    For y = 1 To .SelectedItems.Count
        FileName = .SelectedItems(y)
        
       With GetObject(FileName)
          CompanyName = Left(.Name, 1)
          bookdic.Item(CompanyName) = 0&
          
          a = .Sheets(1).Range("A1").CurrentRegion.Value
          For i = 2 To UBound(a)
          If a(i, 1) = t Then
          tt = CompanyName & "|" & t
            For ii = 2 To UBound(a, 2)
                ttt = tt & "|" & a(1, ii)
                'Debug.Print ttt & "-" & a(i, ii)
                maindic.Item(ttt) = a(i, ii)
            Next
          End If
          Next
          .Close 0
        End With
          
    Next
End With

'блок выгрузки данных
i = 1
For Each k In bookdic.keys
i = i + 1
Cells(i, 9) = i - 1
Cells(i, 10) = k

For ii = 11 To 14
Cells(i, ii) = maindic.Item(k & "|" & t & "|" & Cells(1, ii))
Next
Next
   
With Application
    .ScreenUpdating = True
    .DisplayAlerts = True
    .EnableEvents = True
End With

MsgBox "Done"
End Sub
Выгрузку можно ускорить, если использовать массив - создать его под уже известный размер, в цикле заполнить, затем массив выгрузить на лист.
Изменено: Hugo - 27.04.2016 13:01:09
 
Hugo, круто!
Ваш код пока слишком сложен для моего уровня знаний, буду изучать, и может быть, годиков эдак через семь смогу сделать что-то наподобие)

Спасибо!
 
Да ладно круто - там вся круть заимствована у Вас :)
Я только добавил словарь - ключ+значение...
Кстати, переменные tt и ttt можно упразднить - тогда вроде и код будет проще выглядеть. Я их добавлял больше для того, чтоб отлаживать используя debug.print (раскомментируйте, может поможет понять что к чему).
Изменено: Hugo - 27.04.2016 13:15:41
Страницы: 1
Читают тему
Наверх