What's the best way to compare two sheets in an Excel workbook - excel

Given I have the following
<Sheet 1>
Item QTY
A 5
B 1
C 3
<Sheet 2>
Item QTY
A 15
B 4
C 1
D 8
What is the best way to generate a report showing the difference between sheet 1 and 2?
Like
<Difference>
Item QTY
A 10
B 3
C -2
D 8

You shouldn't need VBA for this.
Here's what you do:
Create a new worksheet (Sheet3).
Set it up to look like this:
alt text http://img16.imageshack.us/img16/2451/consolidationsheet.jpg
Here are the formulas you will need (paste each one into the proper cell):
Note: the first two are "array formulas" -- after you paste in the formula, double-click the cell and do Ctrl-Shift-Enter (braces {} should appear around the formula)
------------------------------------------------------------------------------
Cell Formula
------------------------------------------------------------------------------
B2 =SUM(IF(Sheet1!A:A="",0,1)) <-- array formula: use Ctrl-Shift-Enter instead of Enter
B3 =SUM(IF(Sheet2!A:A="",0,1)) <-- array formula: use Ctrl-Shift-Enter instead of Enter
D2 =IF(D1=D$1,2,IF(OR(D1=B$2,D1=""),"",D1+1))
E2 =IF(D2="",IF(D1="",IF(OR(E1=B$3,E1=""),"",E1+1),2),"")
G2 =IF(D2<>"",INDEX(Sheet1!A:A,D2),IF(E2<>"",INDEX(Sheet2!A:A,E2),""))
H2 =IF(D2<>"",-INDEX(Sheet1!B:B,D2),IF(E2<>"",INDEX(Sheet2!B:B,E2),""))
Drag the formulas in D2:H2 down as far as you need to cover all the data for sheets 1 and 2.
Select all the data in columns G & H (including the headings).
Do Insert > PivotTable and click OK.
Click the Pivot Table and drag []Item to the Row Labels box and []QTY to the Values box.
That's it. The Pivot Table will contain a summary for each item. No item will be repeated, and no item will be left out. The "Sum of QTY" column will actually contain the difference (since the formula uses negative for all sheet 1 quantities).

In Excel VBA, use a Dictionary. Use your items from one of the sheets as keys, QTY as values. Put the item/QTY pairs of sheet 1 into the dictionary, then run through the items of sheet 2 update the dictionary accordingly to get the differences in there. Finally, put the result into sheet 3.
EDIT: here is a complete example in code (you have to set a reference to the Microsoft Scripting runtime to get it working this way):
Option Explicit
Sub CreateDiff()
Dim dict As New Dictionary
Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet
Dim i As Long, v As String
Set sh1 = ThisWorkbook.Sheets("Sheet1")
Set sh2 = ThisWorkbook.Sheets("Sheet2")
Set sh3 = ThisWorkbook.Sheets("Sheet3")
For i = 2 To sh1.Cells.SpecialCells(xlCellTypeLastCell).Row
v = Trim(sh1.Cells(i, 1).Value)
dict(v) = -sh1.Cells(i, 2).Value
Next
For i = 2 To sh2.Cells.SpecialCells(xlCellTypeLastCell).Row
v = Trim(sh2.Cells(i, 1).Value)
If dict.Exists(v) Then
dict(v) = dict(v) + sh2.Cells(i, 2).Value
Else
dict(v) = sh2.Cells(i, 2).Value
End If
Next
For i = 0 To dict.Count - 1
v = dict.Keys(i)
sh3.Cells(i + 2, 1) = v
sh3.Cells(i + 2, 2) = dict(v)
Next
End Sub

you could merge both sets of data onto a single sheet side-by-side (item1, qty, item2, qty) then use the VLOOKUP() excel function to find the data from the opposite set.

One possibility is to use ADO
Dim cn As Object
Dim rs As Object
Dim strFile As String
Dim strCon As String
Dim strSQL As String
''http://support.microsoft.com/kb/246335
strFile = Workbooks("Book1.xls").FullName
''Note HDR=Yes, the names in the first row of the range
''can be used.
strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open strCon
strSQL = "SELECT s2.Item, s2.Qty-IIf(s1.Qty Is Null,0,s1.Qty) FROM [Sheet2$] s2 " _
& "LEFT JOIN [Sheet1$] s1 ON s2.Item=s1.Item"
rs.Open strSQL, cn, 3, 3
Workbooks("Book1.xls").Worksheets("Sheet3").Cells(2, 1).CopyFromRecordset rs

