for clarity, see pics and code
Hi,
Having these data:
Table 1 in "customers" sheet
Table 2 in "cars" sheet
I'm able to get the matching value of each row of "customers" to "cars" in a separate sheet "results".
However, I need to achieve 2 things:
Reporting in column A "results" sheet, the A column value of each respective row (therefore extracting this from the individual sheets of "customers" and "cars").
Having a similar table layout with headers denoting the respective columns of results
Col A= Customer/Inventory
Col B= Car
Col C= Color
Col D= Interior
I have been able to achieve up to this stage (pics) from the attached
Sub GenerateTable()
Dim selectedRows As Range
Set selectedRows = ThisWorkbook.Sheets("customers").Range("B2:D9")
Dim resultSheet As Worksheet
On Error Resume Next
Set resultSheet = ThisWorkbook.Sheets("results")
On Error GoTo 0
If resultSheet Is Nothing Then
Set resultSheet = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
resultSheet.Name = "results"
End If
resultSheet.Cells.Clear
Dim carsSheet As Worksheet
Set carsSheet = ThisWorkbook.Sheets("cars")
Dim carsRange As Range
Set carsRange = carsSheet.Range("B2:D13")
Dim rng As Range
Dim row As Range
Dim found As Range
Dim match As Boolean
Dim lastRow As Long
For Each row In selectedRows.Rows
match = False
For Each rng In carsRange.Rows
If row.Cells(1, 1) = rng.Cells(1, 1) And row.Cells(1, 2) = rng.Cells(1, 2) And row.Cells(1, 3) = rng.Cells(1, 3) Then
If match = False Then
lastRow = resultSheet.Cells(Rows.Count, 1).End(xlUp).row + 1
row.Copy resultSheet.Cells(lastRow, 1)
match = True
End If
rng.Copy resultSheet.Cells(lastRow + 1, 1)
lastRow = lastRow + 1
End If
Next rng
Next row
End Sub
Cars
result:
Related
I am starting out with a table containing a name which corresponds to a job code and the start date of said job. See below:
The desired outcome of this is to almost flip it (it is becoming part of a bigger macro, must use VBA for this)
I need dates along the column headings, and the list of unique names. In the column will appear the job for that date. See below for an example:
I have been able to get the code to select all of the rows containing a persons name, however I can't workout how to one by one go through each of the selected rows, copy the job code and paste it to the new table under the correct corresponding date.
Since some jobs have multiple people this code uses InStr() to find occurances of the unqiue names
Sub NewTable()
Dim Rng As Range
Dim Cell As Object
Dim Found As Range
Dim Ws As Worksheet
Set Ws = Worksheets("Sheet1")
Set Rng = Ws.Range("D:D")
searchString = "Emily"
For Each Cell In Rng
If InStr(Cell, searchString) > 0 Then
If Not Found Is Nothing Then
Set Found = Union(myUnion, Cell.EntireRow)
Else
Set Found = Cell.EntireRow
End If
End If
Next
If Found Is Nothing Then
MsgBox "The text was not found in the selection"
Else
Found.Select
End If
End Sub
Any help would be appreciated
Try this out:
Sub Tester()
Dim rw As Range, wsData As Worksheet, wsPivot As Worksheet, arr, e, r, c
Set wsData = ThisWorkbook.Worksheets("Input") 'sheet with original data
Set wsPivot = ThisWorkbook.Worksheets("Pivot") 'sheet for the final table
'loop over each row in the input table
For Each rw In wsData.Range("B6:E" & wsData.Cells(Rows.Count, "B").End(xlUp).Row).Rows
If Application.CountA(rw) = 3 Then 'row has data?
'try to match the date: add as new date if no match
c = Application.Match(CLng(rw.Cells(3).Value), wsPivot.Rows(1), 0)
If IsError(c) Then
c = wsPivot.Cells(1, Columns.Count).End(xlToLeft).Column + 1
If c < 4 Then c = 4 'dates start in D1
wsPivot.Cells(1, c).Value = rw.Cells(3).Value 'add the date
End If
arr = Split(rw.Cells(2).Value, ",") 'get array of all names
'check row for each name: add as new name if no match
For Each e In arr
'look for the name in Col B
r = Application.Match(Trim(e), wsPivot.Columns("B"), 0)
'if name not found, then add it in the next empty cell
If IsError(r) Then
r = wsPivot.Cells(Rows.Count, "B").End(xlUp).Row + 1
If r < 4 Then r = 4 'names begin in B4
wsPivot.Cells(r, "B").Value = e
End If
wsPivot.Cells(r, c).Value = rw.Cells(1).Value 'add the Job Code
Next e
End If
Next rw
End Sub
After countless efforts to keep the array "newvarray" within range, I am now running into a result of an empty array from a 278 line column. I believe this is also the root cause of my endgame function not executing (pasting unmatched values into the rolls sheet)?
Clarification: the actualy empty cells report on locals as "Empty", the columns with string report as " "" "
Dim oldsht As Worksheet
Dim newsht As Worksheet
Dim rollsht As Worksheet
Dim a As Integer
Dim b As Integer
Dim c As Integer
Set oldsht = ThisWorkbook.Sheets("Insert Yesterday's Report Here")
Set newsht = ThisWorkbook.Sheets("Insert Today's Report Here")
Set rollsht = ThisWorkbook.Sheets("Rolls")
Dim OldVArray(), NewVArray(), RollArray() As String
ReDim Preserve OldVArray(1 To oldsht.Range("a" & Rows.Count).End(xlUp).Row - 1, 5 To 5)
ReDim Preserve NewVArray(2 To newsht.Range("a" & Rows.Count).End(xlUp).Row, 5 To 5)
ReDim Preserve RollArray(1 To rollsht.Range("a" & Rows.Count).End(xlUp).Row - 1, 3 To 3)
For a = 2 To oldsht.Range("E" & Rows.Count).End(xlUp).Row
OldVArray(a, 5) = oldsht.Cells(a, 5)
Next a
For b = 2 To newsht.Range("E" & Rows.Count).End(xlUp).Row
NewVArray(b, 5) = newsht.Cells(b, 5)
Next b
For c = 2 To rollsht.Range("C" & Rows.Count).End(xlUp).Row
RollArray(c, 3) = rollsht.Cells(c, 3)
Next c
Dim Voyage As String
For a = 2 To UBound(OldVArray)
Voyage = OldVArray(a, 5)
For b = 2 To UBound(NewVArray)
voyage2 = NewVArray(b, 5)
If voyage2 <> Voyage Then
If voyage2 <> "" Then
For Each cell In NewVArray
voyage2 = rollsheet.Range("C:C")
Next
End If
End If
Next
Next
Here are snips of sample idea, highlighted are the rows that need to be found, and the voyage that changed is in orange. Third on Rolls would be the output of the macro.
Oldsheet:
Newsheet:
Rolls:
Untested, but this is how I'd do it. Just going from your screenshots. If your actual data looks different then you will need to make some adjustments.
Sub test()
Dim wb As Workbook, oldsht As Worksheet, newsht As Worksheet, rollsht As Worksheet
Dim c As Range, id, col, cDest As Range, copied As Boolean, m
Set wb = ThisWorkbook
Set oldsht = wb.Sheets("Insert Yesterday's Report Here")
Set newsht = wb.Sheets("Insert Today's Report Here")
Set rollsht = wb.Sheets("Rolls")
'next empty row on Rolls sheet
Set cDest = rollsht.Cells(Rows.Count, "A").End(xlUp).Offset(1)
'loop colA on new sheet
For Each c In newsht.Range("A2:A" & newsht.Cells(Rows.Count, "A").End(xlUp).row).Cells
id = c.Value 'identifier from Col A
If Len(id) > 0 Then
m = Application.Match(id, oldsht.Columns("A"), 0) 'check for exact match on old sheet
If Not IsError(m) Then
'got a match: check for updates in cols B to C
copied = False
For col = 2 To 3
If c.EntireRow.Cells(col).Value <> oldsht.Cells(m, col).Value Then
If Not copied Then 'already copied this row?
cDest.Resize(1, 3).Value = c.Resize(1, 3).Value 'copy changed row
Set cDest = cDest.Offset(1) ' next empy row
copied = True
End If
cDest.EntireRow.Cells(col).Interior.Color = vbRed 'flag updated value
End If
Next col
Else
cDest.Resize(1, 3).Value = c.Resize(1, 3).Value 'copy new row
Set cDest = cDest.Offset(1) ' next empy row
End If
End If
Next c
End Sub
I'm looking to create a tool which will reconcile values across 3 spreadsheets. For example, Sheet1: ID = 123A, Country = IRELAND; Sheet2: ID = 123A, COUNTRY = UK; Sheet3: ID = 123A, COUNTRY = UK. If it does not match, we will get a FALSE value.
Currently, I have the capacity to reconcile 2 files in columns B & C in shtRecon and am looking to add a 3rd value in column D which will reconcile 3 files instead of 2.
In other words, when column B = column C, there are 0 errors, when column B <> column C, there is 1 error. I'd like it to be column B = column C = column D instead. I've included my working code for 2 column reconciliation beneath. Any ideas?
Sub ReconcileVenueRef()
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim shtRecon As Worksheet
Dim shtSummary As Worksheet
Dim rng1 As Range
Dim rng2 As Range
Dim cel As Range
Dim oDict As Object
Dim reconRow As Long
Dim noOfErrors As Long
Dim ETL As Variant
Dim MiFID As Variant
Dim Match As Boolean
Set sht1 = Worksheets("MiFID Export")
Set sht2 = Worksheets("ETL")
Set shtRecon = Worksheets("Reconciliation")
Set shtSummary = Worksheets("Summary")
'Define the columns that you need, and find the last row
Set rng1 = sht1.Range("A2:B" & sht1.Range("A" & sht1.Rows.Count).End(xlUp).Row)
Set rng2 = sht2.Range("A2:B" & sht2.Range("A" & sht2.Rows.Count).End(xlUp).Row)
'Get a dictionary object holding unique values from sheet 2
'In my case, column A holds the Id, and column B holds the value
Set oDict = GetDictionary(rng2, 1, 4)
reconRow = 0
'Loop over each cel in sheet 1, column 1
'Look for the value in column 1 in the sheet 2 dictionary
'object, and then reconcile
For Each cel In Intersect(rng1, sht1.Columns(1))
'Get the next avail row in reconciliation sheet
reconRow = reconRow + 1
shtRecon.Range("A" & reconRow).Value = cel.Value
'Recon column B holds value from sheet 1, column B
shtRecon.Range("B" & reconRow).Value = cel.Offset(, 3).Value
'If Id is found in Sheet B dictionary, then take the value
'otherwise, write "blank" in column C
'ETL spreadsheet
If oDict.exists(cel.Value) Then
shtRecon.Range("C" & reconRow).Value = oDict(cel.Value)
Else
shtRecon.Range("C" & reconRow).Value = "BLANK"
End If
Next cel
'If ETL cell = MiFID cell then noOfErrors is 0, otherwise it's 1
noOfErrors = 0
ETL = shtRecon.Cells(1, 3).Value
MiFID = shtRecon.Cells(1, 2).Value
If ETL = MiFID Then
Match = True
shtRecon.Cells(1, 4).Value = Match
shtSummary.Cells(4, 3).Value = noOfErrors
ElseIf ETL <> MiFID Then
Match = False
shtRecon.Cells(1, 4).Value = Match
noOfErrors = noOfErrors + 1
shtSummary.Cells(4, 3).Value = noOfErrors
End If
End Sub
'Function stores a range into a dictionary
'Param inputRange takes a range to be stored
'Param idColumn takes the column of the range to be used as the ID
'e.g. if idColumn = 2, and inputRange("C1:F10"), then column D is used for ID
'Param valueColumn points to the column in range used for the value
Function GetDictionary(inputRange As Range, idColumn As Long, valueColumn As Long) As Object
Dim oDict As Object
Dim sht As Worksheet
Dim cel As Range
Set oDict = CreateObject("Scripting.Dictionary")
Set sht = inputRange.Parent
For Each cel In Intersect(inputRange, inputRange.Columns(idColumn))
If Not oDict.exists(cel.Value) Then
oDict.Add cel.Value, sht.Cells(cel.Row, valueColumn).Value
End If
Next cel
Set GetDictionary = oDict
End Function
I'm looking to create a reconciliation sub which will find a value in Spreadsheet A, Column A and return the corresponding value in Column B i.e. Column A = ID123; Column B = HELLO. The procedure will then find the same value in Spreadsheet B, Column A (in this example, ID123) and return the value in Column B. So ideally, I would like these 2 values to be side by side so that I can do a comparison. All I have so far is code that will return values from Column A but I am unable to return Column B. This is essentially a VLookup, but Vlookups have proven to be very consuming in VBA:
Sub findCell()
Dim ETLCell As String
Dim mifidCell As String
Dim last_row_A As Long
Dim last_row_B As Long
last_row_A = Worksheets("Spreadsheet A").UsedRange.Rows.Count
last_row_B = Worksheets("Spreadsheet B").UsedRange.Rows.Count
'Loop which returns the TRN beside each column
For i = 2 To last_row_A
ETLCell = Worksheets("Spreadsheet B").Columns("B:B").Find(What:=Worksheets("Spreadsheet A").Cells(i, 1).Value)
mifidCell = Worksheets("Spreadsheet A").Cells(i, 1).Value
Worksheets("Reconciliation").Cells(i, 1).Value = ETLCell
If ETLCell Is Nothing Then
ETLCell = "BLANK"
Worksheets("Reconciliation").Cells(i, 2).Value = "False"
Else
Worksheets("Reconciliation").Cells(i, 2).Value = "True"
End If
Next i
End Sub
I took a different approach than the one that you took to solve this issue. I tried embedding comments into the code to describe what is happening. Try reviewing it and see if you can incorporate it into your workbook, and write back with questions.
I stored the values from sheet B into a dictionary object for efficient lookup. This type of object holds a unique key, and a corresponding value. Then, I can loop over the Keys in sheet A, looking for them in sheet B, and then adding the values to the reconciliation.
Sub AnalyzeReconciliation()
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim shtRecon As Worksheet
Dim rng1 As Range
Dim rng2 As Range
Dim cel As Range
Dim oDict As Object
Dim reconRow As Long
Set sht1 = Worksheets("Worksheet A")
Set sht2 = Worksheets("Worksheet B")
Set shtRecon = Worksheets("Reconciliation")
'I don't like using UsedRange. I find it to be unreliable
'Define the columns that you need, and find the last row
'using a method call similar to below
Set rng1 = sht1.Range("A2:B" & sht1.Range("A" & sht1.Rows.Count).End(xlUp).Row)
Set rng2 = sht2.Range("A2:B" & sht2.Range("A" & sht2.Rows.Count).End(xlUp).Row)
'Get a dictionary object holding unique values from sheet 2
'In my case, column A holds the Id, and column B holds the value
Set oDict = GetDictionary(rng2, 1, 2)
reconRow = 0
'Loop over each cel in sheet 1, column 1
'Look for the value in column 1 in the sheet 2 dictionary
'object, and then reconcile
For Each cel In Intersect(rng1, sht1.Columns(1))
'Get the next avail row in reconciliation sheet
reconRow = reconRow + 1
shtRecon.Range("A" & reconRow).Value = cel.Value
'Recon column B holds value from sheet 1, column B
shtRecon.Range("B" & reconRow).Value = cel.Offset(, 1).Value
'If Id is found in Sheet B dictionary, then take the value
'otherwise, write "blank" in column C
If oDict.exists(cel.Value) Then
shtRecon.Range("C" & reconRow).Value = oDict(cel.Value)
Else
shtRecon.Range("C" & reconRow).Value = "BLANK"
End If
Next cel
End Sub
'Function stores a range into a dictionary
'Param inputRange takes a range to be stored
'Param idColumn takes the column of the range to be used as the ID
' e.g. if idColumn = 2, and inputRange("C1:F10"), then column D
' is used for ID
'Param valueColumn points to the column in range used for the value
Function GetDictionary(inputRange As Range, idColumn As Long, valueColumn As Long) As Object
Dim oDict As Object
Dim sht As Worksheet
Dim cel As Range
Set oDict = CreateObject("Scripting.Dictionary")
Set sht = inputRange.Parent
For Each cel In Intersect(inputRange, inputRange.Columns(idColumn))
If Not oDict.exists(cel.Value) Then
oDict.Add cel.Value, sht.Cells(cel.Row, valueColumn).Value
End If
Next cel
Set GetDictionary = oDict
End Function
I have a workbook of ten sheets. In sheet 1, I want to list out sheet names (sheets 3 thru 10), column heading values in the sheet (columns 8 and beyond only) and for that column the number of cells that have data in it.
My code works for two of these three requirements. On my sheet 1 (named: SheetName Columns) I get the Sheet Names in column A and Column Heading in column B, however not having any luck getting that sheet/columns number of data rows.
On my sheet 1, column A gets duplicated per number of columns after column 7 on that sheet and that is fine.
Sub ListColumnHeadings()
Dim cNbrs As Long, i As Integer, tr As Long, tc As Long, wst As Worksheet
Dim charList(300, 300) As String
Dim ws As Worksheet, OutputRow As Long
Dim myRange As Range
Dim NumRows As Integer
Dim colNbr As Range
Set shSkip1 = ThisWorkbook.Sheets("SheetName Record Cnt")
Set shList = ThisWorkbook.Sheets("SheetName Columns")
OutputRow = 1
On Error Resume Next
For Each ws In Worksheets
If ws.Name <> shList.Name And ws.Name <> shSkip1.Name Then
cNbrs = ws.Range("A1").CurrentRegion.Columns.Count
For i = 8 To cNbrs
shList.Cells(OutputRow, "A").Value = ws.Name
shList.Cells(OutputRow, "B").Value = ws.Cells(1, i)
Set myRange = ws.Columns(i).Select
NumRows = ws.Application.WorksheetFunction.CountA(myRange)
If NumRows > 0 Then
shList.Cells(OutputRow, "C").Value = NumRows
End If
OutputRow = OutputRow + 1
Next i
End If
Next ws
End Sub
It's because of your use of Set myRange... You don't need to .Select it. Just change that line to Set myRange = ws.Columns(i)
If you want to leave .Select, then the next line should be
NumRows = ws.application.worksheetfunction.counta(selection), but it is highly recommended you avoid using .Select, this is just for your info.