Страницы: 1 2 След.
RSS
Проверка словаря на наличие ключа, корректировка кода в цикле
 
Уважаемые гуру форума, помогите, пожалуйста, исправить ошибку, возникающую в цикле. Скорее даже не ошибку, код отрабатывает как ему указано, скорее недоработку условий цикла. Итак, имеются 3 листа (Приход, Расход, Выплаты комитентам) по данным которых мне требуется получить сумму задолженности перед комитентом. Пытаюсь сделать (по кнопке "Новый")я это так: Сначала по данным расхода формирую массив (договор и кол-во) всех возвращенных товаров (по которые не требуется проводить выплаты):
Код
        With CreateObject("Scripting.Dictionary"): .CompareMode = 1 'формирование словаря по данным исходного массива
            Arr_VK = Sheets("Выплаты комитентам").Range("B8:D" & Sheets("Выплаты комитентам").Cells(Rows.Count, 4).End(xlUp).Row).Value
            y = Sheets("Приход").Range("B2:J" & Sheets("Приход").Cells(Rows.Count, 3).End(xlUp).Row).Value
            x = Sheets("Расход").Range("B2:H" & Sheets("Расход").Cells(Rows.Count, 5).End(xlUp).Row).Value
                
                For i = 1 To UBound(x) 'отбираем данные расхода
                    temp = Split(x(i, 1), "|")(1) 'договор
                        If x(i, 6) = "В" Then
                            .Item(temp) = .Item(temp) + x(i, 3)
                        End If
                Next i
                    
                    ReDim Arr_R(1 To .Count, 1 To 2) 'формируем массив
                    For Each k In .Keys
                        j = j + 1
                        Data = Split(k, "|")
                        Arr_R(j, 1) = CStr(Data(0)) 'договор
                        Arr_R(j, 2) = Split(.Item(k), "-")(0) 'итоговое кол-во
                    Next k
...
здесь все ОК. Далее я пытаюсь сравнить данные Прихода с полученным массивом и при нахождении совпадающего договора, вычитать из общего кол-ва кол-во возвращенного:    
Код
   ...     
.RemoveAll
                For i = 1 To UBound(y) 'отбираем данные прихода
                    temp = y(i, 1) & "|" & y(i, 3) 'номер договора|комитент
                        For j = 1 To UBound(Arr_R)
                            If y(i, 1) = Arr_R(j, 1) Then 'если совпадает номер договора
                                If y(i, 6) - Arr_R(j, 2) > 0 Then 'если исходное кол-во было больше возвращенного
                                    .Item(temp) = .Item(temp) + (y(i, 6) - Arr_R(j, 2)) * y(i, 8): Exit For 'в словарь: договор|комитент + (кол-во - возврат)*цену
                                End If
                            Else
                                .Item(temp) = .Item(temp) + y(i, 6) * y(i, 8): Exit For 'в словарь: договор|комитент + кол-во*цену
                            End If
                        Next j
                Next i
и вот тут и начинается моя недоработка. В принципе, при пошаговой проверке, я вижу место ее возникновения:
Код
If y(i, 1) = Arr_R(j, 1) Then 
Если при сверке данных листа и массива "возвратный" договор стоит первым, то для него код отработает правильно, а если нет, то в соответствии с моим кодом отработает действие  по ELSE. А требуется (в моем понимании) следующее: Если номер договора НЕ СОВПАДАЕТ, то сначала должен отработать цикл на проверку этого номера среди всего массива Arr_R  и только потом, в случае ненахождения, отработать действия по ELSE. А вот как это сделать я не могу понять. В итоге данные в столбце G (выгрузка прописана ниже в коде) должны совпадать с данными столбца D.
P.S. В принципе, все вышеописанное, является попыткой оптимизировать рабочий код (кнопка "Текущий") в котором по ходу выполнения формируются и сравниваются 4 массива (а в моей попытке я пытаюсь свести это количество к 2-м). Отсюда возникает дополнительный вопрос: А будет ли этот новый код (в случае исправления ошибки) оптимизацией, или грамотнее оставить текущий вариант? Заранее спасибо всем откликнувшихся.
Изменено: OlegO - 30.03.2017 09:27:29
 
Не могу скачать файл, поэтому в задачу не вникаю.
Но по первой части кода есть замечание - строку 7 есть смысл перенести после строки 9 - зачем тратить время на иногда (или часто) ненужную работу?
 
Спасибо за отклик, Hugo. Логику переноса строки понял, действительно, зачем высчитывать temp, если данные могут не потребоваться. Исправлю. По поводу сроков основного вопроса, то они совсем не горят,  поэтому буду рад Вашей консультации в любом обозримом будущем :)
 