Why use VBA?
On Sheet 3 comparison sheet list all possible items from sheets 1 and 2 in column A then in Column B use the following formula. Starting in B2 then copy down.
=if(iserror(vlookup(A2,Sheet2'$A$2:$B$5,2,false),0,vlookup(A2,Sheet2'$A$2:$B$5,2,false))-if(iserror(vlookup(A2,Sheet1'$A$2:$B$5,2,false),0,vlookup(A2,Sheet1'$A$2:$B$5,2,false))
Change the table range as necessary.

Related

xlookup on filtered range

I am new in VBA and would like to write a code which fills cells with xlookup formula.
I have 2 tables on different sheets but in the same workbook:
on "New TS" sheet, I need to filter for the TBD-s in col H, and replace them with the exact value based on the data on the "Old TS" sheet.
formula should to be used in the filtered range: =XLOOKUP(1, ('New TS'!C4='Old TS'!C2:C35) * ('New TS'!E4='Old TS'!E2:E35),'Old TS'!G2:G35,"TBD",0)
C4 and E4 can change based on which row contains the first TBD
Last row (now 35) can change based on the table on the Old TS sheet.
I would highly appreciate if you could help me how to add that to my code.
ThisWorkbook.Worksheets("New TS").range("1:1").AutoFilter Field:=8, Criteria1:="TBD"
endrow2 = ThisWorkbook.Worksheets("Old TS").range("G" & Rows.Count).End(xlUp).Row
firstrow = ThisWorkbook.Worksheets("New TS").range("H2:H" & Rows.Count).SpecialCells(xlCellTypeVisible).Cells().Row
ThisWorkbook.Worksheets("New TS").Cells(firstrow, 8) = Application.XLookup(1, (ThisWorkbook.Worksheets("New TS").range(firstrow, 3) = ThisWorkbook.Worksheets("Old TS").range("C2:C" & endrow2)) * (ThisWorkbook.Worksheets("New TS").range(firstrow, 5) = ThisWorkbook.Worksheets("Old TS").range("E2:E" & endrow2)), ThisWorkbook.Worksheets("Old TS").range("G2:G" & endrow2), "TBD", 0)
Please let me know if you need more information about that.
I've never been able to get the XLOOKUP working in the same way as the formula when you have multiple criteria, as in your case.
My own implementation doesn't have to filter the range at all, just look for rows containing TBD.
The other "trick" to the example solution here is in how you find a "matching" row. Your criteria is essentially a combination of data from two columns. Assuming that this combination is always unique, the solution is ideal for a Dictionary. Each "key" to a dictionary entry is this unique combination of the two values.
(Another technique in the example below creates memory-based arrays from the worksheet range to speed processing.)
Option Explicit
Sub ReplaceTBDValues()
'--- capture the data into memory-based arrays
Dim newTSArea As Range
Dim newTS As Variant
Set newTSArea = ThisWorkbook.Sheets("New TS").UsedRange
newTS = newTSArea.Value
Dim oldTSArea As Range
Dim oldTS As Variant
Set oldTSArea = ThisWorkbook.Sheets("Old TS").UsedRange
oldTS = oldTSArea.Value
'--- create a Dictionary of the OldTS values for quick lookup
' the "key" for quick lookup is a combination of the values
' in columns C and E
Dim oldTSDict As Dictionary
Set oldTSDict = New Dictionary
Const NUMBER_COL As Long = 3
Const GROUP2_COL As Long = 5
Const OLD_TITLE_COL As Long = 8
Dim i As Long
For i = 2 To UBound(oldTS, 1) 'skip the header row
Dim tsKey As String
tsKey = oldTS(i, NUMBER_COL) & oldTS(i, GROUP2_COL)
If Not oldTSDict.Exists(tsKey) Then
'--- store the row number in the dictionary
oldTSDict.Add tsKey, i
Else
Debug.Print "Duplicate C/E values in row " & i & "!"
End If
Next i
'--- now run through the lines in New TS and replace the TBD data
For i = 2 To UBound(newTS, 1) 'skip the header row
If newTS(i, OLD_TITLE_COL) = "TBD" Then
Dim checkKey As String
checkKey = newTS(i, NUMBER_COL) & newTS(i, GROUP2_COL)
If oldTSDict.Exists(checkKey) Then
'--- found the values, so replace
newTS(i, OLD_TITLE_COL) = oldTS(oldTSDict(checkKey), OLD_TITLE_COL)
End If
End If
Next i
'--- finally copy the array back to the New TS sheet
newTSArea.Value = newTS
End Sub

Excel Copy and Pasting data into multiple cells

I have an Excel question that comes in two parts: Firstly, if I would like to copy data from one worksheet to another where Sheet 1 has:
A
B
C
D
E
and for Sheet 2 I want every cell to repeat thrice such that when I paste the previous five cells into Sheet 2, each cell appears thrice:
A
A
A
B
B
B
C
C
C
D
D
D
E
E
E
How do I do that? I personally do now know of any formula or function that can let me do that so really looking forward to your advice.
Once that is done, is there a way to write it up using VBA? I am very very new to VBA and was just thinking if it is possible. Otherwise, I will just record a macro. Thank you very much!
With data in Sheet1 like:
In Sheet2, cell A1 enter:
=INDEX(Sheet1!A:A,ROUNDUP(ROW()/3,0))
and copy down:
( if you want 4 copies of each data item, use 4 in the formula)
a VBA way:
Option Explicit
Sub main()
Dim data As Variant, datum As Variant
Dim iDatum As Long, nTimes As Long
With Worksheets("Sheet 1") '<--| reference your "source" worksheet (change "Sheet 1" to your actual "source" sheet name
data = Application.Transpose(.Range("A1", .Cells(.Rows.count, 1).End(xlUp)).Value) '<--| store its column A cells values from row 1 down to last not empty one into an array
End With
nTimes = 3 '<--| set how many times you want to copy the same value
With Worksheets("Sheet 2") '<--| reference your "target" worksheet (change "Sheet 2" to your actual "target" sheet name
.Range("A1").Resize(nTimes) = data(LBound(data)) '<--| write 1st 'data' array value 'nTimes' from its cell A1 down
For iDatum = LBound(data) + 1 To UBound(data) '<--| loop through other 'data' array values
.Cells(.Rows.count, 1).End(xlUp).Offset(1).Resize(nTimes) = data(iDatum) '<--| write them 'nTimes' from first empty row after last not empty one down
Next iDatum
End With
End Sub
Eventually, I did the old-fashioned way of doing it step by step and it worked:
LastRow = have.Cells(have.Rows.Count, "A").End(xlUp).Row
Dim p As Long
pp = 3
For p = 1 To LastRow
want.Range("A" & pp) = have.Range("A" & p).Value
pp = pp + 1
want.Range("A" & pp) = have.Range("A" & p).Value
pp = pp + 1
want.Range("A" & pp) = have.Range("A" & p).Value
pp = pp + 1
Next p

Excel VBA calculate one cell based on string from another

I want to perform a simple calculation on another cell based off a key word in the string from a different cell. For example Column H might have a description along the lines of "Purchase of 50,000 shares." I want to multiple Column B by -1 if the word Purchase appears. Is it possible to build a condition off of a key word in a string?
You can do it in formula or VBA.
In formula, say B1 had content X before, you change to formula
=(X) * IF(ISNUMBER(SEARCH("Purchase", H1)), -1, 1)
In VBA, try this
Sub NegateB_IfPurchaseH()
Dim celB As Range
Dim celH As Range
Dim celPtr As Range
' you can change 1:10 to any other range
Set celB = Range("B1:B10")
Set celH = celB.Cells(1, 7)
For Each celPtr In celB
If InStr(1, celPtr.Cells(1, 7).Value, "purchase", vbTextCompare) > 0 Then
celPtr.Formula = "=-" & Mid(celPtr.Formula, 2)
End If
Next
End Sub

Copy a set of data multiple times based on criteria on another sheet

Excel 2010. I am trying to write a macro that could copy a set of data multiple times based on criteria on another sheet, but I've been stuck for a long time. I very much appreciate any help that could be offered to help me solve this problem.
Step 1: In the "Criteria" worksheet, there are three columns in which each row contains a specific combination of data. The first set of combination is "USD, Car".
Criteria worksheet
Step 2: Then the macro will move to the Output worksheet (please refer to the below link for screenshots), and then filter column A and B with the first set of criteria "USD" and "Car" in the "Criteria" worksheet.
Step 3: Afterwards, the macro will copy the filtered data into the last blank row. But the tricky part here is that, the filtered data has to be copied two times (as the "Number of set" column in the "Criteria" tab is 3 in this combination, and it doesn't have to copy the data three times since the filtered data would be treated as the first set of data)
Step4: After the filtered data have been copied, the "Set" column D will need to fill in the corresponding number of set that the rows are in. Therefore, in this 1st example, cell D2 and D8 will have "1" value, cell D14-15 will have "2" value, and cell D16-17 will have "3" value.
Step5: The macro will then move back to the "Criteria" worksheet and continue to based on the 2nd set of combination "USD, Plane" to filter the data in the "Output" worksheet. Again, it will copy the filtered data based on the "Number of set" in the "Criteria" worksheet. This process will continue until all the different combinations in the "Criteria" worksheet have been processed.
Output worksheet
Ok sorry for delay, here is a working version
you just have to add a sheet called "BF" because the autofilter count wasn't working properly so I had to use another sheet
Sub testfct()
Dim ShC As Worksheet
Set ShC = ThisWorkbook.Sheets("Criteria")
Dim EndRow As Integer
EndRow = ShC.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To EndRow
Get_Filtered ShC.Cells(i, 1), ShC.Cells(i, 2), ShC.Cells(i, 3)
Next i
End Sub
Sub Get_Filtered(ByVal FilterF1 As String, ByVal FilterF2 As String, ByVal NumberSetsDisered As Integer)
Dim NbSet As Integer
NbSet = 0
Dim ShF As Worksheet
Set ShF = ThisWorkbook.Sheets("Output")
Dim ColCr1 As Integer
Dim ColCr2 As Integer
Dim ColRef As Integer
ColCr1 = 1
ColCr2 = 2
ColRef = 4
If ShF.AutoFilterMode = True Then ShF.AutoFilterMode = False
Dim RgTotal As String
RgTotal = "$A$1:$" & ColLet(ShF.Cells(1, Columns.Count).End(xlToLeft).Column) & "$" & ShF.Cells(Rows.Count, 1).End(xlUp).Row
ShF.Range(RgTotal).AutoFilter field:=ColCr1, Criteria1:=FilterF1
ShF.Range(RgTotal).AutoFilter field:=ColCr2, Criteria1:=FilterF2
'Erase Header value, fix? or correct at the end?
ShF.AutoFilter.Range.Columns(ColRef).Value = 1
Sheets("BF").Cells.ClearContents
ShF.AutoFilter.Range.Copy Destination:=Sheets("BF").Cells(1, 1)
Dim RgFilt As String
RgFilt = "$A$2:$B" & Sheets("BF").Cells(Rows.Count, 1).End(xlUp).Row '+ 1
Dim VR As Integer
'Here was the main issue, the value I got with autofilter was not correct and I couldn't figure out why....
'ShF.AutoFilter.Range.SpecialCells(xlCellTypeVisible).Rows.Count
'Changed it to a buffer sheet to have correct value
VR = Sheets("BF").Cells(Rows.Count, 1).End(xlUp).Row - 1
Dim RgDest As String
ShF.AutoFilterMode = False
'Now we need to define Set's number and paste N times
For k = 1 To NumberSetsDisered - 1
'define number set
For j = 1 To VR
ShF.Cells(Rows.Count, 1).End(xlUp).Offset(j, 3) = k + 1
Next j
RgDest = "$A$" & ShF.Cells(Rows.Count, 1).End(xlUp).Row + 1 & ":$B$" & (ShF.Cells(Rows.Count, 1).End(xlUp).Row + VR)
Sheets("BF").Range(RgFilt).Copy Destination:=ShF.Range(RgDest)
Next k
ShF.Cells(1, 4) = "Set"
Sheets("BF").Cells.ClearContents
'ShF.AutoFilterMode = False
End Sub
And the function to get column letter using an integer input :
Function ColLet(x As Integer) As String
With ActiveSheet.Columns(x)
ColLet = Left(.Address(False, False), InStr(.Address(False, False), ":") - 1)
End With
End Function

Adding checkboxes from VBA form into Excel spreadsheet

I am trying to fill a range that is unknown in size, as a user can choose up to 15 items in the first set. This will be inserted into a certain row.
I have a checkbox with the following names/values:
Name Value
========== =====
chk_week1 1
chk_week2 2
... ...
... ...
chk_week15 15
For example if the user selects chk_week1, chk_week2, chk_week4 and chk_week5, then it should be inserted into the cell as 1,2,4,5.
I've included an image how it looks like to better demonstrate it:
Each checkbox has the name and value listed in the table above. Here is the code I am using so far:
Private Sub btnSubmit_Click()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Worksheets("main")
' Copy the data to the database
' Get last empty cell in column A
Set rng1 = ws.Cells(Rows.Count, "a").End(xlUp)
deptCodeSplit = Split(cbo_deptCode.Value, " ")
' Having difficulty adding the code here
' rng1.Offset(1, 6) = weeks
End Sub
Value of a marked checkbox is shown in Linkedcell. Both can be assigned from Checkbox Properties.
Let's assign values from 1 to 15 for every separate checkbox, Linkedcells are in cells A1 to A15, having unique values from 1 to 15 for ticked checkboxes, or are blank for checkboxes which are not selected.
Corresponding cells in column B will be used for sequential merging:
B1:
=IF(A1<>"",A1,"")
B2 to Bn:
=IF(AND(A2<>"",B1<>""),B1&" ,"&A2,IF(AND(B1="",A2<>""),A2,B1))
The formula can be copied downwards indefinitely, the required string will be in the last row.
To accomplish the same in VBA:
Function ValuesFromRange(Rng As range, Optional Delimiter As String)
Dim c As range
Dim txt As String
If Delimiter = "" Then Delimiter = ","
txt = ""
For Each c In Rng
If Len(c.Value) > 0 Then
If Len(txt) = 0 Then
txt = c.Value
Else
txt = Trim(txt) & Delimiter & c.Value
End If
End If
Next
ValuesFromRange = txt
End Function
Spreadsheet example:
http://www.bumpclub.ee/~jyri_r/Excel/ValuesFromRange.xls

Resources