Vba excel ошибка 457

 

ALFA

Пользователь

Сообщений: 243
Регистрация: 13.09.2013

#1

13.03.2015 01:35:29

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

Код
On Error GoTo line2
ColT.Add 5,5
line2:

но ошибка по прежнему повторяется..

 

Doober

Пользователь

Сообщений: 2204
Регистрация: 09.04.2013

#2

13.03.2015 04:41:49

Можно так проверять.

Код
Dim Key As String
Key=5
if not Exists(Key,ColT) then  ColT.Add 5,Key
'=========================================

 Function Exists(Key As String,Col as collection) As Boolean
   On Error Resume Next
   Exists = TypeName(Col.Item(Key)) > ""
   err.clear
End Function

<#0>

 

SAS888

Пользователь

Сообщений: 757
Регистрация: 01.01.1970

#3

13.03.2015 07:53:10

Вместо

Код
ColT.Add 5, 5

используйте

Код
ColT.Add 5, CStr(5)

Чем шире угол зрения, тем он тупее.

 

Hugo

Пользователь

Сообщений: 23251
Регистрация: 22.12.2012

#4

13.03.2015 09:24:34

Не понятно зачем вообще там это всё…

Код
On Error resume next
ColT.Add 5,"5"
line2:
 

Казанский

Пользователь

Сообщений: 8839
Регистрация: 11.01.2013

#5

14.03.2015 14:31:20

ALFA, но ошибка по прежнему повторяется..[/QUOTE]Basic предполагает, что на метке line2 начинается обработчик ошибки. Он должен завершаться оператором Resume или выходом из процедуры. Если в обработчике ошибок возникает ошибка, она уже не обрабатывается *) и происходит останов. Это и происходит у Вас при следующем повторяющемся элементе, т.к. Basic не встретил оператор Resume и считает, что работает обработчик ошибок. Простейший обработчик ошибок должен выглядеть так:

Код
On Error GoTo line2 'вне цикла
For Each x In Array(1, 2, 1, 5, 5)
  ColT.Add x, CStr(x)
nxt: Next
On Error GoTo 0
'...
Exit Sub

line2: Resume nxt
End Sub

*) Для обработки ошибок в обработчике ошибок можно использовать оператор On Error GoTo -1

 

ALFA

Пользователь

Сообщений: 243
Регистрация: 13.09.2013

#6

18.03.2015 14:48:29

Doober, Ваш вариант успешно подошел, работает, Спасибо!
Казанский, Ваш еще не опробовал)
SAS888, Изменение типа ключа не помогло

Цитата
Hugo написал:On Error resume next
ColT.Add 5,»5″
line2:

не подходит, так как если ключ уже существует в коллекции мне необходимо было перейти в определенный участок кода, видимо я привел не совсем подходящий пример( Необходимо было в случае ошибки перейти не к следующему элементу а именно перепрыгнуть на line2:

Всем спасибо за предложенные варианты решения!

Permalink

Cannot retrieve contributors at this time

title keywords f1_keywords ms.prod ms.assetid ms.date ms.localizationpriority

This key is already associated with an element of this collection (Error 457)

vblr6.chm1000457

vblr6.chm1000457

office

d6c2ba60-4077-0ccd-5bf4-221367db7b59

06/08/2017

medium

A key is a string specified in the Add method that uniquely identifies a specific member of a collection. This error has the following cause and solution:

  • You specified a key for a collection member that already identifies another member of the collection. Choose a different key for this member.

For additional information, select the item in question and press F1 (in Windows) or HELP (on the Macintosh).

[!includeSupport and feedback]

#vba #vba7 #vba6

#vba #vba7 #vba6

Вопрос:

У меня возникли проблемы с написанием макроса для сравнения нескольких столбцов на нескольких листах (одного и того же файла Excel). Я написал несколько, но они занимали так много времени, что Excel зависал.

Допустим, у меня есть 4 листа в одном файле. Лист1 с двумя столбцами (B и C) и 7000 строк. Лист2 пустой лист новые записи. Лист3 пустой лист для старых записей, но с некоторым обновленным значением / информацией. Лист4 представляет собой базу данных с 2 столбцами (A и B) и 22000 строками.

Мне нужно сравнить столбец A из листа 1 со столбцом B в листе 4. Если в столбце A sheet1 есть совершенно новые записи, скопируйте эту запись из столбца A sheet1 (и ее соответствующее значение из столбца B sheet1) в новую строку (столбцы A и B) в Sheet2. Если в столбце A Sheet1 есть записи, которые уже есть в столбце A sheet4, затем сравните их соответствующие значения в столбце B. Если комбинация столбца A столбца B из листа 1 находится в листе 4, игнорируйте ее. Если значение из столбца A Sheet1 находится в столбце A Sheet4, но их соответствующие значения в столбце B не совпадают, скопируйте столбец A столбец B из листа 1 в новую строку (столбцы A и B) в листе 3.

Надеюсь, это достаточно ясно. Из-за количества строк (7000 в Sheet1 для сравнения с 20000 в Sheet4) Я не могу написать макрос, который обрабатывает все меньше минуты.

Любая помощь?

Редактирование 1: я использовал код, предложенный @FaneDuru (спасибо!). но я сталкиваюсь с ошибкой: «Ошибка времени выполнения ‘457’: этот ключ уже связан с элементом этой коллекции» Это потому, что у меня много повторяющихся значений в одних и тех же столбцах?

Редактировать 2: похоже, что код «if not dict3.exists» не распознается VBA. Когда я набираю «.exists» с меньшей буквой и перехожу на другую строку, предполагается исправить ее на заглавную «.Exists», верно? Он этого не делает.

Редактировать 3: я провел еще несколько тестов. Я ставил разрывы и запускал код. Когда я ставлю разрыв в этой строке «If WorksheetFunction.CountIf(rngA4, arr1(i, 1))> 0 Тогда», ошибка не возникает. Когда я ставлю разрыв на одну строку ниже «Для j = UBound (arr4) До 1 шага -1» происходит ошибка.

Ошибка: «Ошибка времени выполнения ‘457’: этот ключ уже связан с элементом этой коллекции»

 Private Sub CommandButton1_Click()

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.DisplayAlerts = False

Dim arr1, arr2, arr3, arr4, dict2 As Object, dict3 As Object, rngA4 As Range
Dim rngB4 As Range, i As Long, j As Long, lastR1 As Long, lastR4 As Long

lastR1 = Sheet1.Range("A" amp; Sheet1.Rows.Count).End(xlUp).Row
lastR4 = Sheet4.Range("A" amp; Sheet4.Rows.Count).End(xlUp).Row

Set rngA4 = Sheet4.Range("A2:A" amp; lastR4)
Set rngB4 = Sheet4.Range("B2:B" amp; lastR4)

arr1 = Sheet1.Range("B2:C" amp; lastR1).Value
arr4 = Sheet4.Range("A2:B" amp; lastR4).Value

Set dict2 = CreateObject("Scripting.Dictionary")
Set dict3 = CreateObject("Scripting.Dictionary")

For i = UBound(arr1) To 1 Step -1
    If WorksheetFunction.CountIf(rngB4, arr1(i, 1)) = 0 Then
        dict2.Add arr1(i, 1), arr1(i, 2):
    End If
    If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then
        For j = UBound(arr4) To 1 Step -1
            If arr1(i, 1) = arr4(j, 1) Then
                If arr1(i, 2) <> arr4(j, 2) Then
                    If arr1(i, 2) <> arr4(j, 2) Then
                        dict3.Add arr1(i, 1), arr1(i, 2): Exit For
                End If
            End If
        Next j
    End If
Next i

If dict2.Count > 0 Then
    arr2 = Application.Transpose(Array(dict2.keys, dict2.Items))
    Sheet2.Range("A2").Resize(dict2.Count, 2).Value = arr2
End If

If dict3.Count > 0 Then
    arr3 = Application.Transpose(Array(dict3.keys, dict3.Items))
    Sheet3.Range("A2").Resize(dict3.Count, 2).Value = arr3
End If

MsgBox "Done!"

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.DisplayAlerts = True

End Sub
 

Комментарии:

1. Пожалуйста, отредактируйте свой вопрос и опубликуйте то, что вы пробовали самостоятельно. Даже если он не выполняется точно так, как вам нужно. Также помогут некоторые картинки (если они недоступны для редактирования), показывающие существующую ситуацию, соответственно, нужный вам результат.

2. Что означает «Если в столбце листа 1 есть совершенно новые записи»?

3. «совершенно новые записи в столбце лист1» — означает запись среди этих 7000 строк на листе 1, которой нет среди 20000 строк на листе4.

4. Таким образом, «полностью» не имеет никакого значения… Теперь, возможно ли существование большего количества вхождений строки из листа 1 в столбцах листа 4 B или A? И, для ускорения кода, как обновляются обсуждаемые листы? Я имею в виду, что все время добавляются новые строки, или новые записи могут быть сделаны в любой строке столбца (B или A)?

5. Вы по-прежнему не ответили на уточняющие вопросы, но жалуетесь на «обнаружение ошибки»… Я четко сформулировал предположение «не более одного вхождения». Это не способ помочь нам помочь вам. Ввод On Error Resume Next не является хорошим способом решения проблемы с ошибкой. Вы должны понять, откуда возникает проблема, и решить ее в соответствии с ее корнями. Итак, у вас есть еще такие случаи? Если да, как вам нравится, чтобы код выполнялся в таком случае? Затем сообщать нам, что появляется ошибка, не указывая, в какой строке кода , снова является плохой практикой. Пожалуйста, уточните это

Ответ №1:

Вы можете использовать формулу Excel countif, чтобы найти любую запись данных, которая не существует в вашем наборе данных.

Затем вы можете скопировать значение с помощью Sheets().Range().Value = Sheets().Range().Value на лист, где вы хотите получить свой вывод. Если выходной диапазон уже заполнен, вы можете использовать Sheets().Range().End(xlDown) .Адрес, чтобы найти адрес последней строки вашего выходного набора данных.

Вы перебираете все значения countif, которые возвращают 0, чтобы получить все недостающие данные.

Комментарии:

1. Это я знаю. Но я бы хотел сделать это только с помощью VBA.

2. Vba может получить доступ к функциям Excel, на самом деле обычно быстрее использовать функции Excel с VBA, потому что Excel может выполнять вычисления в нескольких потоках, тогда как VBA не может (вы можете обойти это, но это действительно сложно и не стоит усилий)

3. Ему не нужно (только) знать, существует ли конкретная строка на другом листе. Ему нужно заполнить Лист2 недостающими элементами и Лист3 в некоторых условиях.

4. Справедливо, я должен был добавить что-то об использовании функции поиска или функции фильтра, чтобы найти, какие строки были уникальными

Ответ №2:

Пожалуйста, протестируйте следующий код. Вы не ответили на уточняющие вопросы, и код предполагает, что существует не более одного вхождения, а обработанные листы загружаются путем добавления строк. Код работает независимо от этого аспекта, но если приведенное выше предположение верно, он будет выполняться быстрее:

 Sub testProcessNewEntries()
 Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet, sh4 As Worksheet
 Dim arr1, arr2, arr3, arr4, dict2 As Object, dict3 As Object, rngA4 As Range
 Dim rngB4 As Range, i As Long, j As Long, lastR1 As Long, lastR4 As Long
 
 Set sh1 = Worksheets("Sheet1") 'use here your first sheet
 Set sh2 = Worksheets("Sheet2") 'use here your second sheet
 Set sh3 = Worksheets("Sheet3") 'use here your third sheet
 Set sh4 = Worksheets("Sheet4") 'use here your fourth sheet
 
 lastR1 = sh1.Range("A" amp; sh1.Rows.count).End(xlUp).row
 lastR4 = sh4.Range("A" amp; sh4.Rows.count).End(xlUp).row
  
 Set rngA4 = sh4.Range("A2:A" amp; lastR4)
 Set rngB4 = sh4.Range("B2:B" amp; lastR4)
 
 arr1 = sh1.Range("A2:B" amp; lastR1).Value
 arr4 = sh4.Range("A2:B" amp; lastR4).Value
 
 Set dict2 = CreateObject("Scripting.Dictionary")
 Set dict3 = CreateObject("Scripting.Dictionary")
 
 For i = UBound(arr1) To 1 Step -1
    If WorksheetFunction.CountIf(rngB4, arr1(i, 1)) = 0 Then
        dict2.Add arr1(i, 1), arr1(i, 2):
    End If
    If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then
        For j = UBound(arr4) To 1 Step -1
            If arr1(i, 1) = arr4(j, 1) Then
                If arr1(i, 2) <> arr4(j, 2) Then
                    If Not dict3.Exists(arr1(i, 1)) Then
                        dict3.Add arr1(i, 1), arr1(i, 2): Exit For
                    End If
                End If
            End If
        Next j
    End If
 Next i
 
 If dict2.count > 0 Then
    arr2 = Application.Transpose(Array(dict2.Keys, dict2.Items))
    sh2.Range("A2").Resize(dict2.count, 2).Value = arr2
 End If
 If dict3.count > 0 Then
    arr3 = Application.Transpose(Array(dict3.Keys, dict3.Items))
    sh3.Range("A2").Resize(dict3.count, 2).Value = arr3
 End If
 MsgBox "Ready..."
End Sub
 

Комментарии:

1. @Elmar: Разве вы не нашли немного времени, чтобы проверить приведенный выше код? Это было написано для того, чтобы ответить на ваш вопрос. Если его протестировали, разве он не сделал то, что вам нужно?

2. прежде всего, я хотел бы поблагодарить вас за то, что вы нашли время и помогли мне. Я протестировал код (и все еще тестирую). Я сталкиваюсь с ошибкой «Ошибка времени выполнения ‘457’: этот ключ уже связан с элементом этой коллекции». Это потому, что в моих столбцах много повторяющихся значений?

3. Я добавил «При следующей ошибке возобновить работу», и, похоже, это решило проблему. Как вы думаете, это хороший способ? Надеюсь, это была не важная ошибка, которая создаст беспорядок в моих данных. Кстати, это сработало как по волшебству (если мы проигнорируем ошибку) и очень быстро!!!

4. @Elmar: Пожалуйста, протестируйте обновленный код и убедитесь, что он работает без каких-либо ошибок.

5. Если я не ошибаюсь, единственное изменение, которое вы внесли в код, находится в «самом глубоком» цикле, правильно ?… Если не dict3.Exists(arr1(i, 1)), то dict3 . Добавьте arr1(i, 1), arr1(i, 2): Выход для … Я тестирую его, и ошибка все еще остается. Два наблюдения: 1. Вы правы, ошибка вызвана чем-то в цикле. 2. похоже, что код «if not dict3.exists» не распознается VBA. Когда я набираю «.exists» с меньшей буквой и перехожу на другую строку, предполагается исправить ее на заглавную «.Exists», верно? Он этого не делает.

I am having trouble writing a macro for comparing multiple columns in multiple sheets (of same excel file). I wrote few but they were taking so long that excel was crashing.

Let’s say I have 4 sheets in one same file.
Sheet1 with two columns (B and C) and 7000 rows.
Sheet2 empty sheet new entries.
Sheet3 empty sheet for old entries but with some updated value/info.
Sheet4 is a database with 2 columns (A and B) and 22000 rows.

I need to compare Column A from Sheet1 to Column B in Sheet4.
If there are completely new entries in Column A sheet1, then copy that entry from Column A sheet1 (and its respective value from Column B sheet1) to a new row (columns A and B) in Sheet2.
If there are entries in Column A Sheet1 that are already in Column A sheet4, then compare their respective Column B values. If column A+column B combo from Sheet 1 is in Sheet4 then ignore it. If a Value from Column A Sheet1 is in Column A Sheet4, but their respective Column B values are not matching then copy Column A+Column B from Sheet1 to new row (columns A and B) in Sheet3.

I hope it is clear enough. Due to amount of rows (7000 in Sheet1 to be compared to 20000 in Sheet4) I cannot write a macro that processes everything under a minute.

Any help ?

Edit 1: I used the code suggested by @FaneDuru (Thank You!). but I am encountering an error: «Run-time error ‘457’:This key is already associated with an element of this collection»
Is it because I have many repeating values in same columns ?

Edit 2: It seems like «if not dict3.exists» code is not recognized by VBA. When I type «.exists» with smaller letter and jump to another line it is supposed correct it to capital «.Exists», right? It is not doing it.

Edit 3: I did some more testing. I was putting breaks and running the code. When I put the break on this line «If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then», no error happens. When I put the break on one line below «For j = UBound(arr4) To 1 Step -1», the error is happening.

Error is : «Run-time error ‘457’:This key is already associated with an element of this collection»

Private Sub CommandButton1_Click()

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.DisplayAlerts = False

Dim arr1, arr2, arr3, arr4, dict2 As Object, dict3 As Object, rngA4 As Range
Dim rngB4 As Range, i As Long, j As Long, lastR1 As Long, lastR4 As Long

lastR1 = Sheet1.Range("A" & Sheet1.Rows.Count).End(xlUp).Row
lastR4 = Sheet4.Range("A" & Sheet4.Rows.Count).End(xlUp).Row

Set rngA4 = Sheet4.Range("A2:A" & lastR4)
Set rngB4 = Sheet4.Range("B2:B" & lastR4)

arr1 = Sheet1.Range("B2:C" & lastR1).Value
arr4 = Sheet4.Range("A2:B" & lastR4).Value

Set dict2 = CreateObject("Scripting.Dictionary")
Set dict3 = CreateObject("Scripting.Dictionary")

For i = UBound(arr1) To 1 Step -1
    If WorksheetFunction.CountIf(rngB4, arr1(i, 1)) = 0 Then
        dict2.Add arr1(i, 1), arr1(i, 2):
    End If
    If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then
        For j = UBound(arr4) To 1 Step -1
            If arr1(i, 1) = arr4(j, 1) Then
                If arr1(i, 2) <> arr4(j, 2) Then
                    If arr1(i, 2) <> arr4(j, 2) Then
                        dict3.Add arr1(i, 1), arr1(i, 2): Exit For
                End If
            End If
        Next j
    End If
Next i

If dict2.Count > 0 Then
    arr2 = Application.Transpose(Array(dict2.keys, dict2.Items))
    Sheet2.Range("A2").Resize(dict2.Count, 2).Value = arr2
End If

If dict3.Count > 0 Then
    arr3 = Application.Transpose(Array(dict3.keys, dict3.Items))
    Sheet3.Range("A2").Resize(dict3.Count, 2).Value = arr3
End If

MsgBox "Done!"

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.DisplayAlerts = True

End Sub

 

ALFA

Пользователь

Сообщений: 243
Регистрация: 13.09.2013

#1

13.03.2015 01:35:29

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

Код
On Error GoTo line2
ColT.Add 5,5
line2:

но ошибка по прежнему повторяется..

 

Doober

Пользователь

Сообщений: 2131
Регистрация: 09.04.2013

#2

13.03.2015 04:41:49

Можно так проверять.

Код
Dim Key As String
Key=5
if not Exists(Key,ColT) then  ColT.Add 5,Key
'=========================================

 Function Exists(Key As String,Col as collection) As Boolean
   On Error Resume Next
   Exists = TypeName(Col.Item(Key)) > ""
   err.clear
End Function

<#0>

 

SAS888

Пользователь

Сообщений: 757
Регистрация: 01.01.1970

#3

13.03.2015 07:53:10

Вместо

Код
ColT.Add 5, 5

используйте

Код
ColT.Add 5, CStr(5)

Чем шире угол зрения, тем он тупее.

 

Hugo

Пользователь

Сообщений: 23100
Регистрация: 22.12.2012

#4

13.03.2015 09:24:34

Не понятно зачем вообще там это всё…

Код
On Error resume next
ColT.Add 5,"5"
line2:
 

Казанский

Пользователь

Сообщений: 8839
Регистрация: 11.01.2013

#5

14.03.2015 14:31:20

ALFA, но ошибка по прежнему повторяется..[/QUOTE]Basic предполагает, что на метке line2 начинается обработчик ошибки. Он должен завершаться оператором Resume или выходом из процедуры. Если в обработчике ошибок возникает ошибка, она уже не обрабатывается *) и происходит останов. Это и происходит у Вас при следующем повторяющемся элементе, т.к. Basic не встретил оператор Resume и считает, что работает обработчик ошибок. Простейший обработчик ошибок должен выглядеть так:

