А вот ещё вопрос: В общий файл нужно свести данные из нескольких других файлов(их число заранее неизвестно). В имеющемся макросе, после ввода параметра получается значение, если оно удовлетворяет условию(так же введенному с клавиатуры), то данные переносятся в общий файл. Но данные добавляются, если человек ещё не добавлен в список и подходит по условию.
А как сделать так что бы переносил, только тех, кто во всех файлах удовлетворяет этому условию, а не к каком то одном???? Например, если оба получившихся параметра, у одного человека больше определенного числа, то переносим, если только в одном файле, то нет.
Function GetCritValue(ByRef Cells() As Variant, CurRow As Integer, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Integer Dim ii As Integer Dim x As Integer Dim ILastrow As Integer Dim oDict As Object Dim calc_status& sFolder = ThisWorkbook.Path & "\5\" sFiles = Dir(sFolder & "*.xls") Set oDict = CreateObject("Scripting.Dictionary") oDict.CompareMode = 1 With Application calc_status = .Calculation .Calculation = xlManual .ScreenUpdating = False .EnableEvents = False a = .Sheets(1).UsedRange.Value For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 1 End If Else Exit For End If Next Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value ReDim b(1 To UBound(a), 1 To 4) ii = 0 n = Val(InputBox("Введите число N")) k = Val(InputBox("Введите число которое подходит по условию")) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If oDict.exists(a(i, cnstNom)) And GetCritValue(a, i, n) > k Then oDict.Item(a(i, cnstNom)) = 1 ii = ii + 1 b(ii, 1) = a(i, cnstNom) b(ii, 2) = a(i, cnstName) b(ii, 3) = a(i, cnstGod) b(ii, 4) = cnstBalls End If Else End If Next sFiles = Dir .Close 0 End With If ii > 0 Then ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = b End If Loop .Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
[/vba]
А вот ещё вопрос: В общий файл нужно свести данные из нескольких других файлов(их число заранее неизвестно). В имеющемся макросе, после ввода параметра получается значение, если оно удовлетворяет условию(так же введенному с клавиатуры), то данные переносятся в общий файл. Но данные добавляются, если человек ещё не добавлен в список и подходит по условию.
А как сделать так что бы переносил, только тех, кто во всех файлах удовлетворяет этому условию, а не к каком то одном???? Например, если оба получившихся параметра, у одного человека больше определенного числа, то переносим, если только в одном файле, то нет.
Function GetCritValue(ByRef Cells() As Variant, CurRow As Integer, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Integer Dim ii As Integer Dim x As Integer Dim ILastrow As Integer Dim oDict As Object Dim calc_status& sFolder = ThisWorkbook.Path & "\5\" sFiles = Dir(sFolder & "*.xls") Set oDict = CreateObject("Scripting.Dictionary") oDict.CompareMode = 1 With Application calc_status = .Calculation .Calculation = xlManual .ScreenUpdating = False .EnableEvents = False a = .Sheets(1).UsedRange.Value For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 1 End If Else Exit For End If Next Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value ReDim b(1 To UBound(a), 1 To 4) ii = 0 n = Val(InputBox("Введите число N")) k = Val(InputBox("Введите число которое подходит по условию")) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If oDict.exists(a(i, cnstNom)) And GetCritValue(a, i, n) > k Then oDict.Item(a(i, cnstNom)) = 1 ii = ii + 1 b(ii, 1) = a(i, cnstNom) b(ii, 2) = a(i, cnstName) b(ii, 3) = a(i, cnstGod) b(ii, 4) = cnstBalls End If Else End If Next sFiles = Dir .Close 0 End With If ii > 0 Then ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = b End If Loop .Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
CurRow As Integer Dim i As Integer Dim ii As Integer Dim x As Integer Dim ILastrow As Integer
[/vba] нужно бы поменять на [vba]
Code
As Long
[/vba], а то когда-нибудь на больших таблицах/массивах будет ошибка.
P.S. вот кросс-тема на планете - там и файл есть, хотя это в общем ничего не даёт - я всё равно код не понял (хотя там есть часть написанного мною ) - почему запрос на каждом файле? Да и вообще этот код отличается от того, который в файле на планете - тут например вообще нет заполнения словаря http://www.planetaexcel.ru/forum.php?thread_id=44985
Т.е. я думаю этот код нужно на 90% переделать, на 50 дополнить. На планете я поторопился, передумал - тот алгоритм не пойдёт... Алгоритм значит вижу такой: если нам нужно отобрать только тех, кто во всех файлах соответствует критериям - 1. запрашиваем критерии один раз до перебора файлов 2. перебираем все файлы и данные в них (заодно можно запомнить количество анализированных файлов, если эти данные позже будут нужны) 3. каждого нового работника заносим в словарь (только номер|имя), проверяем по критерию, ставим ему в Item метку true/false. Думаю, самое простое - ставить 0, если не прошёл. Т.е. новому ставим 1, проверяем. Если не прошёл - то ставим 0. Уже существующему проверяем Item - если там уже 0, то дальше не проверяем. Т.е. в итоге все будут в словаре с результатом проверки. 4. после заполнения словаря его перебираем циклом - считаем тех, у кого в Item не 0 - сразу ставим им в Item порядковый номер. 5. объявляем массив для результатов по результату подсчёта в словаре. 6. снова цикл по файлам и данным - теперь сразу у каждого сотрудника проверяем в словаре его Item и по этому индексу копируем данные в массив результата. Если нужны данные только из одного любого файла - то можно сразу ему Item обнулить, чтоб при следующей встрече уже его не брать. Если нужны и другие данные в строку в результат - тут можно использовать подсчитанное общее количество файлов и их имена.
В общем, обычная "человеческая" логика - просмотрели данные всех претендентов, определили тех, у кого нет нигде "проколов" - теперь приглашаем их на собеседование Сразу звать всех, чтоб выгнать неподходящих нельзя - неизвестно, сколько их всего будет, и какое помещение готовить
Заметил косячок в коде: [vba]
Code
CurRow As Integer Dim i As Integer Dim ii As Integer Dim x As Integer Dim ILastrow As Integer
[/vba] нужно бы поменять на [vba]
Code
As Long
[/vba], а то когда-нибудь на больших таблицах/массивах будет ошибка.
P.S. вот кросс-тема на планете - там и файл есть, хотя это в общем ничего не даёт - я всё равно код не понял (хотя там есть часть написанного мною ) - почему запрос на каждом файле? Да и вообще этот код отличается от того, который в файле на планете - тут например вообще нет заполнения словаря http://www.planetaexcel.ru/forum.php?thread_id=44985
Т.е. я думаю этот код нужно на 90% переделать, на 50 дополнить. На планете я поторопился, передумал - тот алгоритм не пойдёт... Алгоритм значит вижу такой: если нам нужно отобрать только тех, кто во всех файлах соответствует критериям - 1. запрашиваем критерии один раз до перебора файлов 2. перебираем все файлы и данные в них (заодно можно запомнить количество анализированных файлов, если эти данные позже будут нужны) 3. каждого нового работника заносим в словарь (только номер|имя), проверяем по критерию, ставим ему в Item метку true/false. Думаю, самое простое - ставить 0, если не прошёл. Т.е. новому ставим 1, проверяем. Если не прошёл - то ставим 0. Уже существующему проверяем Item - если там уже 0, то дальше не проверяем. Т.е. в итоге все будут в словаре с результатом проверки. 4. после заполнения словаря его перебираем циклом - считаем тех, у кого в Item не 0 - сразу ставим им в Item порядковый номер. 5. объявляем массив для результатов по результату подсчёта в словаре. 6. снова цикл по файлам и данным - теперь сразу у каждого сотрудника проверяем в словаре его Item и по этому индексу копируем данные в массив результата. Если нужны данные только из одного любого файла - то можно сразу ему Item обнулить, чтоб при следующей встрече уже его не брать. Если нужны и другие данные в строку в результат - тут можно использовать подсчитанное общее количество файлов и их имена.
В общем, обычная "человеческая" логика - просмотрели данные всех претендентов, определили тех, у кого нет нигде "проколов" - теперь приглашаем их на собеседование Сразу звать всех, чтоб выгнать неподходящих нельзя - неизвестно, сколько их всего будет, и какое помещение готовить Hugo
Словарь один. Код выше можно использовать частично - там есть ошибки, поэтому так, как он написан здесь сейчас, он работать не будет. Вообще тут ещё непонятно одно условие задачи - первая версия, для которой я писал первый код, сперва проверяла уже присутствующих в таблице, и дописывала только новых. Т.е. при повторном запуске лишь дописывала новых. Т.е. если Вам теперь нужно делать проверку на критерий - то нужно понять, что делать с уже записанными ранее. Или их там быть не может, или их стираем и записываем всех с проверкой заново, или работаем как написано. Т.е. если уже Иванов в таблице - тогда его не проверяем вообще (ставим ему в Item 0 и из новых файлов ничего не импортируем).
Если для каждого файла свои условия - ну пусть, можно и так. Но тогда вероятно нужно показать, какой файл сейчас анализируется - иначе как понять, какие условия нужно вводить?
Словарь один. Код выше можно использовать частично - там есть ошибки, поэтому так, как он написан здесь сейчас, он работать не будет. Вообще тут ещё непонятно одно условие задачи - первая версия, для которой я писал первый код, сперва проверяла уже присутствующих в таблице, и дописывала только новых. Т.е. при повторном запуске лишь дописывала новых. Т.е. если Вам теперь нужно делать проверку на критерий - то нужно понять, что делать с уже записанными ранее. Или их там быть не может, или их стираем и записываем всех с проверкой заново, или работаем как написано. Т.е. если уже Иванов в таблице - тогда его не проверяем вообще (ставим ему в Item 0 и из новых файлов ничего не импортируем).
Если для каждого файла свои условия - ну пусть, можно и так. Но тогда вероятно нужно показать, какой файл сейчас анализируется - иначе как понять, какие условия нужно вводить?Hugo
есть одна таблица в которую сводятся данные из других файлов. а уже вот эти другие файлы формируются по разному. в каких то просто надо свести данные, и если нет человека, то добавить нового. а какие то файлы формируются по условию. так вот в этом файле в таблицу добавятся только те люди, у которые в некоторых других файлах удовлетворяют условию. так как данные сводятся за разное время, то условия для каждого файла разные, поэтому их каждый раз нужно водить, для каждого файла.
есть одна таблица в которую сводятся данные из других файлов. а уже вот эти другие файлы формируются по разному. в каких то просто надо свести данные, и если нет человека, то добавить нового. а какие то файлы формируются по условию. так вот в этом файле в таблицу добавятся только те люди, у которые в некоторых других файлах удовлетворяют условию. так как данные сводятся за разное время, то условия для каждого файла разные, поэтому их каждый раз нужно водить, для каждого файла.Рита
Дата: Воскресенье, 09.09.2012, 16:32 |
Сообщение № 7
Группа: Гости
Вот если я правильно поняла: в открывшемся файле производим необходимые вычисления. в итоге, если человека нет в словаре и он удовлетворяет условию, то ставим ему 1, иначе - 0
If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) And GetCritValue(a, i, n) > k Then oDict.Item(a(i, cnstNom)) = 1 Else oDict.Item(a(i, cnstNom)) = 0 End If
Правильно?
Вот если я правильно поняла: в открывшемся файле производим необходимые вычисления. в итоге, если человека нет в словаре и он удовлетворяет условию, то ставим ему 1, иначе - 0
If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) And GetCritValue(a, i, n) > k Then oDict.Item(a(i, cnstNom)) = 1 Else oDict.Item(a(i, cnstNom)) = 0 End If
Дата: Воскресенье, 09.09.2012, 16:33 |
Сообщение № 8
Группа: Гости
или If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) oDict.Item(a(i, cnstNom)) = 1 End If If oDict.Item(a(i, cnstNom)) = 1 And GetCritValue(a, i, n) > k Then oDict.Item(a(i, cnstNom)) = 1 Else oDict.Item(a(i, cnstNom)) = 0 End If
или If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) oDict.Item(a(i, cnstNom)) = 1 End If If oDict.Item(a(i, cnstNom)) = 1 And GetCritValue(a, i, n) > k Then oDict.Item(a(i, cnstNom)) = 1 Else oDict.Item(a(i, cnstNom)) = 0 End IfРита
If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре
If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку oDict.Item(a(i, cnstNom)) = 1 ' ставим 1 Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If
Else 'если есть уже в словаре
If oDict.Item(a(i, cnstNom)) = 1 Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If
End If End If
[/vba] Ну а потом перебираем словарь, всем прошедшим проверку ставим порядковые номера, затем уже из файлов тянем данные в массив по позициям этих номеров. Чуть пока не ясно, что делать с теми, кто уже может быть есть в этом сводном - если будет такой вариант, то это можно доработать. Например , пусть у них в Item уже будут значения, больше 1, и тогда новым будем продолжать ставить номера дальше. Тогда такие с номерами >1 проверяться не будут, а все новые можно будет выгрузить ниже (при копировании данных в массив скорректировать индекс на это макисмальное число уже занесённых клиентов. Либо использовать в начале не 0/1, а -1/0 - т.е. непрошедшим ставим -1, прошедшим 0 (или словами false/true) Затем всем с 0 ставим новые индексы в продолжение таблицы, и всех с такими индексами импортируем при втором проходе по файлам (после первого импорта скидываем Item в 0, чтоб из других файлов больше не брать).
Думаю как-то так: [vba]
Code
If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре
If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку oDict.Item(a(i, cnstNom)) = 1 ' ставим 1 Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If
Else 'если есть уже в словаре
If oDict.Item(a(i, cnstNom)) = 1 Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If
End If End If
[/vba] Ну а потом перебираем словарь, всем прошедшим проверку ставим порядковые номера, затем уже из файлов тянем данные в массив по позициям этих номеров. Чуть пока не ясно, что делать с теми, кто уже может быть есть в этом сводном - если будет такой вариант, то это можно доработать. Например , пусть у них в Item уже будут значения, больше 1, и тогда новым будем продолжать ставить номера дальше. Тогда такие с номерами >1 проверяться не будут, а все новые можно будет выгрузить ниже (при копировании данных в массив скорректировать индекс на это макисмальное число уже занесённых клиентов. Либо использовать в начале не 0/1, а -1/0 - т.е. непрошедшим ставим -1, прошедшим 0 (или словами false/true) Затем всем с 0 ставим новые индексы в продолжение таблицы, и всех с такими индексами импортируем при втором проходе по файлам (после первого импорта скидываем Item в 0, чтоб из других файлов больше не брать).Hugo
Дата: Воскресенье, 09.09.2012, 17:33 |
Сообщение № 10
Группа: Гости
Нет, в данном случае нужно выбрать тех, кто во всех файлах подходит по условию. Главное что бы в список не попали те, которые удовлетворяют условию только 1 раз. Спасибо большое, сейчас по пробую
Нет, в данном случае нужно выбрать тех, кто во всех файлах подходит по условию. Главное что бы в список не попали те, которые удовлетворяют условию только 1 раз. Спасибо большое, сейчас по пробуюРита
Function GetCritValue(ByRef Cells() As Variant, CurRow As Integer, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Integer Dim ii As Integer Dim x As Integer Dim ILastrow As Integer Dim oDict As Object Dim calc_status& sFolder = ThisWorkbook.Path & "\5\" sFiles = Dir(sFolder & "*.xls") Set oDict = CreateObject("Scripting.Dictionary") oDict.CompareMode = 1 With Application calc_status = .Calculation .Calculation = xlManual .ScreenUpdating = False .EnableEvents = False a = .Sheets(1).UsedRange.Value For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 1 End If Else Exit For End If Next Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value ReDim b(1 To UBound(a), 1 To 4) ii = 0 n = Val(InputBox("Введите число N")) k = Val(InputBox("Введите число которое подходит по условию")) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку oDict.Item(a(i, cnstNom)) = 1 ' ставим 1 Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If Else 'если есть уже в словаре If oDict.Item(a(i, cnstNom)) = 1 Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If
If oDict.Item(a(i, cnstNom)) = 1 Then ' если есть в словаре, то переносим остальные значения ii = ii + 1 b(ii, 1) = a(i, cnstNom) b(ii, 2) = a(i, cnstName) b(ii, 3) = a(i, cnstGod) b(ii, 4) = cnstBalls End If End If End If Next sFiles = Dir .Close 0 End With If ii > 0 Then ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = b End If Loop .Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
[/vba]
Вроде работает правльно, но я только на небольшом примере проверяла. но написано как то неудачно:
Function GetCritValue(ByRef Cells() As Variant, CurRow As Integer, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Integer Dim ii As Integer Dim x As Integer Dim ILastrow As Integer Dim oDict As Object Dim calc_status& sFolder = ThisWorkbook.Path & "\5\" sFiles = Dir(sFolder & "*.xls") Set oDict = CreateObject("Scripting.Dictionary") oDict.CompareMode = 1 With Application calc_status = .Calculation .Calculation = xlManual .ScreenUpdating = False .EnableEvents = False a = .Sheets(1).UsedRange.Value For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 1 End If Else Exit For End If Next Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value ReDim b(1 To UBound(a), 1 To 4) ii = 0 n = Val(InputBox("Введите число N")) k = Val(InputBox("Введите число которое подходит по условию")) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку oDict.Item(a(i, cnstNom)) = 1 ' ставим 1 Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If Else 'если есть уже в словаре If oDict.Item(a(i, cnstNom)) = 1 Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If
If oDict.Item(a(i, cnstNom)) = 1 Then ' если есть в словаре, то переносим остальные значения ii = ii + 1 b(ii, 1) = a(i, cnstNom) b(ii, 2) = a(i, cnstName) b(ii, 3) = a(i, cnstGod) b(ii, 4) = cnstBalls End If End If End If Next sFiles = Dir .Close 0 End With If ii > 0 Then ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = b End If Loop .Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
Я Ваш код под спойлер спрятал, а то страницу мотать долго... Проверить код не могу - не знаю критериев Но вижу логическую ошибку - надо ведь файлы по два раза перебирать, т.к. пока не просмотришь все, не известно - пройдёт человек все проверки или нет. Или можно заносить в массив всех прошедших сразу проверку (как у Вас), а если позже придётся в Item поставить 0 - то и из массива их надо будет удалить. Можно просто стереть все данные, а перед ваыгрузкой переложить оставшихся в другой массив (уплотнить) и выгрузить уже его. Так кстати даже будет лучше, быстрее. И этот код тогда особо менять не нужно, просто чуть доработать: там, где [vba]
Code
oDict.Item(a(i, cnstNom)) = 0 ' иначе 0
[/vba] нужно добавить "чистку" массива b, а перед выгрузкой его "уплотнение". Уже стал так пробовать делать - не пойдёт! Мы ведь до просмотра всех файлов не знаем, сколько будет "клиентов", и соотв. не знаем, какой массив b нам нужен. Разве что задать его заведомо с запасом, например на 1000 (или 10000, смотря какая фирма) Но я этого не знаю...
Хотя сделал ка зачёркнуто - вот попробуйте на 1000 клиентов. Писал без проверки, так что результат не знаю...
Function GetCritValue(ByRef Cells() As Variant, CurRow As Long, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function
Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Long Dim ii As Long Dim x As Long Dim p As Long Dim ILastrow As Long Dim oDict As Object Dim calc_status& sFolder = ThisWorkbook.Path & "\5\" sFiles = Dir(sFolder & "*.xls") Set oDict = CreateObject("Scripting.Dictionary") oDict.CompareMode = 1 With Application calc_status = .Calculation .Calculation = xlManual .ScreenUpdating = False .EnableEvents = False a = .Sheets(1).UsedRange.Value
For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 0 p = i - 4 'считаем присутствующих End If Else Exit For End If Next
ReDim b(1 To 1000, 1 To 4)
Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value n = Val(InputBox("Открыт " & sFiles & " Введите число N")) k = Val(InputBox("Введите число которое подходит по условию для " & sFiles)) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку p = p + 1 oDict.Item(a(i, cnstNom)) = p ' ставим p 'заполняем массив данными b(p, 1) = a(i, cnstNom) b(p, 2) = a(i, cnstName) b(p, 3) = a(i, cnstGod) b(p, 4) = cnstBalls Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If Else 'если есть уже в словаре If oDict.Item(a(i, cnstNom)) <> 0 Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл b(oDict.Item(a(i, cnstNom)), 1) = "" oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If End If End If Next sFiles = Dir .Close 0 End With Loop
ReDim c(1 To 1000, 1 To 4) For i = 1 To 1000 If Len(b(i, 1)) Then ii = ii + 1 For x = 1 To 4: c(ii, x) = b(i, x): Next End If Next
If ii > 0 Then ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = c End If
.Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
[/vba]
Я Ваш код под спойлер спрятал, а то страницу мотать долго... Проверить код не могу - не знаю критериев Но вижу логическую ошибку - надо ведь файлы по два раза перебирать, т.к. пока не просмотришь все, не известно - пройдёт человек все проверки или нет. Или можно заносить в массив всех прошедших сразу проверку (как у Вас), а если позже придётся в Item поставить 0 - то и из массива их надо будет удалить. Можно просто стереть все данные, а перед ваыгрузкой переложить оставшихся в другой массив (уплотнить) и выгрузить уже его. Так кстати даже будет лучше, быстрее. И этот код тогда особо менять не нужно, просто чуть доработать: там, где [vba]
Code
oDict.Item(a(i, cnstNom)) = 0 ' иначе 0
[/vba] нужно добавить "чистку" массива b, а перед выгрузкой его "уплотнение". Уже стал так пробовать делать - не пойдёт! Мы ведь до просмотра всех файлов не знаем, сколько будет "клиентов", и соотв. не знаем, какой массив b нам нужен. Разве что задать его заведомо с запасом, например на 1000 (или 10000, смотря какая фирма) Но я этого не знаю...
Хотя сделал ка зачёркнуто - вот попробуйте на 1000 клиентов. Писал без проверки, так что результат не знаю...
Function GetCritValue(ByRef Cells() As Variant, CurRow As Long, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function
Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Long Dim ii As Long Dim x As Long Dim p As Long Dim ILastrow As Long Dim oDict As Object Dim calc_status& sFolder = ThisWorkbook.Path & "\5\" sFiles = Dir(sFolder & "*.xls") Set oDict = CreateObject("Scripting.Dictionary") oDict.CompareMode = 1 With Application calc_status = .Calculation .Calculation = xlManual .ScreenUpdating = False .EnableEvents = False a = .Sheets(1).UsedRange.Value
For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 0 p = i - 4 'считаем присутствующих End If Else Exit For End If Next
ReDim b(1 To 1000, 1 To 4)
Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value n = Val(InputBox("Открыт " & sFiles & " Введите число N")) k = Val(InputBox("Введите число которое подходит по условию для " & sFiles)) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку p = p + 1 oDict.Item(a(i, cnstNom)) = p ' ставим p 'заполняем массив данными b(p, 1) = a(i, cnstNom) b(p, 2) = a(i, cnstName) b(p, 3) = a(i, cnstGod) b(p, 4) = cnstBalls Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If Else 'если есть уже в словаре If oDict.Item(a(i, cnstNom)) <> 0 Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл b(oDict.Item(a(i, cnstNom)), 1) = "" oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If End If End If Next sFiles = Dir .Close 0 End With Loop
ReDim c(1 To 1000, 1 To 4) For i = 1 To 1000 If Len(b(i, 1)) Then ii = ii + 1 For x = 1 To 4: c(ii, x) = b(i, x): Next End If Next
If ii > 0 Then ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = c End If
.Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
можно использовать ReDim Preserve но, т.к. для двумерных размеров можно менять только вторую размерность, то нужна маленькая хитрость - положить массив "набок".
первый раз: [vba]
Code
Redim b(1 to 4, 1 to 1000)
[/vba]
при достижении конца массива, добавляем, к примеру, еще 1000: [vba]
Code
Redim Preserve b(1 to 4, 1 to ubound(b,2)+1000)
[/vba]
и так далее.
можно использовать ReDim Preserve но, т.к. для двумерных размеров можно менять только вторую размерность, то нужна маленькая хитрость - положить массив "набок".
первый раз: [vba]
Code
Redim b(1 to 4, 1 to 1000)
[/vba]
при достижении конца массива, добавляем, к примеру, еще 1000: [vba]
Да, я как-то избегаю preserve, но тут оно в любом случае будет быстрее, чем два раза файлы читать. С другой стороны, если "столбцов" всего 4, то можно эти данные сразу в item и держать, в строке. Или туда каждому массив поместить, на 4 значения. Ведь у нас словарь клиентов уже есть, где держим индекс "привязанного" массива - так вот переносим данные сразу в словарь, и массив не нужен. Ну а при выгрузке всё равно данные приходится перекладывать (заодно и массив можно перевернуть без transpose). А для метки годится/брак можно в этом массиве иметь пятое поле (это если не хотим совсем данные выкидывать), или просто вместо массива кладём 0 (если данные и клиент больше не нужны). В общем, можно ещё над кодом поработать и сделать его лучше - главное понять, как оно работает, и что именно нам нужно в итоге получить.
P.S. С утра посмотрел - строка [vba]
Code
p = i - 4 'считаем присутствующих
[/vba] лишняя. Как-то я вероятно хотел это употребить позже - но не употребил Её можно смело убрать/закомментировать - на результат не влияет.
Переделал код на "без массива" - теперь у каждого свой маленький массив сразу в словаре.
Function GetCritValue(ByRef Cells() As Variant, CurRow As Long, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function
Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Long Dim ii As Long Dim x As Long Dim el Dim ILastrow As Long Dim oDict As Object Dim calc_status& Dim ForItArr(1 To 4) Dim ItArr()
For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 0 End If Else Exit For 'если данных ниже нет(UsedRange ещё может продолжаться) End If Next
Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value n = Val(InputBox("Открыт " & sFiles & " Введите число N")) k = Val(InputBox("Введите число которое подходит по условию для " & sFiles)) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку 'делаем чистый массив ItArr = ForItArr 'заполняем массив данными ItArr(1) = a(i, cnstNom) ItArr(2) = a(i, cnstName) ItArr(3) = a(i, cnstGod) ItArr(4) = cnstBalls 'помещаем массив в словарь oDict.Item(a(i, cnstNom)) = ItArr
Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If Else 'если есть уже в словаре If IsArray(oDict.Item(a(i, cnstNom))) Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If End If End If Next sFiles = Dir .Close 0 End With Loop ReDim b(1 To oDict.Count, 1 To 4) For Each el In oDict.keys If IsArray(oDict.Item(el)) Then ii = ii + 1 For x = 1 To 4: b(ii, x) = oDict.Item(el)(x): Next End If Next If ii > 0 Then 'если есть что выгружать ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = b End If .Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub
[/vba]
Да, я как-то избегаю preserve, но тут оно в любом случае будет быстрее, чем два раза файлы читать. С другой стороны, если "столбцов" всего 4, то можно эти данные сразу в item и держать, в строке. Или туда каждому массив поместить, на 4 значения. Ведь у нас словарь клиентов уже есть, где держим индекс "привязанного" массива - так вот переносим данные сразу в словарь, и массив не нужен. Ну а при выгрузке всё равно данные приходится перекладывать (заодно и массив можно перевернуть без transpose). А для метки годится/брак можно в этом массиве иметь пятое поле (это если не хотим совсем данные выкидывать), или просто вместо массива кладём 0 (если данные и клиент больше не нужны). В общем, можно ещё над кодом поработать и сделать его лучше - главное понять, как оно работает, и что именно нам нужно в итоге получить.
P.S. С утра посмотрел - строка [vba]
Code
p = i - 4 'считаем присутствующих
[/vba] лишняя. Как-то я вероятно хотел это употребить позже - но не употребил Её можно смело убрать/закомментировать - на результат не влияет.
Переделал код на "без массива" - теперь у каждого свой маленький массив сразу в словаре.
Function GetCritValue(ByRef Cells() As Variant, CurRow As Long, n As Variant) As Double If n = 0 Then GetCritValue = 0 Else If Cells(CurRow, cnstGod) > 0 Then GetCritValue = Cells(CurRow, cnstSum) / Cells(CurRow, cnstGod) / n Else GetCritValue = 0 End If End If End Function
Sub Test() Dim Book As Excel.Workbook Dim Sheet As Excel.Worksheet Dim sFolder As String Dim sFiles As String Dim a() As Variant Dim i As Long Dim ii As Long Dim x As Long Dim el Dim ILastrow As Long Dim oDict As Object Dim calc_status& Dim ForItArr(1 To 4) Dim ItArr()
For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then oDict.Item(a(i, cnstNom)) = 0 End If Else Exit For 'если данных ниже нет(UsedRange ещё может продолжаться) End If Next
Do While sFiles <> "" With GetObject(sFolder & sFiles) a = .Sheets(1).UsedRange.Value n = Val(InputBox("Открыт " & sFiles & " Введите число N")) k = Val(InputBox("Введите число которое подходит по условию для " & sFiles)) For i = 5 To UBound(a) If Len(a(i, cnstNom)) Then If Not oDict.exists(a(i, cnstNom)) Then 'если нет в словаре If GetCritValue(a, i, n) > k Then 'если прошёл сейчас проверку 'делаем чистый массив ItArr = ForItArr 'заполняем массив данными ItArr(1) = a(i, cnstNom) ItArr(2) = a(i, cnstName) ItArr(3) = a(i, cnstGod) ItArr(4) = cnstBalls 'помещаем массив в словарь oDict.Item(a(i, cnstNom)) = ItArr
Else oDict.Item(a(i, cnstNom)) = 0 ' иначе 0 End If Else 'если есть уже в словаре If IsArray(oDict.Item(a(i, cnstNom))) Then 'если все предыдущие проверки прошёл If Not GetCritValue(a, i, n) > k Then ' проверяем снова, если не прошёл oDict.Item(a(i, cnstNom)) = 0 ' ставим 0 End If End If End If End If Next sFiles = Dir .Close 0 End With Loop ReDim b(1 To oDict.Count, 1 To 4) For Each el In oDict.keys If IsArray(oDict.Item(el)) Then ii = ii + 1 For x = 1 To 4: b(ii, x) = oDict.Item(el)(x): Next End If Next If ii > 0 Then 'если есть что выгружать ILastrow = Cells(Rows.Count, 5).End(xlUp).Row Cells(ILastrow + 1, cnstNom).Resize(ii, 4) = b End If .Calculation = calc_status .ScreenUpdating = True .EnableEvents = True n = 1 For RowNo = 4 To Range("B4").SpecialCells(xlLastCell).Row - 1 Лист1.Cells(RowNo + 1, 1) = n n = n + 1 Next RowNo End With End Sub