Здравствуйте!
Пожалуйста, помогите решить мою задачу. Нужен макрос, который бы собирал данные с листа на новый лист по образцу во вложении.
[вложение удалено администратором]
Для одной модели в столбцах G, R, S одни и те же данные всегда? Например в столбце F несколько данных.
Да, данные всегда одинаковые по модели, кроме столбца F.
Нужно создавать новый лист? Или нужно копировать данные в уже имеющийся лист?
на новый лист
А зачем единицы нужны в столбце A?
Автоматически формируются при выгрузке из какой-то программы. Как я понимаю, они идут между группами ID.
И в столбце B данные отсортированы по алфавиту всегда?
Нет. Порядок произвольный. И в столбце А, и в В.
Можно ведь ориентироваться на столбец M - там всегда будут объединённые ячейки?
Строки, которые имеют одну объединённую ячейку в столбце M относятся к одной группу и эти строки нужно объединить.
Внутри группы данные в столбце B одинаковые?
Да, в столбце М строки, которые имеют объединенную ячейку относят к одной группе (если не считать, что они захватывают и пустые строки, с единицамив столбце А. Их учитывать в сборке не надо).
Для ориентации - столбец А как и столбец В уникальны. Внутри А (и В) одинаковые группы соответственно. По ним можно ориентироваться для объединения.
Да, внутри группы данные в столбце В одинаковые.
Здесь две процедуры. Поместите их в один модуль. Запускайте процедуру "Макрос", вторая процедура будет запускаться процедурой "Макрос".
В файле должен быть лист-шаблон. Макрос на его основе создаёт новый лист. Лист-шаблон можно скрыть. Макрос копирует оформление из второй строки во все остальные.
Макрос. Версия от 29.03.2017 23:28
Sub Макрос()
Dim shSrc As Worksheet, shRes As Worksheet, res()
Dim lr As Long
'1. Отключение монитора, чтобы ускорить работу макроса и чтобы не мерцало.
Application.ScreenUpdating = False
'2. Присваивание имени листу-источнику.
Set shSrc = Worksheets("исходный")
'3. Отображение скрытых строк, чтобы найти последнюю строку.
' Метод End не ищет в скрытых строках.
If shSrc.AutoFilterMode = True Then
shSrc.AutoFilter.ShowAllData
End If
shSrc.Rows.Hidden = False
'4. Обработка данных. Обработанные данные запишутся в массив "res".
Process shSrc, res()
'5. Создание листа-результата. Учитывается, если лист-шаблон будет скрыт.
' Копирование перед, а не сразу после, т.к. если есть скрытые листы,
' то потом сложно найти вставленный лист - нужно анализировать скрытые листы.
Worksheets("шаблон").Copy Before:=shSrc
Set shRes = shSrc.Previous
shRes.Visible = True
shRes.Move After:=shSrc
' Ошибка будет, если уже есть лист с таким именем.
On Error Resume Next
shRes.Name = "Результат"
On Error GoTo 0
'6. Вставка массива "res" на лист-результат.
shRes.Range("A2").Resize(UBound(res, 1), 6).Value = res()
'7. Копирование оформления из строки 2 во все остальные строки.
lr = shRes.UsedRange.Rows.Count
If lr > 2 Then
shRes.Rows(2).Copy
shRes.Rows("2:" & lr).PasteSpecial xlPasteFormats
Application.CutCopyMode = False
shRes.Range("A1").Select
End If
'8. Вкл. монитора.
Application.ScreenUpdating = True
End Sub
Private Sub Process(shSrc As Worksheet, res())
' Обработка данных.
Dim src(), lr As Long, i As Long, r As Long
'1. В столбце B удаление повторов, в столбце F объединение данных в одну ячейку.
' Копирование данных в массив. С массивом быстрее работать, чем с эксель-ячейками.
lr = shSrc.Cells(shSrc.Rows.Count, "B").End(xlUp).Row
src() = shSrc.Range("A1:S" & lr).Value
' В столбце B удаление повторов, в столбце F объединение данных в одну ячейку.
For i = UBound(src, 1) To 3 Step -1
If src(i, 2) = src(i - 1, 2) Then
src(i, 2) = Empty
src(i - 1, 6) = src(i - 1, 6) & ";" & src(i, 6)
End If
Next i
'2. Копирование данных из массива "src" в массив "res".
' Копируются только те строки, у которых не пусто в столбце B.
' Создание ячеек в массиве-результате.
' Создаётся максимально возможное кол-во строк, т.к. заранее не известо, сколько будет данных.
ReDim res(1 To UBound(src, 1), 1 To 6)
For i = 2 To UBound(src, 1)
If src(i, 2) <> "" Then
r = r + 1
res(r, 1) = src(i, 2)
res(r, 2) = src(i, 6)
res(r, 3) = src(i, 7)
res(r, 4) = src(i, 13)
res(r, 5) = src(i, 18)
res(r, 6) = src(i, 19)
End If
Next i
End Sub
[вложение удалено администратором]
Спасибо. Работает, но есть ошибка.
'6. Вставка массива "res" на лист-результат.
shRes.Range("A2").Resize(UBound(res, 1), 6).Value = res()
Есть ли ограничение на количество знаков, помещаемых на лист-результат в столбец В (где выполнено объединение)?
Насколько я вижу, максимально помещается 904 знака. А если требуется больше, то появляется эта ошибка.
Какой текст ошибки?
Я сейчас взял наобум 10 000 символов и вставляю их в ячейку. Ошибки не возникает. Пробовал и в "Excel 2003" это сделать.
Ошибка при вставке данных из vba-массива может быть, если в массиве есть какие-нибудь специфические знаки, например, в начале ячейки есть знак равно.
Чтобы специальные знаки не мешали работе макроса, нужно сделать на листе-результате формат ячеек "Текстовый".
Run-time error "1004":
Application-defined or object defined error
Сделайте на листе-шаблоне формат ячеек "Текстовый" и снова запустите макрос на том файле, который вызывает ошибку.
Если проблема исчезнет. То сделайте текстовый формат в тех столбцах, в которых по вашему мнению, могут быть специфические знаки, а в остальных столбцах сделайте нужный формат.
Обнаружила, что если надо объединить 27 строк, то ошибки нет. Если больше, ошибка.
А текстовый формат сделали? Перейдите на лист-шаблон - выделите все ячейки (ну или столбцы целиком, где находятся данные) - вкладка "Главная" - группа "Число" - в списке выберите "Текстовый".
После этого запустите макрос на том файле, который вызывает ошибку. Осталась ошибка?
Поменяла на текстовый формат и на исходном листе, и на шаблоне - ошибка так и есть.
Специфических знаков не обнаружила нигде.
Во вложении смоделировала ошибку.
[вложение удалено администратором]
Какая у вас версия экселя: 2003, 2007 или другая?
2003. Сейчас попробовала в 2010. Отработал весь файл. Кажется, все верно.
Да, у меня та же ошибка в "Excel 2003". Значит для вашей задачи не подходит "Excel 2003".
В "Excel 2003" максимальное кол-во символов в ячейке должно быть 255 символов.
Если больше, то в самой ячейке текст не видно (видно только в строке формул).
Поэтому если вам нужно видеть, что находится в ячейке, то "Excel 2003" вам не подходит. Если не нужно видеть, то можно попробовать изменить макрос - если вставлять не массив, а из переменной, то ошибки не происходит.
В файле демонстрируется, как видно и как не видно. Чтобы увидеть проблему, файл нужно открывать в "Excel 2003".
[вложение удалено администратором]
1) Мне нравится 2003 Excel, и в нем я постоянно работаю. Но раз этот макрос работает в 2010, то это решает проблему. Буду запускать макрос в 2010, а с результатом работать в 2003.
Главное, что он работает и решает очень нужную мне задачу. За что вам большое спасибо!
2) "В "Excel 2003" максимальное кол-во символов в ячейке должно быть 255 символов."
В общем-то мне не надо видеть. Не предполагается, что этот файл будут "читать глазами". А выборочно , если потребуется, можно прочитать через строку формул.
С другой стороны, "видимость" наверное лишней бы не была. Все зависит от того, насколько вам сложно это доработать. Если сложно, я могу без этого спокойно обойтись.
3) Обнаружила, что в данных бывает заполнен столбец А (например группа из 7 значений), столбец В (также 7 значений), а столбец F содержит меньше значений - 3, 4... (т.е. какие-то ячейки пустые).
Соответственно при объединении появляются ";;;;" вместо пустых ячеек. Например, "Стол 1; стол 2; стол 3;;;;;".
Если можно убрать лишние ";", то было бы удобно.
Эта версия работает в "Excel 2003" (и в других версиях). Также учтёно, если в столбце "F" (на исходном листе) будут пустые ячейки.
В этой версии три процедуры, все их поместите в один модуль и запускайте только процедуру "Макрос".
Макрос
Sub Макрос()
Dim shSrc As Worksheet, shRes As Worksheet, res()
Dim lr As Long
'1. Отключение монитора, чтобы ускорить работу макроса и чтобы не мерцало.
Application.ScreenUpdating = False
'2. Присваивание имени листу-источнику.
Set shSrc = Worksheets("исходный")
'3. Отображение скрытых строк, чтобы найти последнюю строку.
' Метод End не ищет в скрытых строках.
If shSrc.AutoFilterMode = True Then
shSrc.AutoFilter.ShowAllData
End If
shSrc.Rows.Hidden = False
'4. Обработка данных. Обработанные данные запишутся в массив "res".
Process shSrc, res()
'5. Создание листа-результата. Учитывается, если лист-шаблон будет скрыт.
' Копирование перед, а не сразу после, т.к. если есть скрытые листы,
' то потом сложно найти вставленный лист - нужно анализировать скрытые листы.
Worksheets("шаблон").Copy Before:=shSrc
Set shRes = shSrc.Previous
shRes.Visible = True
shRes.Move After:=shSrc
' Ошибка будет, если уже есть лист с таким именем.
On Error Resume Next
shRes.Name = "Результат"
On Error GoTo 0
'6. Вставка массива "res" на лист-результат.
InsertToShRes shRes, res()
'7. Копирование оформления из строки 2 во все остальные строки.
lr = shRes.UsedRange.Rows.Count
If lr > 2 Then
shRes.Rows(2).Copy
shRes.Rows("2:" & lr).PasteSpecial xlPasteFormats
Application.CutCopyMode = False
shRes.Range("A1").Select
End If
'8. Вкл. монитора.
Application.ScreenUpdating = True
End Sub
Private Sub Process(shSrc As Worksheet, res())
' Обработка данных.
Dim src(), arr()
Dim lr As Long, i As Long, r As Long
'1. В столбце B удаление повторов, в столбце F объединение данных в одну ячейку.
' Копирование данных в массив. С массивом быстрее работать, чем с эксель-ячейками.
lr = shSrc.Cells(shSrc.Rows.Count, "B").End(xlUp).Row
src() = shSrc.Range("A1:S" & lr).Value
' В столбце B удаление повторов, в столбце F объединение данных в одну ячейку.
For i = UBound(src, 1) To 3 Step -1
If src(i, 2) = src(i - 1, 2) Then
src(i, 2) = Empty
If src(i, 6) <> "" Then
If src(i - 1, 6) = "" Then
src(i - 1, 6) = src(i, 6)
Else
src(i - 1, 6) = src(i - 1, 6) & ";" & src(i, 6)
End If
End If
End If
Next i
'2. Копирование данных из массива "src" в массив "res".
' Копируются только те строки, у которых не пусто в столбце B.
' Создание ячеек в массиве-результате.
' Создаётся максимально возможное кол-во строк, т.к. заранее не известо, сколько будет данных.
ReDim res(1 To UBound(src, 1), 1 To 6)
For i = 2 To UBound(src, 1)
If src(i, 2) <> "" Then
r = r + 1
res(r, 1) = src(i, 2)
res(r, 2) = src(i, 6)
res(r, 3) = src(i, 7)
res(r, 4) = src(i, 13)
res(r, 5) = src(i, 18)
res(r, 6) = src(i, 19)
End If
Next i
'3. Удаление пустых строк с конца массива "res".
arr() = res()
ReDim res(1 To r, 1 To 6)
For i = 1 To UBound(res, 1)
For j = 1 To 6
res(i, j) = arr(i, j)
Next j
Next i
End Sub
Private Sub InsertToShRes(shRes As Worksheet, res())
' Вставка массива "res" на лист-результат.
' В "Excel 2003" не получается вставить массив, если в ячейке массива много символов.
' Много символов в столбце 2.
Dim arr(), i As Long
'1. Копирование данных во вспомогательный массив.
arr() = res()
'2. Удаление данных в столбце 2.
For i = 1 To UBound(arr, 1)
arr(i, 2) = Empty
Next i
'3. Вставка массива в эксель.
shRes.Range("A2").Resize(UBound(arr, 1), 6).Value = arr()
'4. Вставка столбца 2 по-элементно.
For i = 1 To UBound(res, 1)
shRes.Cells(i + 1, "B").Value = res(i, 2)
Next i
End Sub
Отработало без ошибок.
Спасибо! Вы очень помогли!