Код
On Error GoTo line2 'вне цикла
For Each x In Array(1, 2, 1, 5, 5)
  ColT.Add x, CStr(x)
nxt: Next
On Error GoTo 0
'...
Exit Sub

line2: Resume nxt
End Sub

*) Для обработки ошибок в обработчике ошибок можно использовать оператор On Error GoTo -1

 

ALFA

Пользователь

Сообщений: 243
Регистрация: 13.09.2013

#6

18.03.2015 14:48:29

Doober, Ваш вариант успешно подошел, работает, Спасибо!
Казанский, Ваш еще не опробовал)
SAS888, Изменение типа ключа не помогло

Цитата
Hugo написал:On Error resume next
ColT.Add 5,»5″
line2:

не подходит, так как если ключ уже существует в коллекции мне необходимо было перейти в определенный участок кода, видимо я привел не совсем подходящий пример( Необходимо было в случае ошибки перейти не к следующему элементу а именно перепрыгнуть на line2:

Всем спасибо за предложенные варианты решения!

I am having trouble writing a macro for comparing multiple columns in multiple sheets (of same excel file). I wrote few but they were taking so long that excel was crashing.

Let’s say I have 4 sheets in one same file.
Sheet1 with two columns (B and C) and 7000 rows.
Sheet2 empty sheet new entries.
Sheet3 empty sheet for old entries but with some updated value/info.
Sheet4 is a database with 2 columns (A and B) and 22000 rows.

I need to compare Column A from Sheet1 to Column B in Sheet4.
If there are completely new entries in Column A sheet1, then copy that entry from Column A sheet1 (and its respective value from Column B sheet1) to a new row (columns A and B) in Sheet2.
If there are entries in Column A Sheet1 that are already in Column A sheet4, then compare their respective Column B values. If column A+column B combo from Sheet 1 is in Sheet4 then ignore it. If a Value from Column A Sheet1 is in Column A Sheet4, but their respective Column B values are not matching then copy Column A+Column B from Sheet1 to new row (columns A and B) in Sheet3.

I hope it is clear enough. Due to amount of rows (7000 in Sheet1 to be compared to 20000 in Sheet4) I cannot write a macro that processes everything under a minute.

Any help ?

Edit 1: I used the code suggested by @FaneDuru (Thank You!). but I am encountering an error: «Run-time error ‘457’:This key is already associated with an element of this collection»
Is it because I have many repeating values in same columns ?

Edit 2: It seems like «if not dict3.exists» code is not recognized by VBA. When I type «.exists» with smaller letter and jump to another line it is supposed correct it to capital «.Exists», right? It is not doing it.

Edit 3: I did some more testing. I was putting breaks and running the code. When I put the break on this line «If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then», no error happens. When I put the break on one line below «For j = UBound(arr4) To 1 Step -1», the error is happening.

Error is : «Run-time error ‘457’:This key is already associated with an element of this collection»

Private Sub CommandButton1_Click()

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.DisplayAlerts = False

Dim arr1, arr2, arr3, arr4, dict2 As Object, dict3 As Object, rngA4 As Range
Dim rngB4 As Range, i As Long, j As Long, lastR1 As Long, lastR4 As Long

lastR1 = Sheet1.Range("A" & Sheet1.Rows.Count).End(xlUp).Row
lastR4 = Sheet4.Range("A" & Sheet4.Rows.Count).End(xlUp).Row

Set rngA4 = Sheet4.Range("A2:A" & lastR4)
Set rngB4 = Sheet4.Range("B2:B" & lastR4)

arr1 = Sheet1.Range("B2:C" & lastR1).Value
arr4 = Sheet4.Range("A2:B" & lastR4).Value

Set dict2 = CreateObject("Scripting.Dictionary")
Set dict3 = CreateObject("Scripting.Dictionary")

For i = UBound(arr1) To 1 Step -1
    If WorksheetFunction.CountIf(rngB4, arr1(i, 1)) = 0 Then
        dict2.Add arr1(i, 1), arr1(i, 2):
    End If
    If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then
        For j = UBound(arr4) To 1 Step -1
            If arr1(i, 1) = arr4(j, 1) Then
                If arr1(i, 2) <> arr4(j, 2) Then
                    If arr1(i, 2) <> arr4(j, 2) Then
                        dict3.Add arr1(i, 1), arr1(i, 2): Exit For
                End If
            End If
        Next j
    End If
Next i

If dict2.Count > 0 Then
    arr2 = Application.Transpose(Array(dict2.keys, dict2.Items))
    Sheet2.Range("A2").Resize(dict2.Count, 2).Value = arr2
End If

If dict3.Count > 0 Then
    arr3 = Application.Transpose(Array(dict3.keys, dict3.Items))
    Sheet3.Range("A2").Resize(dict3.Count, 2).Value = arr3
End If

MsgBox "Done!"

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.DisplayAlerts = True

End Sub

I’m trying to create a dictionary of dictionary structure in vba

Basically, I start with a 3 column tables :

Product Id | Customer Id | Source

1 | 1 | A

1 | 2 | A

2 | 1 | A

3 | 1 | B

And I want to transform it into a main dictionary «DicByUser» where the keys are the user ids and the items are another dictionary that contain as keys the products visited by a client and as item the source code.

In that case, I would have

DicByUser= { 1 : { 1 : A , 2 : A, 3 : B}, 2 : {1 : A }}

My approach was to go through all the rows of my initial table then :

with Cid the customer Id,

Pid the product Id,

source the Source

If DicByUser.Exists(Cid) Then
    If DicByUser.Item(Cid).Exists(Pid) Then
        'We do something on the item
    Else
        DicByUser.Item(Cid).Add Pid, source
    End If
 Else
    Dim dicotoadd As New Scripting.Dictionary
    dicotoadd.Add Pid, source
    DicByUser.Add Cid, dicotoadd

Weirdly, the line before the last gives me the error : vba tells me that

Error 457 : this key is already associated with an element of collection

Then, if I go in debug mode and I try to display the number of elements in my object dicotoadd, I find 1, while the object was created at the line before.

I believe there is probably a problem in the way I put a dictionary in another one by always giving it the same name, otherwise I don’t see why a dictionary that I create one line above can already contain an element

What am I doing wrong in my procedure to create a nested dictionary in vba?

Edit : Solved by changing my code to the following, as suggested by Mat’s Mug

If DicByUser.Exists(Cid) Then
    If DicByUser.Item(Cid).Exists(Pid) Then
        'We do something on the item
    Else
        DicByUser.Item(Cid).Add Pid, source
    End If
 Else
    Dim dicotoadd As Scripting.Dictionary
    Set dicotoadd = New Scripting.Dictionary
    dicotoadd.Add Pid, source
    DicByUser.Add Cid, dicotoadd

I’m trying to create a dictionary of dictionary structure in vba

Basically, I start with a 3 column tables :

Product Id | Customer Id | Source

1 | 1 | A

1 | 2 | A

2 | 1 | A

3 | 1 | B

And I want to transform it into a main dictionary «DicByUser» where the keys are the user ids and the items are another dictionary that contain as keys the products visited by a client and as item the source code.

In that case, I would have

DicByUser= { 1 : { 1 : A , 2 : A, 3 : B}, 2 : {1 : A }}

My approach was to go through all the rows of my initial table then :

with Cid the customer Id,

Pid the product Id,

source the Source

If DicByUser.Exists(Cid) Then
    If DicByUser.Item(Cid).Exists(Pid) Then
        'We do something on the item
    Else
        DicByUser.Item(Cid).Add Pid, source
    End If
 Else
    Dim dicotoadd As New Scripting.Dictionary
    dicotoadd.Add Pid, source
    DicByUser.Add Cid, dicotoadd

Weirdly, the line before the last gives me the error : vba tells me that

Error 457 : this key is already associated with an element of collection

Then, if I go in debug mode and I try to display the number of elements in my object dicotoadd, I find 1, while the object was created at the line before.

I believe there is probably a problem in the way I put a dictionary in another one by always giving it the same name, otherwise I don’t see why a dictionary that I create one line above can already contain an element

What am I doing wrong in my procedure to create a nested dictionary in vba?

Edit : Solved by changing my code to the following, as suggested by Mat’s Mug

If DicByUser.Exists(Cid) Then
    If DicByUser.Item(Cid).Exists(Pid) Then
        'We do something on the item
    Else
        DicByUser.Item(Cid).Add Pid, source
    End If
 Else
    Dim dicotoadd As Scripting.Dictionary
    Set dicotoadd = New Scripting.Dictionary
    dicotoadd.Add Pid, source
    DicByUser.Add Cid, dicotoadd

Hi there,
I have a Form in my spreadsheet that works with a SQL Server to add information to a Worksheet.
For some reason, whenever I click on the «Get New Orders» button, I get the Run-time error 457: «This key is already associated with an element of this collection.» I have been able to identify the line that it comes up on, I will indicate below.

Form Code:[vba]Option Explicit

Private clDataCollection As ScheduleDataCollection ‘ (SQL Query in class module)
Private clNewOrders As ScheduleNewOrders

Private Sub UserForm_Initialize()
Set clNewOrders = New ScheduleNewOrders
Me.txtFilterDateStarted.Text = VBA.Format(VBA.DateAdd(«d», -10, Now()), «mm/dd/yyyy»)
End Sub

Private Sub UserForm_Terminate()
Set clNewOrders = Nothing
End Sub

Private Sub cmdAddSelectedOrders_Click()
Dim i As Long

For i = 0 To Me.lstNewOrders.ListCount — 1
If (Me.lstNewOrders.Selected(i) = True) Then
‘ add the isSelected = True to ScheduleData
‘ REMEMBER TO ADD 1 to i BECAUSE IT IS OFFSET BY 1
clDataCollection.Item(i + 1).IsSelected = True
End If
Next

clNewOrders.AddSelectedNewOrdersToSchedule
End Sub

Private Sub cmdGetNewOrders_Click()
‘ Check to see if there is a date filter added to the Userform
If (Len(Me.txtFilterDateStarted.Text) > 0) Then
‘ Check if it’s a date
If (IsDate(Me.txtFilterDateStarted.Text) = False) Then
MsgBox «Please enter a valid date in ‘Filter Date Started’.», vbExclamation, «Error»
Exit Sub
Else
‘ Set date filter in NewOrders class
clNewOrders.FilterDateEnteredInFE = CDate(Me.txtFilterDateStarted.Text)
End If
End If

Set clDataCollection = clNewOrders.getNewOrders <——Error Here

If (clDataCollection.Count <= 0) Then Exit Sub

‘ Clear list
Me.lstNewOrders.Clear

Dim i As Long

‘ Loop through data collection and add orders
lstNewOrders.ColumnCount = 2

For i = 1 To clDataCollection.Count
lstNewOrders.AddItem clDataCollection.Item(i).JobCode
lstNewOrders.List(lstNewOrders.ListCount — 1, 1) = clDataCollection.Item(i).LineNumber
Next
End Sub

[/vba]

Class Module information; some values and names were replaced with comments or other values:
[vba]Option Explicit

‘ This class is designed to update the Production Schedule
Private Const ScheduleSheetName As String = «Production Schedule»
Private Const sSQLFolder As String = «PATHSQL Queries»
Private Const sFileScheduleCopyPaste As String = Location
Private Const sReplaceDateFilter As String = «@selectDate»
Private dteFilterDateEnteredInFE As Date

Private colScheduleData As ScheduleDataCollection
Private NAMEDDB As NAMEDSQL

‘ Enum for the worksheet to update the columns
Private Enum ScheduleColumns
‘Long list of column names
End Enum

»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»’
‘ Filter Date Entered in FE Property
‘ lets the user filter the results based on date entered in FE
»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»’
Public Property Let FilterDateEnteredInFE(ByVal dte As Date)
dteFilterDateEnteredInFE = dte
End Property
Public Property Get FilterDateEnteredInFE() As Date
FilterDateEnteredInFE = dteFilterDateEnteredInFE
End Property

»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»’
‘ Schedule Data Collection Property
»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»’
Public Property Get DataCollection() As ScheduleDataCollection
Set DataCollection = colScheduleData
End Property

Private Sub Class_Initialize()
Set SchrothDB = New NAMEDSQL
Set colScheduleData = New ScheduleDataCollection

‘ set a standard filter date of 1 month previous
Me.FilterDateEnteredInFE = VBA.DateAdd(«m», -1, Now())
End Sub

Private Sub Class_Terminate()
‘ clean up code
colScheduleData.RemoveAll
Set colScheduleData = Nothing

Set SchrothDB = Nothing
End Sub

Public Function getNewOrders() As ScheduleDataCollection
‘ this procedure uses the SchrothSQL database connection to get a list of orders based on a date
‘ to filter for the results

‘ if we ever need to leave the function, it will be set to nothing by default
Set getNewOrders = Nothing

‘ Clear collection before setting new values
colScheduleData.RemoveAll

Dim sSQL As String

sSQL = getTextFromFile(sSQLFolder & sFileScheduleCopyPaste)

If (VBA.Len(sSQL) <= 0) Then Exit Function

sSQL = VBA.Replace(sSQL, sReplaceDateFilter, «‘» & VBA.Format(Me.FilterDateEnteredInFE, «mm/dd/yyyy») & «‘»)

NAMEDDB.OpenConnection
NAMEDDB.executeSQL sSQL

Set getNewOrders = processRecordSetForNewOrders(SchrothDB)

NAMEDDB.CloseConnection
End Function

Private Function processRecordSetForNewOrders(ByRef db As SchrothSQL) As ScheduleDataCollection
‘ loops through data and updates the ScheduleDataCollection
If (db.recordSetIsEmpty = True) Then Exit Function

Set processRecordSetForNewOrders = Nothing

Dim rs As ADODB.RecordSet
Set rs = db.RecordSet

If rs.Fields.Count > 0 Then

Do While Not rs.EOF
Dim d As ScheduleData
Set d = New ScheduleData

‘Long list of SQL Query Values

colScheduleData.Add d

rs.MoveNext
Loop

rs.Close
Set rs = Nothing
Set processRecordSetForNewOrders = colScheduleData
End If
End Function

Private Function IfIsNull(ByRef v As Variant) As String
If (IsNull(v)) Then
IfIsNull = vbNullString
Else
IfIsNull = v
End If
End Function

Public Sub AddSelectedNewOrdersToSchedule()
‘ This is used to add new orders selected from user to the schedule at the very end of the data
If (SheetExists(ScheduleSheetName) = False) Then
MsgBox «Cannot find sheet ‘» & ScheduleSheetName & «‘! Terminating program.»
Exit Sub
End If

If (colScheduleData.Count <= 0) Then Exit Sub

Application.ScreenUpdating = False

Dim i As Long

For i = 1 To colScheduleData.Count
‘ looks for isSelected, then adds it to the schedule
If (colScheduleData.Item(i).IsSelected) Then
Call AddOrderToSchedule(colScheduleData.Item(i))
End If
Next

Application.ScreenUpdating = True
End Sub

Private Sub AddOrderToSchedule(ByRef d As ScheduleData)
‘ this is used to add the order to the worksheet
Dim ScheduleSheet As Excel.Worksheet
Dim NewRow As Long
Dim cf As CellFunctions

Set cf = New CellFunctions

Set ScheduleSheet = ThisWorkbook.Worksheets(ScheduleSheetName)
NewRow = cf.getLastRowInColumn(ScheduleSheet, 1) + 1

With ScheduleSheet
‘Long list of cell values
End With ‘ With ScheduleSheet

Call AddFormulasToSchedule(ScheduleSheet, NewRow)
End Sub

Private Sub AddFormulasToSchedule(ByRef ws As Excel.Worksheet, ByVal iRow As Long)
‘ using the passed Row value, this adds the necessary formulas to the schedule
‘ Assumes the sheet exists

‘ Order Total formula
ws.Cells(iRow, ScheduleColumns.OrderTotal).Formula = _
«=SUM(» _
& ws.Cells(iRow, ScheduleColumns.BeltRevenue).Address(0, 1) _
& «:» _
& ws.Cells(iRow, ScheduleColumns.PlatingRevenue).Address(0, 1) _
& «)»

‘ Balance Formula
ws.Cells(iRow, ScheduleColumns.Balance).Formula = _
«=» _
& ws.Cells(iRow, ScheduleColumns.InvoicedAmount).Address(0, 1) _
& «-» _
& ws.Cells(iRow, ScheduleColumns.OrderTotal).Address(0, 1)

End Sub

Private Function getDocumentationInfo(ByRef sInfo As String) As String
‘ processes which kind of documentation info to look for in the string
‘ then builds a new string to be added to the production schedule

If (Len(sInfo) <= 0) Then Exit Function

Dim searchStrings(3, 1) As String
Dim displayString As String

searchStrings(0, 0) = «VALUE»
searchStrings(0, 1) = «VALUE»
searchStrings(1, 0) = «VALUE»
searchStrings(1, 1) = «VALUE»
searchStrings(2, 0) = «VALUE»
searchStrings(2, 1) = «VALUE»
searchStrings(3, 0) = «VALUE»
searchStrings(3, 1) = «VALUE»

Dim i As Long
Dim index As Long

For i = 0 To UBound(searchStrings, 1)
index = InStr(1, sInfo, searchStrings(i, 0), vbTextCompare)
If (index > 0) Then
‘ check if NOT required
If (InStr(1 _
, Mid(sInfo, index, Len(searchStrings(i, 0)) + Len(» not req»)) _
, «Not Req», vbTextCompare) _
<= 0) Then

displayString = displayString & searchStrings(i, 1) & Chr(10)
End If
End If
Next

‘ remove last newline from string
If (Len(displayString) > 0) Then
displayString = Left(displayString, Len(displayString) — 1)
End If

getDocumentationInfo = displayString
End Function

Private Function SheetExists(ByVal wsName As String) As Boolean
‘ Determines if the specified sheet exists in the workbook
On Error Resume Next
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(wsName)
SheetExists = Not ws Is Nothing
End Function

‘ COURTESY OF: http://www.exceluser.com/explore/que…a_textcols.htm
Private Function getTextFromFile(ByRef sFile As String) As String

If (FileExists(sFile) = False) Then
getTextFromFile = vbNullString
Exit Function
End If

Dim nSourceFile As Integer

‘ Close any open text files
Close

‘ Get the number of the next free text file
nSourceFile = FreeFile

‘ Write the entire file to sText
Open sFile For Input As #nSourceFile
getTextFromFile = VBA.Input$(LOF(1), 1)
Close
End Function

Private Function FileExists(ByRef sFile As String) As Boolean
On Error Resume Next
If Not Dir(sFile, vbDirectory) = vbNullString Then
FileExists = True
Exit Function
End If
On Error GoTo 0
FileExists = False
End Function
[/vba]
If anything is unclear, I have more code I can paste in for anything that may not be defined.

#vba #vba7 #vba6

#vba #vba7 #vba6

Вопрос:

У меня возникли проблемы с написанием макроса для сравнения нескольких столбцов на нескольких листах (одного и того же файла Excel). Я написал несколько, но они занимали так много времени, что Excel зависал.

Допустим, у меня есть 4 листа в одном файле. Лист1 с двумя столбцами (B и C) и 7000 строк. Лист2 пустой лист новые записи. Лист3 пустой лист для старых записей, но с некоторым обновленным значением / информацией. Лист4 представляет собой базу данных с 2 столбцами (A и B) и 22000 строками.

Мне нужно сравнить столбец A из листа 1 со столбцом B в листе 4. Если в столбце A sheet1 есть совершенно новые записи, скопируйте эту запись из столбца A sheet1 (и ее соответствующее значение из столбца B sheet1) в новую строку (столбцы A и B) в Sheet2. Если в столбце A Sheet1 есть записи, которые уже есть в столбце A sheet4, затем сравните их соответствующие значения в столбце B. Если комбинация столбца A столбца B из листа 1 находится в листе 4, игнорируйте ее. Если значение из столбца A Sheet1 находится в столбце A Sheet4, но их соответствующие значения в столбце B не совпадают, скопируйте столбец A столбец B из листа 1 в новую строку (столбцы A и B) в листе 3.

Надеюсь, это достаточно ясно. Из-за количества строк (7000 в Sheet1 для сравнения с 20000 в Sheet4) Я не могу написать макрос, который обрабатывает все меньше минуты.

Любая помощь?

Редактирование 1: я использовал код, предложенный @FaneDuru (спасибо!). но я сталкиваюсь с ошибкой: «Ошибка времени выполнения ‘457’: этот ключ уже связан с элементом этой коллекции» Это потому, что у меня много повторяющихся значений в одних и тех же столбцах?

Редактировать 2: похоже, что код «if not dict3.exists» не распознается VBA. Когда я набираю «.exists» с меньшей буквой и перехожу на другую строку, предполагается исправить ее на заглавную «.Exists», верно? Он этого не делает.

Редактировать 3: я провел еще несколько тестов. Я ставил разрывы и запускал код. Когда я ставлю разрыв в этой строке «If WorksheetFunction.CountIf(rngA4, arr1(i, 1))> 0 Тогда», ошибка не возникает. Когда я ставлю разрыв на одну строку ниже «Для j = UBound (arr4) До 1 шага -1» происходит ошибка.

Ошибка: «Ошибка времени выполнения ‘457’: этот ключ уже связан с элементом этой коллекции»

 Private Sub CommandButton1_Click()

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.DisplayAlerts = False

Dim arr1, arr2, arr3, arr4, dict2 As Object, dict3 As Object, rngA4 As Range
Dim rngB4 As Range, i As Long, j As Long, lastR1 As Long, lastR4 As Long

lastR1 = Sheet1.Range("A" amp; Sheet1.Rows.Count).End(xlUp).Row
lastR4 = Sheet4.Range("A" amp; Sheet4.Rows.Count).End(xlUp).Row

Set rngA4 = Sheet4.Range("A2:A" amp; lastR4)
Set rngB4 = Sheet4.Range("B2:B" amp; lastR4)

arr1 = Sheet1.Range("B2:C" amp; lastR1).Value
arr4 = Sheet4.Range("A2:B" amp; lastR4).Value

Set dict2 = CreateObject("Scripting.Dictionary")
Set dict3 = CreateObject("Scripting.Dictionary")

For i = UBound(arr1) To 1 Step -1
    If WorksheetFunction.CountIf(rngB4, arr1(i, 1)) = 0 Then
        dict2.Add arr1(i, 1), arr1(i, 2):
    End If
    If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then
        For j = UBound(arr4) To 1 Step -1
            If arr1(i, 1) = arr4(j, 1) Then
                If arr1(i, 2) <> arr4(j, 2) Then
                    If arr1(i, 2) <> arr4(j, 2) Then
                        dict3.Add arr1(i, 1), arr1(i, 2): Exit For
                End If
            End If
        Next j
    End If
Next i

If dict2.Count > 0 Then
    arr2 = Application.Transpose(Array(dict2.keys, dict2.Items))
    Sheet2.Range("A2").Resize(dict2.Count, 2).Value = arr2
End If

If dict3.Count > 0 Then
    arr3 = Application.Transpose(Array(dict3.keys, dict3.Items))
    Sheet3.Range("A2").Resize(dict3.Count, 2).Value = arr3
End If

MsgBox "Done!"

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.DisplayAlerts = True

End Sub
 

Комментарии:

1. Пожалуйста, отредактируйте свой вопрос и опубликуйте то, что вы пробовали самостоятельно. Даже если он не выполняется точно так, как вам нужно. Также помогут некоторые картинки (если они недоступны для редактирования), показывающие существующую ситуацию, соответственно, нужный вам результат.

2. Что означает «Если в столбце листа 1 есть совершенно новые записи»?

3. «совершенно новые записи в столбце лист1» — означает запись среди этих 7000 строк на листе 1, которой нет среди 20000 строк на листе4.

4. Таким образом, «полностью» не имеет никакого значения… Теперь, возможно ли существование большего количества вхождений строки из листа 1 в столбцах листа 4 B или A? И, для ускорения кода, как обновляются обсуждаемые листы? Я имею в виду, что все время добавляются новые строки, или новые записи могут быть сделаны в любой строке столбца (B или A)?

5. Вы по-прежнему не ответили на уточняющие вопросы, но жалуетесь на «обнаружение ошибки»… Я четко сформулировал предположение «не более одного вхождения». Это не способ помочь нам помочь вам. Ввод On Error Resume Next не является хорошим способом решения проблемы с ошибкой. Вы должны понять, откуда возникает проблема, и решить ее в соответствии с ее корнями. Итак, у вас есть еще такие случаи? Если да, как вам нравится, чтобы код выполнялся в таком случае? Затем сообщать нам, что появляется ошибка, не указывая, в какой строке кода , снова является плохой практикой. Пожалуйста, уточните это

Ответ №1:

Вы можете использовать формулу Excel countif, чтобы найти любую запись данных, которая не существует в вашем наборе данных.

Затем вы можете скопировать значение с помощью Sheets().Range().Value = Sheets().Range().Value на лист, где вы хотите получить свой вывод. Если выходной диапазон уже заполнен, вы можете использовать Sheets().Range().End(xlDown) .Адрес, чтобы найти адрес последней строки вашего выходного набора данных.

Вы перебираете все значения countif, которые возвращают 0, чтобы получить все недостающие данные.

Комментарии:

1. Это я знаю. Но я бы хотел сделать это только с помощью VBA.

2. Vba может получить доступ к функциям Excel, на самом деле обычно быстрее использовать функции Excel с VBA, потому что Excel может выполнять вычисления в нескольких потоках, тогда как VBA не может (вы можете обойти это, но это действительно сложно и не стоит усилий)

3. Ему не нужно (только) знать, существует ли конкретная строка на другом листе. Ему нужно заполнить Лист2 недостающими элементами и Лист3 в некоторых условиях.

4. Справедливо, я должен был добавить что-то об использовании функции поиска или функции фильтра, чтобы найти, какие строки были уникальными

Ответ №2:

Пожалуйста, протестируйте следующий код. Вы не ответили на уточняющие вопросы, и код предполагает, что существует не более одного вхождения, а обработанные листы загружаются путем добавления строк. Код работает независимо от этого аспекта, но если приведенное выше предположение верно, он будет выполняться быстрее:

 Sub testProcessNewEntries()
 Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet, sh4 As Worksheet
 Dim arr1, arr2, arr3, arr4, dict2 As Object, dict3 As Object, rngA4 As Range
 Dim rngB4 As Range, i As Long, j As Long, lastR1 As Long, lastR4 As Long
 
 Set sh1 = Worksheets("Sheet1") 'use here your first sheet
 Set sh2 = Worksheets("Sheet2") 'use here your second sheet
 Set sh3 = Worksheets("Sheet3") 'use here your third sheet
 Set sh4 = Worksheets("Sheet4") 'use here your fourth sheet
 
 lastR1 = sh1.Range("A" amp; sh1.Rows.count).End(xlUp).row
 lastR4 = sh4.Range("A" amp; sh4.Rows.count).End(xlUp).row
  
 Set rngA4 = sh4.Range("A2:A" amp; lastR4)
 Set rngB4 = sh4.Range("B2:B" amp; lastR4)
 
 arr1 = sh1.Range("A2:B" amp; lastR1).Value
 arr4 = sh4.Range("A2:B" amp; lastR4).Value
 
 Set dict2 = CreateObject("Scripting.Dictionary")
 Set dict3 = CreateObject("Scripting.Dictionary")
 
 For i = UBound(arr1) To 1 Step -1
    If WorksheetFunction.CountIf(rngB4, arr1(i, 1)) = 0 Then
        dict2.Add arr1(i, 1), arr1(i, 2):
    End If
    If WorksheetFunction.CountIf(rngA4, arr1(i, 1)) > 0 Then
        For j = UBound(arr4) To 1 Step -1
            If arr1(i, 1) = arr4(j, 1) Then
                If arr1(i, 2) <> arr4(j, 2) Then
                    If Not dict3.Exists(arr1(i, 1)) Then
                        dict3.Add arr1(i, 1), arr1(i, 2): Exit For
                    End If
                End If
            End If
        Next j
    End If
 Next i
 
 If dict2.count > 0 Then
    arr2 = Application.Transpose(Array(dict2.Keys, dict2.Items))
    sh2.Range("A2").Resize(dict2.count, 2).Value = arr2
 End If
 If dict3.count > 0 Then
    arr3 = Application.Transpose(Array(dict3.Keys, dict3.Items))
    sh3.Range("A2").Resize(dict3.count, 2).Value = arr3
 End If
 MsgBox "Ready..."
End Sub
 

Комментарии:

1. @Elmar: Разве вы не нашли немного времени, чтобы проверить приведенный выше код? Это было написано для того, чтобы ответить на ваш вопрос. Если его протестировали, разве он не сделал то, что вам нужно?

2. прежде всего, я хотел бы поблагодарить вас за то, что вы нашли время и помогли мне. Я протестировал код (и все еще тестирую). Я сталкиваюсь с ошибкой «Ошибка времени выполнения ‘457’: этот ключ уже связан с элементом этой коллекции». Это потому, что в моих столбцах много повторяющихся значений?

3. Я добавил «При следующей ошибке возобновить работу», и, похоже, это решило проблему. Как вы думаете, это хороший способ? Надеюсь, это была не важная ошибка, которая создаст беспорядок в моих данных. Кстати, это сработало как по волшебству (если мы проигнорируем ошибку) и очень быстро!!!

4. @Elmar: Пожалуйста, протестируйте обновленный код и убедитесь, что он работает без каких-либо ошибок.

5. Если я не ошибаюсь, единственное изменение, которое вы внесли в код, находится в «самом глубоком» цикле, правильно ?… Если не dict3.Exists(arr1(i, 1)), то dict3 . Добавьте arr1(i, 1), arr1(i, 2): Выход для … Я тестирую его, и ошибка все еще остается. Два наблюдения: 1. Вы правы, ошибка вызвана чем-то в цикле. 2. похоже, что код «if not dict3.exists» не распознается VBA. Когда я набираю «.exists» с меньшей буквой и перехожу на другую строку, предполагается исправить ее на заглавную «.Exists», верно? Он этого не делает.

Like this post? Please share to your friends:
  • Vba excel ошибка 438
  • Vba excel очистка массива
  • Vba excel очистить текст
  • Vba excel очистить содержимое ячейки
  • Vba excel очистить содержимое ячеек