Vba excel ошибка 457

I am working on vba macros. I was trying to use a dictionary. But it is giving error 457 with debugger pointing to toprow.Add ActiveCell.value, val. Can anyone please tell the issue? I even used Cstr(activecell.value), Cstr(val) as mentioned in one of the answer on similar issue.

Dim toprow As New Dictionary, Dictkey As Variant
Dim val As String

Range("A1").Activate 
i = 0
Do Until i = ColLen
    val = Chr(65 + i)
    toprow.Add ActiveCell.value, val
    i = i + 1
    ActiveCell.Offset(0, 1).Activate
Loop

Community's user avatar

asked Feb 5, 2014 at 9:32

Aakash Goyal's user avatar

Aakash GoyalAakash Goyal

1,0514 gold badges12 silver badges44 bronze badges

6

Adding keys with dictionaries is only possible when a key does not already exist. Accidentally you could entered the key before, or you are watching the key with the debug watcher, creating the key instanteneously. (= If you watch a certain key in a dictionary it gets created if it doesn’t already exist).

You have to

  • make sure you are not watching the key with the debugger
  • create unique entries by testing on d.Exists(keyname) and then use the d.Add keyname, value method
  • alternatively you can default to overwrite existing keys by using d.Item(keyname) = value

answered Feb 5, 2014 at 9:41

AutomatedChaos's user avatar

AutomatedChaosAutomatedChaos

7,2672 gold badges27 silver badges47 bronze badges

0

You can also add some very basic error handling, if all you wish to do is skip over the record throwing this error. I simply inserted the below line immediately above the one which was generating this error for me, and now it happily moves along, ignoring duplicate keys which used to throw this error.

On Error Resume Next

answered Nov 13, 2015 at 18:32

bpboldin's user avatar

3

I was getting the same error message: «Error This key is already associated with an element of this collection». In my case, the problem was that I had this:

'assign values to properties
Property Let EmployeeName(Valor As String)
    m_employeename = Valor
End Property
Property Let EmployeeID(Valor As String)
    m_employeename = Valor
End Property

I was supposed to have this:

'assign values to properties
Property Let EmployeeName(Valor As String)
    m_employeename = Valor
End Property
Property Let EmployeeID(Valor As String)
    m_employeeid = Valor
End Property

Maybe you just have to double check your «Property Let» code to see if you are using appropriate names for those variables that are private in your class.

answered Jun 27, 2017 at 22:33

Jaime Montoya's user avatar

Jaime MontoyaJaime Montoya

6,94514 gold badges67 silver badges106 bronze badges

 

ALFA

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

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

#1

13.03.2015 01:35:29

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

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

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

 

Doober

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

Сообщений: 2254
Регистрация: 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
 

SAS888

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

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

#3

13.03.2015 07:53:10

Вместо

Код
ColT.Add 5, 5

используйте

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

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

 

Hugo

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

Сообщений: 23706
Регистрация: 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
 

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», верно? Он этого не делает.

  • #1

Hi All,

The code below used to be running fine but suddenly it started giving me the following error: «Run-time error 457:This key is already associated with an element of this collection.»

Would you please help with such error as I couldn’t find why it is doing that.

Code:

Dim x

Set dic = CreateObject("Scripting.dictionary")
    With Sheets("Test")
    For Each r In .Range("a2", .Range("a65536").End(xlUp))
            If r = Me.cmbcategory.Value And r.Offset(, 2) _
                  = Me.cmbname.Value Then
                If Not dic.exists(r.Offset(, 5).Value) Then
                    dic.Add r.Offset(, 5).Value, Nothing
                    [COLOR="Red"]dic.Add r.Offset(, 6).Value, Nothing[/COLOR]
                    Me.cmbprice.AddItem r.Offset(, 5)
                    Me.cmbserial.AddItem r.Offset(, 6)
                    ReDim Preserve m_price(m_long) As Variant
                    ReDim Preserve m_serial(m_long) As Variant
                    m_price(m_long) = r.Offset(, 5)
                    m_serial(m_long) = r.Offset(, 6)
                    m_long = m_long + 1
                 End If
            End If
        Next
    End With
   End Sub

The error pops up at the line in red above.

Thanks

How can you automate Excel?

Press Alt+F11 from Windows Excel to open the Visual Basic for Applications (VBA) editor.

  • #2

You test if r.Offset(,5) already exists but not if r.Offset(,6) exists before you try and add it (so you could simply test if it exists first). If it does exist, what do you want to do though? Just not add it and carry on with the rest of the code, or something else?

  • #3

Thank you for the reply. Would you please explain more, I think I got lost.

  • #4

This line:

If Not dic.exists(r.Offset(, 5).Value) Then

checks if the value contained in r.Offset(,5) already exists in the dictionary’s Keys. If it doesn’t already exist, then this line adds it to the Keys:

dic.Add r.Offset(, 5).Value, Nothing

That will work fine and you won’t get an error as you never try and add a value into the dictionary that already exists.

However, immediately following the line above, you try and add another value to the dictionary with:

dic.Add r.Offset(, 6).Value, Nothing

But you don’t first check that this value doesn’t already exist in the Dictionary — you get the error because it is already in the Dictionary (ie the value has been added in previously).

If you did check for r.Offset(,6).Value’s existence first, you could skip trying to add an already-existing value. This would mean, however, that your two arrays (m_price and m_serial) would be ‘out of step’. Because I don’t know exactly why you are doing this, I am not sure if this is important to you.

  • #5

Thank you very much for your help.It is now working after eliminating that line.

Thanks again.


Go to excel


r/excel

A vibrant community of Excel enthusiasts. Get expert tips, ask questions, and share your love for all things Excel. Elevate your spreadsheet skills with us!




Members





Online



How exactly to fix macro error Run-time error 457, “This key is already associated with an element of this collection”?


solved

Hi, so I have a macro to copy and paste from one workbook to another. Sometimes it works. Other times I get error ‘457’ “this key is already associated with an element of this collection”. I’m not sure what this means. Like I said sometimes the macro works perfectly and during those times I don’t notice myself doing anything different. Visual basics highlights this for the error. https://i.imgur.com/Ml0aleJ.jpg. Thank you!

Понравилась статья? Поделить с друзьями:
  • Vba excel ошибка 438
  • Vba excel ошибка 361
  • Vba excel обработчик ошибок
  • Vba excel обработка ошибок vba
  • Vba excel коды ошибок