Hugo, если Вы не можете скачать файл из-за каких-либо ограничений по расширениям файла, то только укажите требуемое ;)
 
Не, у меня тут админы суровые... :(
Да и вообще сейчас вникать некогда, вечером может гляну если решения ещё не будет.
Изменено: Hugo - 30.03.2017 10:32:35
 
Вот в этом цикле: For Each k In .Keys
у Вас ключи вида "693." "703."
Что хотите получить вот здесь  Data = Split(k, "|") ?
Или здесь: Split(.Item(k), "-")(0) - элемент ключа - количество, там нет "-"
 
Hugo, как я писал выше, буду ждать.
nilem, немного Вас не понял, не могли ли бы Вы указать номер строки? И еще, данные ведь считаются (только иногда неверно ;) ), просто суммы не те. Смотрите, начинается проверка на совпадение номера договора, первый номер не совпадает, цикл переходит к Else, в словарь попадает (и далее выгружается) исходная сумма договора, а между тем, номер договора был в массиве возвращенных, только далее.  Если раскомментировать 176 строку моего кода (закомментировав все строки ниже), мы увидим массив Arr_P в котором 926. договор будет "оценен" в 800 руб., а должен в 400 (ведь бы 1 возврат. То же самое касается 1. и 992. договоров. Так что, как мне кажется, ошибка возникает не в том месте которое Вы указали.
 
Цитата
nilem написал: Или здесь: Split(.Item(k), "-")(0) - элемент ключа - количество, там нет "-"
В Вашем словаре нет Item'ов, которые можно было-бы разделить по "-"
Согласие есть продукт при полном непротивлении сторон
 
номер строки 14.
в одном договоре может быть несколько комитентов?
 
Цитата
nilem написал:
в одном договоре может быть несколько комитентов?
Нет, 1 договор-1 комитент, и сами номера договоров уникальны, повторов быть не должно
 
Цитата
Sanja написал:
В Вашем словаре нет Item'ов, которые можно было-бы разделить по "-"
Уважаемый Sanja, если совсем честно, то я сам не до конца понимаю как работает эта строка кода (полученного давным-давно в одной из моих тем). Действительно -разделителя нет, но ведь считает, это и при пошаговой проверке видно и в результатах 8-0
 
Повторов быть не должно, а Сидоров присутствует в 5-ти договорах
В общем, попробуйте вот так:
Код
Sub ertert()
Dim sp, i As Long, j As Long, temp$
Dim x, y, k, iTimer!
iTimer = Timer
' сначала сверяем суммы выплат с суммами договоров (за вычетом сумм по возврату!):

x = Sheets("Расход").Range("A1").CurrentRegion.Value
With CreateObject("Scripting.Dictionary")  'формирование словаря по данным исходного массива
    .CompareMode = 1
    For i = 2 To UBound(x)    'отбираем данные расхода
        If x(i, 7) = "В" Then
            temp = Split(x(i, 2), "|")(1)    'договор
            .Item(temp) = .Item(temp) + x(i, 4)
        End If
    Next i

    y = Sheets("Приход").Range("A1").CurrentRegion.Value
    For i = 2 To UBound(y)    'отбираем данные прихода
        temp = y(i, 2) & "|" & y(i, 4)    'номер договора|комитент
        If .exists(y(i, 2)) Then    'номер договора
            If y(i, 7) - .Item(y(i, 2)) > 0 Then   'если исходное кол-во больше возвращенного
                .Item(temp) = .Item(temp) + (y(i, 7) - .Item(y(i, 2))) * y(i, 9)
            End If
        Else
            .Item(temp) = .Item(temp) + y(i, 7) * y(i, 9)    'в словарь: договор|комитент & кол-во*цену
        End If
    Next i

    ReDim x(1 To .Count, 1 To 3)    'формируем массив
    For Each k In .Keys
        If InStr(k, "|") Then
            sp = Split(k, "|"): j = j + 1
            'Дата выплаты  -- ??
            x(j, 1) = sp(1) 'Комитент
            x(j, 2) = sp(0) 'договор
            x(j, 3) = .Item(k) 'Сумма выплаты
        End If
    Next k
End With

With Sheets("Выплаты комитентам")
    .Range("A7").CurrentRegion.Offset(1, 1).ClearContents
    .Range("B8").Resize(j, 3).Value = x
End With
MsgBox Timer - iTimer
End Sub
 
Вынужден сейчас отойти от компьютера, возникли срочные дела. Проверю Ваш код немного позже и обязательно отпишусь. А по-поводу повторов, я имел ввиду, что одинаковых номеров договоров быть не должно, ведь у всех договоров Сидорова разные номера.  
 
Проверил, код от nilem почти подходит, но:              
Код
 ...
               For i = 1 To UBound(y)    'отбираем данные прихода
                    temp = y(i, 1) & "|" & y(i, 3)    'номер договора|комитент
                    If .exists(y(i, 1)) Then 'номер договора
                        If y(i, 7) - .Item(y(i, 2)) > 0 Then   'если исходное кол-во больше возвращенного
                            .Item(temp) = .Item(temp) + (y(i, 6) - .Item(y(i, 2))) * y(i, 
                        End If
                    Else
                        .Item(temp) = .Item(temp) + y(i, 6) * y(i,     'в словарь: договор|комитент & кол-во*цену
                    End If
                Next i
...
теперь правильно помещает в словарь (и в дальнейшем выгружает) данные для возвратных договоров, но только со второго совпадения. Ведь для строки:
Код
If .exists(y(i, 1)) Then 'номер договора
первого номера нет в словаре и код отработает неверное (в данном случае) действие по ELSE. Можно ли подправить код (в остальном все вроде бы как и требовалось)? Может быть каким-либо образом до отработки указанной строки поместить в словарь первое значение договора (чтобы было с чем сравнивать?
Изменено: OlegO - 30.03.2017 17:15:47
 
как-то криво вы код скопировали. в одном месте у вас 1, в другом 2
Код
If .exists(y(i, 1)) Then 'номер договора
                        If y(i, 7) - .Item(y(i, 2)) > 0 Then

По сути вообще непонятно, что делает этот блок.
Проверяет, есть ли в словаре номер договора. Предположим, его нет.
Затем записывает в словарь ключ "номер договора|комитент"
Ну записали.
Предположим, следующая проверка опять проверит по тому же номеру договора, и опять его нет - так как ключ не номер догвора, а "номер договора|комитент"
F1 творит чудеса
 
Максим, посмотрите мой приложенный файл из поста №14, код (да чуть переделанный) выгружает данные в столбец G. Данные правильные до строки 11 (я выделил ее рамкой). В этой строке должны находиться данные по договору по которому произошел 1-ый возврат и, следовательно, сумма задолженности должна быть уменьшена (а она выгружается первоначальной). Все дальнейшие ячейки с "возвратными"  договорами вычисляются правильно (13 и 14 строка). Именно поэтому я предположил, что проблема в том, что словарь в самом начале пустой и при первом совпадении номеров, все равно отработает код по ELSE. Прошу учесть мои крайне поверхностные знания словаря то, что я именно предположил это.
 
"По сути вообще непонятно, что делает этот блок."
Ключи с договорами формируются в первом блоке, и нужны только те номера договоров, где есть возврат. А во 2-м блоке уже собираем ключи договор-комитент.
Oleg,
у Вас в примере сообщ. 14 то, что нужно получить на листе Выплаты комитентам?
 
Цитата
nilem написал:
у Вас в примере сообщ. 14 то, что нужно получить на листе Выплаты комитентам?
Почти так, только в G11 требуется получить 400 а не 800. (2 позиции по 400 руб. было изначально, 1 позиция была продана, еще 1 возвращена (за нее разумеется мы ничего не должны), следовательно сумма задолженности =400)
nilem, Ваш код работает правильно начиная со 2-го совпадения "возвратных" договоров (а мой первоначальный наоборот только для 1-го). Вот как бы их объединить что ли?
Изменено: OlegO - 30.03.2017 17:46:25
 
а "Дата выплаты" откуда брать?
 
Дата выплаты и прочие данные на лиcте "Выплаты комитентам"  заносятся совершенно независимо через форму.  Вообще весь поднятый вопрос сводится к тому, чтобы впоследствии (в коде это закомментировано) сравнить  внесенную выплату (напоминаю, внесенную вручную) с рассчитанными суммами (по каким-либо причина сумма после внесения может быть изменена (например если будет выплачена только часть и т.д.). А так по активации листа суммы сравниваются и в случае несоответствия, сигнализируют об этом пользователю.
 
Вот код:
Код
Sub ertert()
Dim sp, i As Long, j As Long, temp$
Dim x, y(), k

x = Sheets("Расход").Range("A1").CurrentRegion.Value
With CreateObject("Scripting.Dictionary")  'формирование словаря по данным исходного массива
    .CompareMode = 1
    For i = 2 To UBound(x)    'отбираем данные расхода
        temp = Split(x(i, 2), "|")(1)    'договор
        If x(i, 7) <> "В" Then .Item(temp) = .Item(temp) + x(i, 4)    'Кол-во расход
    Next i

    x = Sheets("Приход").Range("A1").CurrentRegion.Value
    For i = 2 To UBound(x)    'отбираем данные прихода
        If .exists(x(i, 2)) Then    'номер договора
            temp = x(i, 2) & "|" & x(i, 4)    'номер договора|комитент
            .Item(temp) = .Item(x(i, 2)) * x(i, 9)    'в словарь: договор|комитент & кол-во*цену
        End If
    Next i

    ReDim x(1 To .Count, 1 To 3)    'формируем массив
    For Each k In .Keys
        If InStr(k, "|") Then
            sp = Split(k, "|"): j = j + 1
            'Дата выплаты  -- ??
            x(j, 1) = sp(1)    'Комитент
            x(j, 2) = sp(0)    'договор
            x(j, 3) = .Item(k)    'Сумма выплаты
        End If
    Next k
End With

With Sheets("Выплаты комитентам")
    .Range("A7").CurrentRegion.Offset(1, 1).ClearContents
    .Range("B8").Resize(j, 3).Value = x
End With
End Sub
правда он сам все заполняет, поздно увидел ваше сообщение (ну это можно убавить :)
 
К файлу их #14, если правильно понял

Код
    y = Sheets("Приход").Range("A1").CurrentRegion.Value
    For i = 2 To UBound(y)    'отбираем данные прихода
        temp = y(i, 2) & "|" & y(i, 4)    'номер договора|комитент
        prihod = y(i, 7) ' берем приход
        If .exists(y(i, 2)) Then    'если был возврат по номеру договора
                prihod = prihod - .Item(y(i, 2)) ' уменьшаем размер прихода на размер возврата
        End If
        .Item(temp) = .Item(temp) + prihod * y(i, 9)   'в словарь: договор|комитент & кол-во*цену
    Next i
F1 творит чудеса
 
То ли не соображаю к ночи, то ли еще еще чего, но никак не могу адаптировать к себе Ваш код, nilem, который в Вашем файле считает правильно. Не проходит проверка здесь:
Код
If .exists(y(i, 1)) Then  'номер договора
и все тут :oops:.   nilem, не могли ли бы Вы указать на мою ошибку в ниже предложенном файле? Мне надо, чтобы в столбец G выгрузились только требуемые суммы, далее  надеюсь сам  справлюсь
 
Немного поправлюсь, nilem. В приложенном файле из поста №14 Ваш код (немного адаптированный мною) считает правильно, начиная со 2-го совпадения. Вот точно такую же выгрузку мне и хотелось бы видеть (с исправлением имеющегося недочета разумеется)
 
Просили выгружать в ст. G
В файле нажимайте зеленую кнопку
имхо формулой можно было бы обойтись
 
Спасибо за помощь, nilem. Немного по другому по сравнению с первоначальным кодом, но надеюсь смогу адаптировать к реальному коду. По поводу формул, nilem, Вы считаете этот вариант для нескольких тысяч строк прихода и расхода и несколько сотен строк в "Выплатах..." более ресурсоэкономным и быстрым? Я ведь и модернизацию РАБОЧЕГО кода затеял для ускорения расчетов, занимающих в реальном файле ~5-6 сек., а Вы предлагаете постоянно считающие формулы, неужели это будет быстрее? (написанное ни в коем случае не подначка или стеб, это вопрос). И еще, если вспомнить вопрос из P.S. самого первого поста этой темы, попытка модернизации кода Вашим или моим способом, это Оптимизация существующего рабочего кода (с точки зрения программирования) или нет?
 
Вот с формулой
 
Формулка не слишком тяжелая, на несколько тыс строк, по идее, должна работать. Попробуйте, возможно подойдет.
По поводу "Оптимизация или нет". Нет, это другой код, хотя и похожий.
 
nilem. да я и не сомневался, что можно решить это формулами (хотя за них отдельное спасибо), я хотел узнать Ваше мнение по поводу оптимизации первоначального кода и уместности использования формул в сравнении с VBA (ведь СУММПРРОИЗВ достаточно "тяжелая" формула как мне кажется? Как Вы считаете?
 
Надо просто попробовать. Мне кажется, формулы будут быстро работать.
А если что, у Вас есть запасной вариант с макросом.
Страницы: 1 2 След.
Наверх