the vba code is:
Sub D()
Dim a As String
Dim wb As Workbook
Dim file As Variant
Dim arr1() As Variant
Dim arr2() As Variant
Dim arr3() As Variant
Dim arr() As Variant
Dim arr4() As Variant
Dim arr5() As Variant
Dim arr6() As Variant
Dim t As Integer
ActiveWorkbook.Activate
ActiveSheet.Activate
arr4 = Range("J2:J256")
arr5 = Range("K2:K256")
arr6 = Range("L2:L256")
ActiveSheet.Activate
For Row = 1 To UBound(arr4, 1)
If arr4(Row, 10) = "IS" And arr5(Row, 11) = "IS" And arr6(Row, 12) = "IS" Then
Cells(Row + 1, 13) = "UPDATE AB SET S=" & Cells(Row + 1, 6) & "WHERE C=" & Cells(Row + 1, 3) & ";"
End If
Next Row
End Sub
I am getting error as Subscript Out of range at arr4(Row,10)= when debugged.Can u help in rectifying the error so that the code may be able to function correctly.
I think the problem lies in your understanding of the array. You are using absolute cell references for your array (which is relative). The first index of an array, ie (1, 1), references the first cell in your range, so for Range("K2:K256") arr(1, 1) will be referencing the value of cell "K2", arr(10, 1) will be referencing the value of cell "K11", etc.
As LMM9790 points out, if you wanted to keep your code structure as is then it could simply be written as:
If arr4(Row, 1) = "IS" And arr5(Row, 1) = "IS" And arr6(Row, 1) = "IS" Then
Cells(Row + 1, 13) = "UPDATE AB SET S=" & Cells(Row + 1, 6) & "WHERE C=" & Cells(Row + 1, 3) & ";"
End If
However, I'd have to ask why you need so many arrays, one for each column? Given that arr4, 5 and 6 all have the same row dimension, you could simply have one array that contains all of the columns. Moreover, you could have one array for the entire dataset, amend the applicable value, then rewrite the array to the Worksheet.
Elsewhere the code is a little odd. Is there a reason, for example, that you would activate an active sheet and book? You also have several unused variables - are you intending to use these later?
Your whole code could be simplified to this:
Sub D()
Dim ws as Worksheet
Dim r As Integer
Dim v As Variant
Set ws = ActiveWorkbook.Worksheets("Sheet1") 'name as appropriate
v = ws.Range("C2:M256").Value2
For r = 1 To UBound(v, 1)
If r < Ubound(v, 1) then
If v(r, 8) = "IS" And v(r, 9) = "IS" And v(r, 10) = "IS" Then
v(r + 1, 11) = "UPDATE AB SET S=" & v(r + 1, 4) & _
" WHERE C=" & v(r + 1, 1) & ";"
End If
End If
Next
'...
ws.Range(("C2:M256").Value = v
End Sub
Regarding the out of range error, since arr4, arr5 and arr6 only contain one column you cannot access for example the 10th column of them (which is done in your code by arr4(Row, 10). Does you code work as wanted if you use the following?
Sub D()
Dim a As String
Dim wb As Workbook
Dim file As Variant
Dim Row As Integer
Dim arr1() As Variant
Dim arr2() As Variant
Dim arr3() As Variant
Dim arr() As Variant
Dim arr4() As Variant
Dim arr5() As Variant
Dim arr6() As Variant
Dim t As Integer
ActiveWorkbook.Activate
ActiveSheet.Activate
arr4 = Range("J2:J256")
arr5 = Range("K2:K256")
arr6 = Range("L2:L256")
For Row = 1 To UBound(arr4, 1)
If arr4(Row, 1) = "IS" And arr5(Row, 1) = "IS" And arr6(Row, 1) = "IS" Then
Cells(Row + 1, 13) = "UPDATE AB SET S=" & Cells(Row + 1, 6) & "WHERE C=" & Cells(Row + 1, 3) & ";"
End If
Next Row
End Sub
Related
I have the following requirement I have 2 columns with unique keys called code. In one column below the code, there are one or multiple values present which is the answer. Like in below format
A X
1
2
B Y
9
3
Now the code will have a value populated in next column, while answers wont.
Now I have to find answers for all codes like A, B, C etc. For e.g If I compare with A then answer should be 1,2. I was writing a small subroutine as a beginning but I am facing issues. Can you please correct it
Sub CalculateCellValue()
Dim ValuesBelow As Variant
Dim ValuesRight As String
Dim rows1 As Integer
rows1 = 4
Dim colC As Integer
colC = 2
ValuesRight = ActiveSheet.Cells(rows1 + 1, colC + 1)
While (Not IsEmpty(ValuesRight))
ValuesBelow = ActiveSheet.Cells(rows1 + 1, colC)
rows1 = rows1 + 1
ValuesRight = ActiveSheet.Cells(rows1 + 1, colC + 1)
Wend
MsgBox (ValuesBelow)
End Sub
Purely for an ordered example as shown:
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet5") 'Change as appropriate
Dim myArr()
myArr = ws.Range("A1:B" & GetLastRow(ws, 1)).Value
Dim i As Long
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
For i = LBound(myArr, 1) To UBound(myArr, 1)
If myArr(i, 2) <> vbNullString Then
If Not dict.exists(myArr(i, 1)) Then
Dim currKey As String
currKey = myArr(i, 1)
dict.Add myArr(i, 1), vbNullString
End If
Else
dict(currKey) = dict(currKey) & ", " & myArr(i, 1)
End If
Next i
Dim key As Variant
For Each key In dict
MsgBox key & " = " & Right$(dict(key), Len(dict(key)) - 1)
Next key
End Sub
Public Function GetLastRow(ByVal ws As Worksheet, Optional ByVal columnNumber As Long = 1) As Long
With ws
GetLastRow = .Cells(.Rows.Count, columnNumber).End(xlUp).Row
End With
End Function
I used below code to match my requirement
Function findBelowAll(rows1 As Long)
Dim ValuesBelow() As Variant
ReDim ValuesBelow(1 To 1) As Variant
Dim ValuesRight As Variant
Dim colC As Long
colC = 1
Dim i As Long
ValuesRight = ""
While (ValuesRight = "")
rows1 = rows1 + 1
' change / adjust the size of array
ReDim Preserve ValuesBelow(1 To UBound(ValuesBelow) + 1) As Variant
' add value on the end of the array
ValuesBelow(UBound(ValuesBelow)) =
Worksheets(ActiveSheet.Name).Cells(rows1, colC).Value
ValuesRight = Worksheets(ActiveSheet.Name).Cells(rows1, 2).Value
Wend
For i = LBound(ValuesBelow) To UBound(ValuesBelow) - 1
findBelowAll = findBelowAll & ValuesBelow(i) & vbNewLine
Next i
End Function
I have two spreadsheets (wb and wbtemp); both have a column for location and a column for feature type. In VBA, I want to find all of the rows on the second sheet where the two columns are the same as the two columns on a row in the first sheet and get a list or a range made up of the row numbers/indices.
I then want to use this range to pull out values from a different column and find the highest object in it, but I think I will probably be able to do that if I can get this range sorted.
Dim wb As Workbook
Dim ws As Worksheet
Dim Features() As Variant
Dim Activity() As Variant
Dim Benthic As Variant
Dim wbtemp As Workbook
Dim BenSenFeatures() As Variant
Dim BenSenActivity() As Variant
Dim LR As Long
Dim LC As Long
Dim r As Long
Dim c As Long
Dim WhatToFind1 As Variant
Dim WhatToFind2 As Variant
Dim rngFound1 As Range
Dim rngFound2 As Range
Dim rng1 As Variant
Dim rng2 As Variant
Dim rngFound As Range
Dim iLoop As Long
Dim colFound As Range
Set wb = ActiveWorkbook
Set ws = wb.ActiveSheet
Features = ws.Range("B:C").Value
Activity = ws.Rows(1).Value
Benthic = InputBox("Filename goes here...")
Set wbtemp = Workbooks.Open(Benthic, True, True)
With wbtemp
BenSenFeatures = .Sheets(1).Range("A:B").Value
BenSenActivity = .Sheets(1).Rows(1).Value
End With
LR = ws.Range("C" & Rows.Count).End(xlUp).Row
LC = ws.Cells(1, Columns.Count).End(xlToLeft).Column
For r = 3 To LR
If Not IsEmpty(Features(r, 2)) Then
If IsInArray(Features(r, 2), BenSenFeatures, 2) Then
'If WorksheetFunction.Match(Features(r, 2), BenSenFeatures(0, 2), 0) Then <---I tried to use the arrays originally
WhatToFind1 = Features(r, 1)
WhatToFind2 = Features(r, 2)
Set rngFound1 = wbtemp.Sheets(1).Columns(1).Cells(wbtemp.Sheets(1).Columns(1).Cells.Count)
Set rngFound2 = wbtemp.Sheets(1).Columns(2).Cells(wbtemp.Sheets(1).Columns(2).Cells.Count)
For iLoop = 1 To WorksheetFunction.CountIf(wbtemp.Sheets(1).Columns(1), WhatToFind1)
Set rngFound1 = wbtemp.Sheets(1).Columns(1).Cells.Find(WhatToFind1, After:=rngFound1)
rng1(iLoop) = rngFound1.Row
'WorksheetFunction.Index(wbtemp.Sheets(1).Range("A:B").Value,_
WorksheetFunction.Match(WhatToFind1 & WhatToFind2,_
wbtemp.Sheets(1).Columns(1) & wbtemp.Sheets(1).Columns(2),_
0), 1) <---originally tried to use match to search for the multiple criteria but couldn't find a way to create a list of indices
Set rngFound2 = wbtemp.Sheets(1).Columns(2).Cells.Find(WhatToFind2, After:=rngFound2)
rng2(iLoop) = rngFound2.Row
Next iLoop
For Each cell In rng1
If Not Application.CountIf(rng2, cell.Value) = 0 Then
rngFound.Cells(Cells(Rows.Count, 1).End(xlUp) + 1) = cell.Value
End If
Next
I originally tried to use .Match to find the multiple criteria, but I couldn't figure out how to create a range of indices from it. Then I tried using .Find to create two list of indices but I can't figure out how to get that to work. I keep getting
Type Mismatch
errors.
I realise this sounds confusing, so let me know if anything needs clarifying.
Something like this should work for you. I tried to comment the code for clarity.
Sub tgr()
Dim wb As Workbook
Dim ws As Worksheet
Dim rData As Range
Dim wbTemp As Workbook
Dim wsTemp As Worksheet
Dim rTempData As Range
Dim aData() As Variant
Dim aTempData() As Variant
Dim aResults() As Variant
Dim lNumResults As Long
Dim DataIndex As Long, TempIndex As Long, ResultIndex As Long, j As Long
Dim sCritRange1 As String, sCritRange2 As String
Dim sCriteria1 As String, sCriteria2 As String
Set wb = ActiveWorkbook
'Adjust these two as necessary
Set ws = wb.Sheets(1)
Set rData = ws.Range("B3", ws.Cells(ws.Rows.Count, "B").End(xlUp))
'Select wbTemp file
On Error Resume Next
Set wbTemp = Workbooks.Open(Application.GetOpenFilename("Excel Files, *.xls*"))
On Error GoTo 0
If wbTemp Is Nothing Then Exit Sub 'Pressed cancel
'Adjust these two as necessary
Set wsTemp = wbTemp.Sheets(1)
Set rTempData = wsTemp.Range("A1", wsTemp.Cells(wsTemp.Rows.Count, "A").End(xlUp))
sCritRange1 = rTempData.EntireColumn.Address(external:=True)
sCritRange2 = rTempData.Offset(, 1).EntireColumn.Address(external:=True)
sCriteria1 = rData.Address(external:=True)
sCriteria2 = rData.Offset(, 1).Address(external:=True)
lNumResults = Evaluate("SUMPRODUCT(COUNTIFS(" & sCritRange1 & "," & sCriteria1 & "," & sCritRange2 & "," & sCriteria2 & "))")
If lNumResults = 0 Then Exit Sub 'No matches
ReDim aResults(1 To lNumResults, 1 To 3)
aData = rData.Resize(, 2).Value
aTempData = rTempData.Resize(, 2).Value
'Loop through both data ranges
For DataIndex = LBound(aData, 1) To UBound(aData, 1)
For TempIndex = LBound(aTempData, 1) To UBound(aTempData, 1)
'Find where both criteria matches
If aTempData(TempIndex, 1) = aData(DataIndex, 1) And aTempData(TempIndex, 2) = aData(DataIndex, 2) Then
'Match found, add to results and collect the row index
ResultIndex = ResultIndex + 1
aResults(ResultIndex, 1) = aData(DataIndex, 1)
aResults(ResultIndex, 2) = aData(DataIndex, 2)
aResults(ResultIndex, 3) = "Row: " & TempIndex + rTempData.Row - 1 'This is the row index from wsTemp of the found match
End If
Next TempIndex
Next DataIndex
'Row index results gathered
'Do what you want with the results
'In this example it is just providing msgboxes displaying the results
For ResultIndex = LBound(aResults, 1) To UBound(aResults, 1)
MsgBox "Location: " & aResults(ResultIndex, 1) & Chr(10) & _
"Feature: " & aResults(ResultIndex, 2) & Chr(10) & _
"RowIndex: " & aResults(ResultIndex, 3)
Next ResultIndex
'Close wbTemp
wbTemp.Close
End Sub
I made some minor modifications to tigeravatar's answer to get it to work with my data:
Mainly creating a loop which cycled through each row in wb so that the criteria used with CountIfs was a single value and not a range of values.
I swapped the Evaluate("SUMPRODUCT(COUNTIFS(" & sCritRange1 & "," & sCriteria1 & "," & sCritRange2 & "," & sCriteria2 & "))") for Application.WorksheetFunction.CountIfs(Range(sCritRange1), Range(sCriteria1).Value, Range(sCritRange2), Range(sCriteria2).Value)
I would like to thank tigeravatar for their help.
LR = ws.Range("C" & Rows.Count).End(xlUp).Row
LC = ws.Cells(1, Columns.Count).End(xlToLeft).Column
For r = 3 To LR
sCritRange1 = rTempData.EntireColumn.Address(external:=True)
sCritRange2 = rTempData.Offset(, 1).EntireColumn.Address(external:=True)
sCriteria1 = rData(r, 1).Address(external:=True)
sCriteria2 = rData(r, 1).Offset(, 1).Address(external:=True)
lNumResults = Application.WorksheetFunction.CountIfs(Range(sCritRange1), Range(sCriteria1).Value, Range(sCritRange2), Range(sCriteria2).Value)
If lNumResults = 0 Then Exit Sub 'No matches
ReDim aResults(1 To lNumResults, 1 To 3)
aData = rData(r, 1).Resize(, 2).Value
aTempData = rTempData.Resize(, 2).Value
'Loop through both data ranges
For DataIndex = LBound(aData, 1) To UBound(aData, 1)
For TempIndex = LBound(aTempData, 1) To UBound(aTempData, 1)
'Find where both criteria matches
If Not IsEmpty(aTempData(TempIndex, 1)) Then
If aTempData(TempIndex, 1) = aData(DataIndex, 1) And aTempData(TempIndex, 2) = aData(DataIndex, 2) Then
'Match found, add to results and collect the row index
ResultIndex = ResultIndex + 1
aResults(ResultIndex, 1) = aData(DataIndex, 1)
aResults(ResultIndex, 2) = aData(DataIndex, 2)
aResults(ResultIndex, 3) = "Row: " & TempIndex + rTempData.Row - 1 'This is the row index from wsTemp of the found match
End If
End If
Next TempIndex
Next DataIndex
Next r
I am trying to create a dictionary which only adds a key and item when "ABC" is found in Column N. They key is a unique ID (Concatenated attributes) and the item is a number. If the key already exists in the dictionary I want to sum the existing item with the new item (which have the same key / unique ID).
Dim x, x2, y, y2()
Dim i As Long
Dim dict As Object
Dim LastRowForDict As Long
Dim p As Long
dim ws as worksheet
dim LastRowResult as long
set ws = worksheets("DictionaryTest")
Set dict = CreateObject("Scripting.Dictionary")
With ws
LastRowForDict = .Range("B" & rows.Count).End(xlUp).Row
For p = 1 To LastRowForDict
If ws.Range("N" & p).Value = "ABC" Then 'only adds to dictionary if line is an "ABC" line
x = .Range("H2:H" & LastRowForDict).Value
x2 = .Range("AG2:AG" & LastRowForDict).Value
'Check if key exists and if yes add new value to existing item (SUM them)
''' For i = 1 To UBound(x, 1) should this be here?
If Not dict.Exists(x(p, 1)) Then
dict.Item(x(p, 1)) = x2(p, 1)
Else
dict.Item(x(p, 1)) = CDbl(dict.Item(x(p, 1))) + CDbl(x2(p, 1))
End If
'''next i should this be here?
End If
Next p
End With
'map the values
With ws
LastRowResult = .Range("B" & rows.Count).End(xlUp).Row
y = .Range("H2:H" & LastRowResult).Value 'looks up to this range
ReDim y2(1 To UBound(y, 1), 1 To 1) '<< size the output array
For i = 1 To UBound(y, 1)
If dict.Exists(y(i, 1)) Then
y2(i, 1) = dict(y(i, 1))
Else
y2(i, 1) = ""
End If
Next i
.Range("CK2:CK" & LastRowResult).Value = y2 '<< place the output on the sheet
End With
I currently am getting an error (RunTime 9 - Subscript Out Of Range) on this line If Not dict.Exists(x(p, 1)) Then and this error occurs on the last row of data on my worksheet (ie. It occurs on LastRowForDict). I am thinking this is related to the UBound that I have commented out? I removed it because it causes the code to run from row 1 to UBound / LastRowForDict every time the "outer" if statement is met. By this I mean for every "ABC" line, the code runs through all rows on the sheet and thus creates incorrect items.
Thank you in advance for any help you can offer!
i need to put in a variable a range of values, i.e. the variable
tsPeriod(1) = (3, 4, 5)
tsPeriod(3) = (1, 2, 3).
I don't know what kind of variable to declare and how to do it. I've tried to do something like this:
Dim tsPeriod() as long
ReDim tsPeriod(nSub) as long
for i = 1 to nSub
tsPeriod(i) = (tsStart(i), tsEnd(i))
next
But it doesnt work that way and im kinda lost how to put that "range" into that variable. (if the first value is 3 and the second is 6 i want the variable to retrieve (3, 4, 5, 6))
Below is part of the code:
Dim wb As Workbook
Set wb = ThisWorkbook
Dim subjects As Worksheet
Set subjects = wb.Sheets("Subject")
Dim nSub As Integer, nRooms As Integer
nSub= subjects.Cells(Rows.Count, 1).End(xlUp).value
Dim tsStart() As Long
ReDim tsStart(nSub) As Long
For i = 1 To nSub
tsStart(i) = subjects.Cells(i + 1, 3).value
Next
Dim tsBusy() As Long
ReDim tsBusy(numDis) As Long
For i = 1 To nSub
tsBusy(i) = subjects.Cells(i + 1, 4).value
Next
Dim tsEnd() As Long
ReDim tsEnd(nSub) As Long
For i = 1 To nSub
tsEnd(i) = tsStart(i) + tsBusy(i) - 1
Next
'Here's where im having trouble
Dim tsPeriod() As Long
ReDim tsPeriod(nSub) As Long
For i = 1 To nSub
tsPeriod(i) = (tsStart(i), TsEnd(i))
Next
There is no built-in "range" method in VBA: you need to dimension an array of the required size and fill it using a loop. You can create a function to do this:
Function RRange(startNum, endNum)
Dim rv() as long, i
Redim rv(1 to (endnum-startnum)+1)
for i = startNum to endNum
rv((i-startNum)+1) = i
next i
RRange = rv
End Function
Then:
For i = 1 To numDis
tsPeriod(i) = RRange(tsStart(i), TsEnd(i))
Next
You can use the combination of Application.Transpose, Evaluate and the worksheets-ROW function to get there.
For the
tsPeriod(i) = (tsStart(i), TsEnd(i))
simply use
tsPeriod(i) = Application.Transpose(Evaluate("=ROW(" & tsStart(i) & ":" & tsEnd(i) & ")"))
To get an array from the first to the last value.
To just get a comma-separated string, put this in a Join like this
tsPeriod(i) = Join(Application.Transpose(Evaluate("=ROW(" & tsStart(i) & ":" & tsEnd(i) & ")")), ",")
Join also is good for testing if everything is fine, because you can use Debug.Print.
I am new to Excel VBA Programming. I have one excel sheet with two columns and each column has some email adresses separated by ##. like
ColumA
aa#yahoo.com##bb#yahoo.com##cc#yahoo.com
x#.com##y#y.com
ColumnB
zz#yahoo.com##aa#yahoo.com
aa#yahoo.com
As you can see that both column has two rows, I need 3rd column that should contain all the unique values like
ColumnC
aa#yahoo.com##bb#yahoo.com##cc#yahoo.com#zz#yahoo.com
x#.com##y#y.com##aa#yahoo.com
Thanks
Something like this with variant arrays and a dictionary is an efficient process of getting your desired outcome
[updated to remove delimiter at front of string, code is flexible on delimiter length]
SO seems to have removed the ability to upload image so my picture has fallen off ....
Sub GetUniques()
Dim strDelim As String
Dim X
Dim Y
Dim objDic As Object
Dim lngRow As Long
Dim lngRow2 As Long
strDelim = "##"
Set objDic = CreateObject("scripting.dictionary")
X = Range([a1], Cells(Rows.Count, "B").End(xlUp)).Value2
For lngRow = 1 To UBound(X, 1)
X(lngRow, 1) = X(lngRow, 1) & strDelim & X(lngRow, 2)
Y = Split(X(lngRow, 1), strDelim)
X(lngRow, 1) = vbNullString
For lngRow2 = 0 To UBound(Y, 1)
If Not objDic.exists(lngRow & Y(lngRow2)) Then
X(lngRow, 1) = X(lngRow, 1) & (strDelim & Y(lngRow2))
objDic.Add (lngRow & Y(lngRow2)), 1
End If
Next lngRow2
If Len(X(lngRow, 1)) > Len(strDelim) Then X(lngRow, 1) = Right(X(lngRow, 1), Len(X(lngRow, 1)) - Len(strDelim))
Next lngRow
[c1].Resize(UBound(X, 1), 1).Value2 = X
End Sub
Here's my take. How it works:
Dump columnA and B into a variant array
Combine each row, split into an array of emails, then weed out dupes with a dictionary.
Combine unique list into a single string and store in a new array
Transpose the new array onto column C.
Sub JoinAndUnique()
Application.ScreenUpdating = False
Dim varray As Variant, newArray As Variant
Dim i As Long, lastRow As Long
Dim temp As Variant, email As Variant
Dim newString As String, seperator As String
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
seperator = "##"
lastRow = range("A" & Rows.count).End(xlUp).Row
varray = range("A1:B" & lastRow).Value
ReDim newArray(1 To UBound(varray, 1))
On Error Resume Next
For i = 1 To UBound(varray, 1)
temp = Split(varray(i, 1) & seperator & varray(i, 2), seperator)
For Each email In temp
If Not dict.exists(email) Then
dict.Add email, 1
newString = newString & (seperator & email)
End If
Next
newArray(i) = Mid$(newString, 3)
dict.RemoveAll
newString = vbNullString
Next
range("C1").Resize(UBound(newArray)).Value = Application.Transpose(newArray)
Application.ScreenUpdating = True
End Sub
Note:
It's fairly similar to brettdj's answer, but there are a few differences worth mentioning:
I used more meaninful names for variables (for readability and to make it easier to edit)
I do clean up of the "##" at the start of the sentence
I use a new array rather than overwrite the values of an existing one
I choose to clear the dictionary after each cell
I choose to use "on error resume next" and just dump entries into the dictionary instead of checking if they exist or not (personal preference, makes no major difference)
The easiest way to do this would be to use the dictionary object, split function, and join function. Of course, you don't need to use those exact ones, but give it a try and see what you get.