I have a formula that works fine when hardcoded but gives me error 1004 when I put it in code. I think it's because I am using an array inside the formula. I tried .FormulaArray but it still returns an error.
ws_a.Range("D2:D" & LastRowCriar).Formula = "=IFERROR(IFERROR(IFERROR(INDEX(IBAN!D:D,MATCH(B2&I2,IBAN!F:F&IBAN!E:E,0)),INDEX(IBAN!D:D,MATCH(B2&I2-1,IBAN!F:F&IBAN!E:E,0))),INDEX(IBAN!D:D,MATCH(B2&I2-2,IBAN!F:F&IBAN!E:E,0))),INDEX(IBAN!D:D,MATCH(B2&I2-3,IBAN!F:F&IBAN!E:E,0)))"
Once again, the formula works when hardcoded, I just need some help on how to use it in VBA. Probably, I have to declare those arrays but I am not sure (if I have to or how to do it).
Hardcoded:
=IFERROR(IFERROR(IFERROR(INDEX(IBAN!D:D;MATCH(B2&I2;IBAN!F:F&IBAN!E:E;0));INDEX(IBAN!D:D;MATCH(B2&I2-1;IBAN!F:F&IBAN!E:E;0)));INDEX(IBAN!D:D;MATCH(B2&I2-2;IBAN!F:F&IBAN!E:E;0)));INDEX(IBAN!D:D;MATCH(B2&I2-3;IBAN!F:F&IBAN!E:E;0)))
Thank you!
Replace Slow Formula With VBA
With this setup, the code will write the resulting values to the 4th ("D") column (cSh1C3) in Sheet1 named "Sheet1". The sheet name wasn't mentioned, so change it appropriately, Also change the other constants to fit your needs. Maybe change cSh1C3 to an empty column first, to see if the code does the expected. There will be no formulas just the values since your formula is slowing down the worksheet considerably. If this code is doing the expected, then the formula isn't. In some cases the results differ, but I think the code is correct. When this happens, check the accuracy manually.
Option Explicit
Sub ReplaceSlowFormulaWithVBA()
' Sheet1
Const cSh1 As String = "Sheet1" ' Sheet1 Name
Const cSh1FR As Long = 2 ' Sheet1 First Row Number
Const cSh1C1 As Variant = 2 ' "B" ' Sheet1 First Column Number/Letter
Const cSh1C2 As Variant = 9 ' "I" ' Sheet1 Second Column Number/Letter
Const cSh1C3 As Variant = 4 ' "D" ' Target Column Number/Letter
' (Sheet1 Third Column Number/Letter)
Const cReduce As Long = 3 ' Reduce Number
' Sheet2
Const cSh2 As String = "IBAN" ' Sheet2 Name
Const cSh2FR As Long = 2 ' Sheet2 First Row Number
Const cSh2C1 As Variant = 6 ' "F" ' Sheet2 First Column Number/Letter
Const cSh2C2 As Variant = 5 ' "E" ' Sheet2 Second Column Number/Letter
Const cSh2C3 As Variant = 4 ' "D" ' Source Column Number/Letter
' Sheet2 Third Column Number/Letter
Dim ws1 As Worksheet ' First Worksheet
Dim ws2 As Worksheet ' Second Worksheet
Dim rng As Range ' Various Ranges
Dim vnt1 As Variant ' Sheet1 Array
Dim vnt1C1 As Variant ' Sheet1 First Column Array
Dim vnt1C2 As Variant ' Sheet1 Second Column Array
Dim vntT As Variant ' Target Array (Sheet1 Third Column Array)
Dim vnt2 As Variant ' Sheet2 Array
Dim vnt2C1 As Variant ' Sheet2 First Column Array
Dim vnt2C2 As Variant ' Sheet2 Second Column Array
Dim vntS As Variant ' Source Array (Sheet2 Third Column Array)
Dim LR As Long ' Last Row Compare Number
Dim sh1LR As Long ' Sheet1 (Current) Last Row Number
Dim sh2LR As Long ' Sheet2 (Current) Last Row Number
Dim UB1 As Long ' Sheet1 Arrays Upper Bound
Dim UB2 As Long ' Sheet2 Arrays Upper Bound
Dim i As Long ' Various Counters
Dim j As Long ' Second Array Elements Counter
Dim k As Long ' Reduce Counter
Dim lng1 As Long ' Current Sheet1 Array Value
Dim lng2 As Long ' Current Sheet2 Array Value
' IN RANGES
' Define Worksheets.
Set ws1 = ThisWorkbook.Worksheets(cSh1)
Set ws2 = ThisWorkbook.Worksheets(cSh2)
' Calculate Sheet1 Last Row Number.
Set rng = ws1.Columns(cSh1C1): GoSub LastRow: sh1LR = LR
Set rng = ws1.Columns(cSh1C2): GoSub LastRow
If LR > sh1LR Then sh1LR = LR
' Calculate Sheet2 Last Row Number.
Set rng = ws2.Columns(cSh2C1): GoSub LastRow: sh2LR = LR
Set rng = ws2.Columns(cSh2C2): GoSub LastRow
If LR > sh2LR Then sh2LR = LR
Set rng = ws2.Columns(cSh2C3): GoSub LastRow
If LR > sh2LR Then sh2LR = LR
' Write Column Ranges to Arrays.
vnt1C1 = ws1.Cells(cSh1FR, cSh1C1).Resize(sh1LR - cSh1FR + 1)
vnt1C2 = ws1.Cells(cSh1FR, cSh1C2).Resize(sh1LR - cSh1FR + 1)
vnt2C1 = ws2.Cells(cSh2FR, cSh2C1).Resize(sh2LR - cSh2FR + 1)
vnt2C2 = ws2.Cells(cSh2FR, cSh2C2).Resize(sh2LR - cSh2FR + 1)
vntS = ws2.Cells(cSh2FR, cSh2C3).Resize(sh2LR - cSh2FR + 1)
' Define Target Range.
Set rng = ws1.Cells(cSh1FR, cSh1C3).Resize(sh1LR - cSh1FR + 1)
' Release worksheet object variables.
Set ws2 = Nothing
Set ws1 = Nothing
' IN ARRAYS
' Define and populate Sheet1 Array from the two Sheet1 Column Arrays.
UB1 = UBound(vnt1C1)
ReDim vnt1(1 To UB1) ' 1D 1-based (1-row)
For i = 1 To UB1: vnt1(i) = vnt1C1(i, 1) & vnt1C2(i, 1): Debug.Print vnt1(i): Next i
' Erase the two Sheet1 Column Arrays.
Erase vnt1C1: Erase vnt1C2
' Define and populate Sheet2 Array from the two Sheet2 Column Arrays.
UB2 = UBound(vnt2C1)
ReDim vnt2(1 To UB2) ' 1D 1-based (1-row)
For i = 1 To UB2: vnt2(i) = vnt2C1(i, 1) & vnt2C2(i, 1): Next i
' Erase the two Sheet2 Column Arrays.
Erase vnt2C1: Erase vnt2C2
' Resize Target Array to rows defined by the number of elements
' in Sheet1 Array.
ReDim vntT(1 To UB1, 1 To 1) ' 2D 1-based 1-column
' Loop through elements of Sheet1 Array.
For i = 1 To UB1
If IsNumeric(vnt1(i)) Then
' Loop through Reduce Values.
For k = 0 To cReduce
' Calculate Current Sheet1 Array Value.
lng1 = vnt1(i) - k
' Loop through elements of Sheet2 Array.
For j = 1 To UB2
If IsNumeric(vnt2(j)) Then
' Calculate Current Sheet2 Array Value.
lng2 = vnt2(j)
' Compare current Sheet1 and Sheet2 Array Values.
If lng1 = lng2 Then
' Write value of current element (row) in Source
' Array to current element (row) in Target Array.
vntT(i, 1) = vntS(j, 1)
' Ensure exiting "For k"-loop immediately after
' exiting "For j"-loop.
k = cReduce
' Exit "For j"-loop.
Exit For
End If
End If
Next j
Next k
End If
Next i
' IN RANGES
' Write Target Array to Target Range.
rng = vntT
Exit Sub
LastRow:
LR = 0
Set rng = rng.Find("*", , xlFormulas, , , xlPrevious)
If Not rng Is Nothing Then LR = rng.Row
Return
End Sub
Related
The results I need is shown in this image.
Previous data, and results needed
I have 1 column of data with multiple repeating Values, and need to CHANGE these Values if they repeat more times than a limit I set.
For example, there are "Andre" 3 times in the column, "Bruno" 2 times in the column, and "Charlie" 7 times in the column. The repeat limit is 2. The result I need would be:
Andre 1
Andre 1
Andre 2
Bruno
Bruno
Charlie 1
Charlie 1
Charlie 2
Charlie 2
Charlie 3
Charlie 3
Charlie 4
Bruno is left the same, as it does not EXCEED the limit I set, which is 2. Is this possible to do with VBA?
Note: very early first time learner of VBA.
Append Indexes in a Particular Way
Option Explicit
Sub AppendIndexes()
' Define constants.
' Source
Const sName As String = "Sheet1"
Const sFirstCellAddress As String = "B2"
Const sRowLimitCellAddress As String = "D7"
' Destination
Const dName As String = "Sheet1"
Const dFirstCellAddress As String = "B16"
Const dDelimiter As String = " "
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Reference the source worksheet ('sws').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
' Attempt to store the row limit in a variable ('sRowLimit').
Dim sRowLimit As Long
On Error Resume Next
sRowLimit = sws.Range(sRowLimitCellAddress).Value
On Error GoTo 0
' Validate the row limit.
If sRowLimit < 1 Then
MsgBox "The row limit needs to be an integer greater than 0.", vbCritical
Exit Sub
End If
' Reference the first source cell ('sfCell').
Dim sfCell As Range: Set sfCell = sws.Range(sFirstCellAddress)
Dim srg As Range
' Reference the source (one-column) range ('srg').
With sfCell.CurrentRegion.Columns(sfCell.Column)
Set srg = sfCell.Resize(.Row + .Rows.Count - sfCell.Row)
End With
' Note that there are many different ways to do it.
' To see if it is the correct range you can use e.g.:
'Debug.Print srg.Address(0, 0)
' or:
'MsgBox srg.Address(0, 0)
' Store the number of rows of the source range in a variable ('rCount').
Dim rCount As Long: rCount = srg.Rows.Count
Dim Data() As Variant
' Store the values from the source range
' in a 2D one-based one-column array, the data array ('Data').
If rCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = srg.Value
Else
Data = srg.Value
End If
' Define a new dictionary (dict).
' Its 'keys' will hold the unique strings from the source range.
' Its 'items' will hold the rows in the unique counts array.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case-insensitive i.e. 'A = a'
' Define the unique counts array ('uCounts'), a 1D one-based array
' with the same number of rows as the number of rows in the data array,
' to hold the count of each unique string.
Dim uCounts() As Long: ReDim uCounts(1 To rCount)
' The size of the array is probably too big but the 'mrCount' variable
' will determine the number of rows of interest, the mapping rows count.
Dim r As Long ' Current Data Array Row
Dim mr As Long ' Current Mapping Row
Dim mrCount As Long ' Current and Final Mapping Rows Count
Dim cString As String ' Current Data Array Value Converted to a String
' Loop through the rows of the data array...
For r = 1 To rCount
' Retrieve the current value from the data array converted to a string.
cString = CStr(Data(r, 1))
' Replace the value with the string.
Data(r, 1) = cString
' Check if the string exists in the 'keys' of the dictionary.
If Not dict.Exists(cString) Then
mrCount = mrCount + 1 ' increment the mapping rows count...
dict(cString) = mrCount ' ... and write it to the associated 'item'
mr = mrCount ' retrieve the current mapping row
Else
mr = dict(cString) ' retrieve the current mapping row
End If
' In the current mapping row of the unique counts array,
' increment the number by 1.
uCounts(mr) = uCounts(mr) + 1
Next r
' Define the unique indexes array ('uIndexes'), a 1D one-based array
' with the same number of rows as the mapping rows count ('mrCount'),
' to hold the current index.
Dim uIndexes() As Long: ReDim uIndexes(1 To mrCount)
' Loop through the elements of the unique array and for each value
' greater than the row limit, write 1 to it.
For mr = 1 To mrCount
If uCounts(mr) > sRowLimit Then
uIndexes(mr) = 1
End If
Next mr
Erase uCounts
' Define the indexes counts array ('uIndexes'), a 1D one-based array
' with the same number of rows as the mapping rows count ('mrCount'),
' to hold the current indexes count, a number from 1 to the row limit.
Dim iCounts() As Long: ReDim iCounts(1 To mrCount)
Dim iCount As Long ' Current Index Count
Dim uIndex As Long ' Current Unique Index
' Write the resulting strings to the data array.
' Loop through the rows of the data array.
For r = 1 To rCount
' Retrieve the string from the current row of the data aray.
cString = Data(r, 1)
' Retrieve the mapping row for the current string.
mr = dict(cString)
' Retrieve the unique index for the current mapping row.
uIndex = uIndexes(mr)
If uIndex > 0 Then
' Increment the current index count by 1.
iCount = iCounts(mr) + 1
' Check if the current index count is greater than the row limit.
If iCount > sRowLimit Then ' it is greater
iCount = 1 ' reset the current index count
uIndex = uIndex + 1 ' increment the 'uIndex' by 1, and...
uIndexes(mr) = uIndex ' ... write it to the unique indexes array
'Else ' the current count is not greater than the row limit
End If
iCounts(mr) = iCount ' write the count to the indexes counts array
' Build and write the resulting string to the current row
' of the data array (overwriting the (previous) string).
Data(r, 1) = cString & dDelimiter & CStr(uIndex)
End If
Next r
Erase iCounts
Erase uIndexes
Set dict = Nothing
' Reference the destination worksheet ('dws').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
' Reference the first destination cell ('dfCell').
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
' Reference the destination (one-column) range ('drg').
Dim drg As Range: Set drg = dfCell.Resize(rCount)
' Write the strings from the data array to the destination range.
drg.Value = Data
' Clear below.
drg.Resize(dws.Rows.Count - drg.Row - rCount + 1).Offset(rCount).Clear
' Inform.
MsgBox "Indexes appeded.", vbInformation
End Sub
If you must use VBA:
Sub NumberIfVariableRowLimit()
Dim I As Long 'Iterate
Dim SrcRG As Range 'Source Range
Dim SrcArray() 'Source Array
Dim DestArray() 'Destination array
Dim CellCnt As Long 'Count of cells in range
Dim ItemCnt As Long 'Count keeping track of item number for each name
Dim TempCnt As Long 'Count of occurances of this item in source array
Dim TempStr As String 'Most recently checked item
Dim RowLimit As Long 'Row limit
RowLimit = 3 'Source this data however you like.
'Range/Array Setup
Set SrcRG = ActiveSheet.Range("B1:B28") 'Source Range
CellCnt = SrcRG.Count 'Count cells in source range
SrcArray = SrcRG 'Store range values in array
ReDim DestArray(1 To CellCnt) 'Resize output array
TempStr = "%&%" 'Initial Value
For I = 1 To CellCnt 'Iterate through array
ItemCnt = CountIfArray(SrcArray, SrcArray(I, 1)) 'Get occurances of item in array
'change -or- notchange value, move to output array
If ItemCnt > RowLimit Then
If TempStr = SrcArray(I, 1) Then
TempCnt = TempCnt + 1
Else
TempCnt = RowLimit
End If
DestArray(I) = SrcArray(I, 1) & " " & Application.WorksheetFunction.RoundDown(TempCnt / RowLimit, 0)
Else
DestArray(I) = SrcArray(I, 1)
End If
TempStr = SrcArray(I, 1) 'Store item value for next iteration
Next I
Sheet1.Range("C1").Resize(CellCnt, 1).Value = Application.Transpose(DestArray()) 'output
End Sub
Function CountIfArray(ARR(), vItem) As Long
Dim X As Long
Dim Y As Long
CountIfArray = 0
For X = LBound(ARR, 1) To UBound(ARR, 1)
For Y = LBound(ARR, 2) To UBound(ARR, 2)
If ARR(X, Y) = vItem Then
CountIfArray = CountIfArray + 1
End If
Next Y
Next X
End Function
Example of output:
Example using variable rowlimit:
I'm working on a project with lots of data in two different sheets which is want to combine.
For example:
My Sheet1 should contain 4 columns. Columns 1 and 2 are already filled with ID's and a status.
In Sheet2 I have 3 columns. The first contains the ID's again, the second a serial-number and the third a Yes/No.
The two sheets have around 5500 rows in it. The first a little more then the second.
I would like to run a loop which picks the first ID in Sheet1, checks if it exists in Sheet2, and if it does, it should copy the two missing columns (serial-number and Yes/No) into into Sheet1.
Then the to the next Id in Sheet1 and do the same again.
I tried it with the code below, but I'm not getting it to work.
Hope you can help me out!
Dim i As Long
Dim Found As Range
For i = 1 To Rows.Count
Worksheets("Sheet1").Activate
If Cells(i, 1).Value <> "" Then
Set Found = Worksheets("Sheet2").Range("A2", Range("A")).Find(i, 1)
If Not Found Is Nothing Then
Worksheets("Sheet1").Range(i, 3).Value = Cells(Found.Row, 2).Value
Worksheets("Sheet1").Range(i, 4).Value = Cells(Found.Row, 3).Value
End If
End If
Next i
You could try with two nested for each loops.
Sub copySerial()
Dim range1 As Range, range2 As Range
Set range1 = Worksheets("Sheet1").Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row)
Set range2 = Worksheets("Sheet2").Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row)
For Each c1 In range1
For Each c2 In range2
If c1.Value = c2.Value Then
c1.Offset(0, 2).Value = c2.Offset(0, 1).Value
c1.Offset(0, 3).Value = c2.Offset(0, 2).Value
End If
Next c2
Next c1
End Sub
Arrays Before Ranges
Adjust the values in the constants section to fit your needs. Do it
carefully (slowly) because there are many.
First I created the second code which appeared to be super slow.
After implementing arrays, it got 30 times faster at 5000 records. I guess the extra work pays off.
Option Explicit
Sub UpdateSheetArray() ' Calculates for about 3s at 5000 records - Acceptable!
Const strSrc As String = "Sheet2" ' Source Worksheet Name
Const frSrc As Long = 2 ' Source First Row Number
Const colSrc As Long = 1 ' Source Compare Column Number
Const colSrc1 As Long = 2 ' Source Data Column 1
Const colSrc2 As Long = 3 ' Source Data Column 2
Const strTgt As String = "Sheet1" ' Target Worksheet Name
Const frTgt As Long = 1 ' Target First Row Number
Const colTgt As Long = 1 ' Target Compare Column Number
Const colTgt1 As Long = 3 ' Target Data Column 1
Const colTgt2 As Long = 4 ' Target Data Column 2
Dim wsSrc As Worksheet ' Source Worksheet
Dim wsTgt As Worksheet ' Target Worksheet
Dim vntSrc As Variant ' Source Compare Array
Dim vntSrc1 As Variant ' Source Data Array 1
Dim vntSrc2 As Variant ' Source Data Array 2
Dim vntTgt As Variant ' Target Compare Array
Dim vntTgt1 As Variant ' Target Data Array 1
Dim vntTgt2 As Variant ' Target Data Array 2
Dim rngSrc As Range ' Source Compare Range,
' Source Data Range 1,
' Source Data Range 2
Dim rngTgt As Range ' Target Compare Range,
' Target Data Range 1,
' Target Data Range 2
Dim lrSrc As Long ' Source Last Non-Empty Row Number
Dim lrTgt As Long ' Target Last Non-Empty Row Number
Dim varCur As Variant ' Current Target Cell Value
Dim i As Long ' Source Row Counter
Dim j As Long ' Target Row Counter
' Define Source and Target Worksheets.
Set wsSrc = Worksheets(strSrc)
Set wsTgt = Worksheets(strTgt)
' Calculate Last Non-Empty Row in Source Worksheet.
lrSrc = wsSrc.Columns(colSrc).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
' Calculate Last Non-Empty Row in Target Worksheet.
lrTgt = wsTgt.Columns(colTgt).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
' Define Source Compare Range and write its values to Source Compare Array.
Set rngSrc = wsSrc.Cells(frSrc, colSrc).Resize(lrSrc - frSrc + 1)
vntSrc = rngSrc
' Define Source Data Range 1 and write its values to Source Data Array 1.
Set rngSrc = rngSrc.Offset(, colSrc1 - colSrc): vntSrc1 = rngSrc
' Define Source Data Range 2 and write its values to Source Data Array 2.
Set rngSrc = rngSrc.Offset(, colSrc2 - colSrc1): vntSrc2 = rngSrc
' Define Target Compare Range and write its values to Target Compare Array.
Set rngTgt = wsTgt.Cells(frTgt, colTgt).Resize(lrTgt - frTgt + 1)
vntTgt = rngTgt
' Define Target Data Arrays (same size as Target Compare Array).
ReDim vntTgt1(1 To UBound(vntTgt), 1 To 1)
ReDim vntTgt2(1 To UBound(vntTgt), 1 To 1)
' Note: These last two arrays are going to be written to,
' while the previous four are going to be read from.
' All arrays are 2-dimensional 1-based 1-column arrays.
' Loop through elements of Target Compare Array.
For i = 1 To UBound(vntTgt)
' Write value of current element in Target Array
' to Current Target Cell Value.
varCur = vntTgt(i, 1)
' Check if Current Target Cell Value is not "".
If varCur <> "" Then
' Loop through elements of Source Compare Array.
For j = 1 To UBound(vntSrc)
' Check if value of current element in Source Array is equal
' to Current Target Cell Value.
If vntSrc(j, 1) = varCur Then
' Write current elements in Source Data Arrays
' to Target Data Arrays.
vntTgt1(i, 1) = vntSrc1(j, 1): vntTgt2(i, 1) = vntSrc2(j, 1)
' No need to loop anymore after found.
Exit For
End If
Next
End If
Next
' Define Target Data Range 1.
Set rngTgt = rngTgt.Offset(, colTgt1 - colTgt)
' Write values of Target Data Array 1 to Target Data Range 1.
rngTgt = vntTgt1
' Define Target Data Range 2.
Set rngTgt = rngTgt.Offset(, colTgt2 - colTgt1)
' Write values of Target Data Array 2 to Target Data Range 2.
rngTgt = vntTgt2
End Sub
Sub UpdateSheetRange() ' Calculates for about 90s at 5000 records - too slow!
Const strSrc As String = "Sheet2" ' Source Worksheet Name
Const frSrc As Long = 2 ' Source First Row Number
Const colSrc As Long = 1 ' Source Compare Column Number
Const colSrc1 As Long = 2 ' Source Data Column 1
Const colSrc2 As Long = 3 ' Source Data Column 2
Const strTgt As String = "Sheet1" ' Target Worksheet Name
Const frTgt As Long = 1 ' Target First Row Number
Const colTgt As Long = 1 ' Target Compare Column Number
Const colTgt1 As Long = 3 ' Target Data Column 1
Const colTgt2 As Long = 4 ' Target Data Column 2
Dim wsSrc As Worksheet ' Source Worksheet
Dim wsTgt As Worksheet ' Target Worksheet
Dim lrSrc As Long ' Source Last Non-Empty Row Number
Dim lrTgt As Long ' Target Last Non-Empty Row Number
Dim varCur As Variant ' Current Target Cell Value
Dim i As Long ' Source Row Counter
Dim j As Long ' Target Row Counter
' Define Worksheet.
Set wsSrc = Worksheets(strSrc)
Set wsTgt = Worksheets(strTgt)
' Calculate Last Non-Empty Row in Source Worksheet.
lrSrc = wsSrc.Columns(colSrc).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
' Calculate Last Non-Empty Row in Target Worksheet.
lrTgt = wsTgt.Columns(colTgt).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious).Row
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
On Error GoTo ProgramError
For i = frTgt To lrTgt
varCur = wsTgt.Cells(i, colTgt).Value
If varCur <> "" Then
For j = frSrc To lrSrc
If wsSrc.Cells(j, colSrc).Value = varCur Then
wsTgt.Cells(i, colTgt1) = wsSrc.Cells(j, colSrc1).Value
wsTgt.Cells(i, colTgt2) = wsSrc.Cells(j, colSrc2).Value
Exit For
End If
Next
End If
Next
SafeExit:
With Application
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
ProgramError:
MsgBox "An unexpected error occurred."
On Error GoTo 0
GoTo SafeExit
End Sub
I want to using sumifs function in VBA. And result paste in same column as previous data.
Twice a SUMIF with Overwrite
Workbook
Download
(Dropbox)
Couldn't find any indication of SUMIFS, so I did it as if there is
twice a SUMIF:
For Columns A and C, and for Columns B and D.
Option Explicit
Sub SumUnique(UniqueFirstCell As Range, ValueFirstCell As Range)
Dim rng As Range ' Unique Last Used Cell
Dim dict As Object ' Dictionary
Dim key As Variant ' Dictionary Key Counter (For Each Control Variable)
Dim vntU As Variant ' Unique Range Array
Dim vntV As Variant ' Value Range Array
Dim vntUT As Variant ' Unique Array
Dim vntVT As Variant ' Value Array
Dim curV As Variant ' Current Value
Dim NorS As Long ' Source Number of Rows
Dim NorT As Long ' Target Number of Rows
Dim i As Long ' Source/Target Row Counter
' Copy Unique Range to Unique Range Array.
With UniqueFirstCell
Set rng = .Worksheet.Columns(.Column) _
.Find("*", , xlFormulas, , , xlPrevious)
Set rng = .Resize(rng.Row - .Row + 1)
End With
vntU = rng
' Copy Value Range to Value Range Array.
With ValueFirstCell
Set rng = .Worksheet.Columns(.Column) _
.Find("*", , xlFormulas, , , xlPrevious)
Set rng = .Resize(rng.Row - .Row + 1)
End With
vntV = rng
' Create Unique Values and SumIf Values in Dictionary.
Set dict = CreateObject("Scripting.Dictionary")
NorS = UBound(vntU)
For i = 1 To NorS
curV = vntU(i, 1)
If curV <> "" Then
dict(curV) = dict(curV) + vntV(i, 1)
End If
Next
NorT = dict.Count
' Resize Unique and Value Arrays to Target Number of Rows.
ReDim vntUT(1 To NorT, 1 To 1)
ReDim vntVT(1 To NorT, 1 To 1)
i = 0
For Each key In dict.keys
i = i + 1
' Write Dictionary Keys to Unique Array.
vntUT(i, 1) = key
' Write Dictionary Values to Value Array.
vntVT(i, 1) = dict(key)
Next
' Copy Unique Array to Target Unique Range.
With UniqueFirstCell
Set rng = .Resize(.Worksheet.Rows.Count - .Row + 1)
rng.ClearContents
Set rng = .Resize(NorT)
End With
rng = vntUT
' Copy Value Array to Target Value Range.
With ValueFirstCell
Set rng = .Resize(.Worksheet.Rows.Count - .Row + 1)
rng.ClearContents
Set rng = .Resize(NorT)
End With
rng = vntVT
End Sub
Sub Uni()
Uni1
Uni2
End Sub
Sub Uni1()
Const cUni As String = "A2"
Const cVal As String = "C2"
With ThisWorkbook.Worksheets("Sheet1")
SumUnique .Range(cUni), .Range(cVal)
End With
End Sub
Sub Uni2()
Const cUni As String = "B2"
Const cVal As String = "D2"
With ThisWorkbook.Worksheets("Sheet1")
SumUnique .Range(cUni), .Range(cVal)
End With
End Sub
I created two command buttons and put the following code into the sheet module:
Option Explicit
Private Sub cmdRevert_Click()
[A2:D31] = [J2:M31].Value
End Sub
Private Sub cmdUnique_Click()
Uni
End Sub
You can use SumIfs in VBA through the Application or WorksheetFunction object with VBA style range references. You are only going to want to use it once for each pair of column A and column B values. If you use it once then loop to another row with the same pair of column A and column B values, you cannot use it again without getting false results due to the changes you made the first time.
However, those false results are okay if you are just going to delete them anyways and RemoveDuplicates deletes from the bottom up leaving the top-most column A and column B pairs with the correct totals.
How do I merge cells with the same value and color in a row?
and the result should be :
I think you could try this:
Option Explicit
Sub test()
Dim LastRow As Long, i As Long, Value As Long
Dim Color As Double
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = LastRow To 2 Step -1
Value = .Range("A" & i).Value
Color = .Range("A" & i).Interior.Color
If .Range("A" & i - 1).Value = Value And .Range("A" & i - 1).Interior.Color = Color Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub
Copy Consecutive to One
Adjust the values in the constants section to fit your needs.
The image looks like you want all this to happen in the same column
of the same worksheet, which is adjusted in the constants section.
Before writing to Target Column (cTgtCol), the code will clear its
contents. Be careful not to lose data.
Colors are applied using a loop, which will slow down the fast array approach of copying the data.
The Code
Sub CopyConsecutiveToOne()
' Source
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cSrcCol As Variant = "A" ' Column Letter/Number
Const cSrcFR As Long = 1 ' Column First Row Number
' Target
Const cTarget As Variant = "Sheet1" ' Worksheet Name/Index
Const cTgtCol As Variant = "A" ' Column Letter/Number
Const cTgtFR As Long = 1 ' Column First Row Number
Dim rng As Range ' Source Column Last Used Cell Range,
' Source Column Range, Target Column Range
Dim vntS As Variant ' Source Array
Dim vntT As Variant ' Target Array
Dim vntC As Variant ' Color Array
Dim i As Long ' Source Range/Array Row/Element Counter
Dim k As Long ' Target/Color Array Element Counter
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
'On Error GoTo ProcedureExit
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource).Columns(cSrcCol)
' Calculate Source Column Last Used Cell Range.
Set rng = .Find("*", , xlFormulas, xlWhole, xlByColumns, xlPrevious)
' Check if data in Source Column.
If Not rng Is Nothing Then ' Data found.
' Calculate Source Range.
Set rng = .Range(.Cells(cSrcFR), .Cells(rng.Row))
' Copy values from Source Range to Source Array.
vntS = rng
Else ' Data Not Found.
With .Cells(1)
MsgBox "No Data in column '" & .Split(.Address, "$")(1) & "'."
GoTo ProcedureExit
End With
End If
End With
' In Arrays
' Count the number of elements in Target/Color Array.
k = 1 ' The first element will be included before the loop.
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
End If
Next
' Write to Target/Color Arrays
' Resize Target/Color Arrays.
ReDim vntT(1 To k, 1 To 1)
ReDim vntC(1 To k, 1 To 1)
' Reset Counter
k = 1 ' The first element will be included before the loop.
' Write first value from Source Array to Target Array.
vntT(1, 1) = vntS(1, 1)
' Write first color value to Target Color Array.
vntC(1, 1) = rng.Cells(1, 1).Interior.Color
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
' Write from Source Array to Target Array.
vntT(k, 1) = vntS(i, 1)
' Write color values from Source Range to Color Array.
vntC(k, 1) = rng.Cells(i, 1).Interior.Color
End If
Next
' All necessary data is in Target/Color Arrays.
Erase vntS
Set rng = Nothing
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget).Cells(cTgtFR, cTgtCol)
' Clear contents of range from Target First Cell to Target Bottom Cell.
.Resize(Rows.Count - .Row + 1).ClearContents
' Calculate Target Column Range.
Set rng = .Resize(k)
' Copy Target Array to Target Range.
rng = vntT
' Apply colors to Target Range.
With rng
' Loop through cells of Target Column Range.
For i = 1 To k
' Apply color to current cell of Target Range using the values
' from Color Array.
.Cells(i, 1).Interior.Color = vntC(i, 1)
Next
End With
End With
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Create a custom function in Visual Basic Editor that will return to the color index of the cell:
Function COLOR(Target As Range)
COLOR = Target.Interior.ColorIndex
End Function
Then in the right column use a formula similar to this:
=IF(OR(A2<>A3,COLOR(A2)<>COLOR(A3)),1,0)
Then filter to show only 1's.
I am wondering if someone can help me figure out how to match up data from two sheets, in a specific format. Here is an example of the data I need matched up, including an example showing expected output.
Note that UniqueToGroup_IDs are unique to only the specific Group_ID listed. As you can see, both of the sample Group_IDs I listed contain a UniqueToGroup_ID value of XSTN, which will return two different result IDs; 2306765 for Group_ID 16453, and 8272773 for Group_ID 8156705.
I can (painfully) do this semi-manually, by a combination of Text To Columns, adding the Group_ID to the UniqueToGroup_ID and NotUniqueToGroup_ID, and VLOOKUP -- but it takes forever and I need to do this often.
I haven't tried to write any VBA yet, because I'm not sure how to approach this problem. I am not terribly experienced with coding.
See example here (Dropbox)
Thank you in advance, for any advice.
Crazy Lookup
Links
Workbook Download how-to-match-up-data-from-two-spreadsheets-using-specific-format_54299649.xls
The Code
Sub CrazyLookup()
Const cSheet1 As String = "Original Data" ' 1st Source Worksheet Name
Const cSheet2 As String = "Data To Match" ' 2nd Source Worksheet Name
Const cSheet3 As String = "Sample Result" ' Target Worksheet Name
Const cFirstR As Long = 2 ' First Row Number
Const cFirstC As Variant = "A" ' First Column Letter/Number
Const cLastC As Variant = "C" ' Source Worksheet's Last Column
Const cNoC As Long = 2 ' Number of Columns of Target Array/Range
Const cDel As String = "|" ' Split/Join Delimiter
Dim vnt1 As Variant ' 1st Source Array
Dim vnt2 As Variant ' 2nd Source Array
Dim vnt3 As Variant ' Target Array
Dim vntU As Variant ' Unique Array
Dim lastR1 As Long ' Last Row Number of 1st Source Range
Dim lastR2 As Long ' Last Row Number of 2nd Source Range
Dim i As Long ' 1st Source Array Row Counter
Dim j As Long ' Unique Array Row Counter
Dim k As Long ' 2nd Source Array Row Counter
Application.ScreenUpdating = False
On Error GoTo ProcedureExit
' Write 1st Source Range to 1st Source Array.
With ThisWorkbook.Worksheets(cSheet1)
lastR1 = .Columns(.Cells(1, cFirstC).Column) _
.Find("*", , -4123, , 2, 2).Row
vnt1 = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR1, cLastC))
End With
' Write 2nd Source Range to 2nd Source Array.
With ThisWorkbook.Worksheets(cSheet2)
lastR2 = .Columns(.Cells(1, cFirstC).Column) _
.Find("*", , -4123, , 2, 2).Row
vnt2 = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR2, cLastC))
End With
' Resize Target Array TO 1st Source Array's rows count and TO
' Number of Columns of Target Array.
ReDim vnt3(1 To UBound(vnt1), 1 To cNoC)
' Write First Source Array's First Column to Target Array's first column.
For i = 1 To UBound(vnt1)
vnt3(i, 1) = vnt1(i, 1)
Next
' Write
For i = 1 To UBound(vnt1) ' Loop through rows of 1st Source Array.
' Split 1st Source Array's row in 3rd column to Unique Array.
vntU = Split(vnt1(i, 3), cDel)
For j = 0 To UBound(vntU) ' Loop through rows of Unique Array.
For k = 1 To UBound(vnt2) ' Loop through rows of 2nd Source Array.
' Match 1st Source Array's row in 2nd column TO 2nd Source
' Array's row in first column AND Unique Array's row TO
' 2nd Source Array's row in 2nd column.
If vnt1(i, 2) = vnt2(k, 1) And vntU(j) = vnt2(k, 2) Then
' Write from 2nd Source Array's row in 3rd column to
' Unique Array's row.
vntU(j) = vnt2(k, 3)
Exit For ' Stop searching.
End If
Next
' Check if match was not found.
If k > UBound(vnt2) Then vntU(j) = "NotFound"
Next
' Join Unique Array's rows to Target Array's row in second column.
vnt3(i, 2) = Join(vntU, cDel)
Next
With ThisWorkbook.Worksheets(cSheet3)
' Clear contents of Target Range columns (excl. Headers).
.Range(.Cells(cFirstR, cFirstC), .Cells(.Rows.Count, _
.Cells(1, cFirstC).Column + cNoC - 1)).ClearContents
' Copy Target Array to Target Range.
.Cells(cFirstR, cFirstC).Resize(UBound(vnt3), UBound(vnt3, 2)) = vnt3
End With
ProcedureExit:
Application.ScreenUpdating = True
End Sub
You can build a two column cross reference with a dictionary.
Option Explicit
Sub ertgyhj()
Dim i As Long, ii As String, gi As Long, ugi As String, nuid As Long, r As String
Dim a As Long, itm As String, tmp As String, arr As Variant, xref As Object, results As Object
Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet
Set ws1 = Worksheets("original data")
Set ws2 = Worksheets("data to match")
Set ws3 = Worksheets("sample result")
Set xref = CreateObject("scripting.dictionary")
Set results = CreateObject("scripting.dictionary")
'build two column cross reference dictionary
With ws2
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
itm = Join(Array(.Cells(i, "A").Value2, .Cells(i, "B").Value2), Chr(124))
xref.Item(itm) = .Cells(i, "C").Value2
Next i
End With
'put column header labels into results
results.Item("image_id") = "result"
'collect results
With ws1
'loop through rows
For i = 2 To .Cells(.Rows.Count, "A").End(xlUp).Row
ii = .Cells(i, "A").Value2
gi = .Cells(i, "B").Value2
ugi = .Cells(i, "C").Value2
tmp = vbNullString
arr = Split(ugi, Chr(124))
'loop through UniqueToGroup_ID and find matches
For a = LBound(arr) To UBound(arr)
itm = Join(Array(gi, arr(a)), Chr(124))
If xref.exists(itm) Then
tmp = IIf(CBool(Len(tmp)), tmp & Chr(124), vbNullString) & xref.Item(itm)
End If
Next a
'store concatenated result with image id
results.Item(ii) = tmp
Next i
End With
'post results
With ws3
.Cells(1, "A").Resize(results.Count, 1) = Application.Transpose(results.keys)
.Cells(1, "B").Resize(results.Count, 1) = Application.Transpose(results.items)
End With
End Sub
I built a workbook that I think can solve your problem. Let me know if this helps!
https://www.dropbox.com/s/3h6mja0xtwucbr5/20180121-Matching.xlsm?dl=0