Conditional copy and paste - excel

In Excel I have two worksheets.
The first lists my comments on a Word file each with a code (in the form ABC and a number), a comment, and other information.
Another links these codes to a comment in Word. This has been collected using a macro, which returns the page number of each of the word Comments (which is stored in the second column).
I now want to copy the page numbers into the original worksheet.
My problem is that the two sets of data are not commensurable, so the data can not be simply pasted. Thus ref. A104 in the first worksheet has a page reference (15) in the second workbook, but A105 does not. Also, some references (A106) for instance have multiple page references so occur multiple times in the second workbook but only once in the first.
In the first worksheet, col. A contains the references, and col. F is where I want to paste the page numbers (the other columns contain various other data).
In the second worksheet, the data in col. A corresponds to the refs. in Sheet1:column A, and col. B contains the page references.
In short, I want to copy data from sheet 2:col.B into sheet1:colF only where Sheet 1:col. A corresponds to sheet 2:col A. Is this possible?

Okay. This is not difficult. You could do it too, or at least try to. That's what we value here - seeing efforts. So next time, please, show some effort first and post your code when you actually have a real coding issue.
In your case, I assumed that your sheets are named "Sheet1" and Sheet2".
Don't forget to check the checkmark if the answer is helpful. And please read our Tour page.
Sub ttt()
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Worksheets("Sheet1")
Dim ws2 As Worksheet: Set ws2 = ThisWorkbook.Worksheets("Sheet2")
Dim i As Long, j As Long
Dim str As String
For i = 1 To ws1.UsedRange.Rows.Count
For j = 1 To ws2.UsedRange.Rows.Count
If ws1.Range("A" & i).Value = ws2.Range("A" & j).Value Then
str = str & ws2.Range("B" & j).Value & ", "
End If
Next
If Len(str) > 1 Then
str = Left(str, Len(str) - 2)
ws1.Range("F" & i).Value = str
End If
str = vbNullString
Next
End Sub

Related

Comparing data in Excel

I have exported CSV files from a Development SQL Server and another from Production.
The table (in the database) has two columns
UserID
DocumentID
both of these should be unique values.
I want to be able to verify that those two combinations (together) match the other environment.
So far I imported both CSV files in separate worksheets in Excel.
After this, I am not sure what I should do to compare these columns?
I did a little google-ing and there are so many different types of answers but not sure how to do it.
Conditional Formatting only works if I select a single column. I need to get the combination of both columns.
A quick and mildly dirty VBA-approach. I assumed your workbook consists of two worksheets, each containing two columns with headers.
Option Explicit
Sub SoftwareIsFun()
Dim wks1 As Worksheet
Dim wks2 As Worksheet
Dim dicObj As Object
Dim lastRow1 As Long
Dim lastRow2 As Long
Dim i As Long
Set dicObj = CreateObject("Scripting.Dictionary")
Set wks1 = ThisWorkbook.Worksheets(1)
Set wks2 = ThisWorkbook.Worksheets(2)
With wks1
lastRow1 = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow1
If Not dicObj.Exists(.Range("A" & i).Value) Then
dicObj.Add .Range("A" & i).Value, .Range("B" & i).Value
Else
.Range("C" & i).Value = "UserID already exists"
End If
Next i
End With
With wks2
lastRow2 = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow2
If dicObj.Exists(.Range("A" & i).Value) Then
If .Range("B" & i).Value = dicObj.Item(.Range("A" & i).Value) Then
.Rows(i).Interior.Color = vbGreen
Else
.Rows(i).Interior.Color = vbRed
End If
Else
.Rows(i).Interior.Color = vbRed
End If
Next i
End With
End Sub
What you are describing is something I do daily for my job:
Step 1
Create a 3rd column in both worksheets called "Key" where you'll concatenate the values for Column's A & B as follows:=A2&B2.
Now autofill your rows in column C with the previous formula you've written.
Step 2
Remove duplicates found in this column you've created, this will effectively preserve pairs and prevent information loss when removing duplicate values. (Data Tab -> Remove Duplicates -> Select column C as the criteria to remove them).
Step 3
Make a Vlookup in a 4th column in your first worksheet, the function takes 4 parameters: =vlookup(C2, <4th column of the other worksheet (select entire range from row 2 to end)>, 1, 0) and autofill your rows with the formula.
If you aren't yet familiar with vlookup yet I strongly advice you watch a brief tutorial on its usage, it is an essential tool to compare data.
Any value that matches will be displayed, whereas an #N/D error will print for those which don't match between the 2 tables.

Compare two data ranges and copy entire row into worksheet VBA

i have found many very similar questions in the forum, but somehow nothing fits what i am looking for.
I have two ranges (a & b) which i'd like to compare and if values do not match, i'd like to copy the entire row to a predefined worksheet. The purpose is to find rows / values that have been changed vs. previous edit.
Dim a, b as range
Dim ws1,ws2,ws3 as worksheet
Dim last_row, last_row2 as integer 'assume last_row =15, last_row2=12
Dim i, j, k as integer
last_row=15
last_row2=12
' the orignal range is not massive, but at 500x 6 not small either
Set a=ws1.range("I5:S"& last_row)
Set b=ws2.range("H2:R"& last_row2)
I have seen different approaches when it comes to addressing each item of the range and don't know which would be quickest / best (loop or for each ).
The main if-statement would look something like this:
'assume i, j are the used as counters running across the range
k = 1
If Not a(i).value=b(j).value then
a(i)EntireRow.copy
ws3.row(k).paste
k = k + 1
end if
The solution cannot be formula based, as I need to have ws3 saved after each comparison.
Any help on this is much appreciated. Thanks!
If you have the ability to leverage Excel Spill Ranges, you can achieve what you want without VBA. Here's a web Excel file that shows all rows in first sheet where column A does not equal column b.
=FILTER(Sheet1!A:ZZ,Sheet1!A:A<>Sheet1!B:B)
If VBA is required, this routine should work. It's not optimal for handling values (doesn't use an array), but it gets it done.
Sub listDifferences()
Dim pullWS As Worksheet, pushWS As Worksheet
Set pullWS = Sheets("Sheet1")
Set pushWS = Sheets("Sheet2")
Dim aCell As Range
For Each aCell In Intersect(pullWS.Range("A:A"), pullWS.UsedRange).Cells
If aCell.Value <> aCell.Offset(0, 1).Value Then
Dim lastRow As Long
lastRow = pushWS.Cells(Rows.Count, 1).End(xlUp).Row
pushWS.Rows(lastRow + 1).Value = aCell.EntireRow.Value
End If
Next aCell
End Sub
This is the small for-loop I ended up using.
Thanks for your input!
For i = 1 To rOutput.Cells.Count
If Not rOutput.Cells(i) = rBackUp.Cells(i) Then
' Debug.Print range1.Cells(i)
' Debug.Print range2.Cells(i)
rOutput.Cells(i).EntireRow.Copy wsChangeLog.Rows(k)
k = k + 1
End If
Next i

Using offset within a loop to copy and paste

I have biomechanics data (i.e. a person walking) that is collected with video recordings and stored as 3 columns of data for each frame number. In a separate data table in the same worksheet, I have data that tells me which excel row number each biomechanic event happens (i.e. the foot strikes the ground or comes up off of the ground). I am trying to take the biomechanics data that is stored in one spreadsheet in rows, and transpose it so that each step is in its own column. However, each biomechanic event (i.e. each step) is a different number of rows.
The biomechanics raw data can be seen here: biomechanics raw data
What I want it to do is copy individual events (steps) to a second worksheet like this: desired biomechanic event output
Instead, the code runs through the loop successfully for each biomechanic event, but places the data in the same place, "A4". This is because I do not know how to use offset within a loop and based on a variable: current biomechanic event output with undesired overlay of data
The code I have currently is here:
Private Sub CommandButton1_Click()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim ws2 As Worksheet: Set ws2 = ThisWorkbook.Sheets("Sheet2")
Dim LeftStrike As Range, FrameLTD As Range, FrameLTDx As Range
Dim lrL As Long, LastFrame As Long
Dim LeftTD As Variant
Dim LeftTDx As Variant
lrL = ws.Range("H" & ws.Rows.Count).End(xlUp).Row
LastFrame = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Set LeftStrike = ws.Range("H2:H" & lrL)
For Each FrameLTD In LeftStrike
If InStr(FrameLTD, "Foot Strike") Then
LeftTD = FrameLTD.Offset(0, 2)
'COMMENT: Set LeftTDx = LeftTD + 1
ws.Range("A" & LeftTD, "D" & LastFrame).Copy ws2.Range("A4")
' COMMENT: ws.Range("A" & LeftTDx, "D" & LastFrame).Copy ws2.Range("FrameLTD").Offset(0, 5)
End If
Next FrameLTD
End Sub
What I want the program to do is:
1. Find the string "Foot Strike" and tell me the value 2 columns over and call it LeftTD (it does this).
2. Starting at the row number value of LeftTD, copy cells in columns A through D and past it into a new worksheet starting at A4 (it does this).
3. For the next "Foot Strike" and all remaining, do the same thing in steps 1 and 2, but then copy the cells with an offset(0,5) from the previous copy & pasted event (it does not do this).
4. Do this until column H is empty (it does do this).
My thought was that if I declare the variables as "variant" then I could tell it to count the next LeftTD as LeftTD +1, then tell it to offset the range of the range variable by 5, it would work. But if I un-comment those lines, then I get a type mismatch error. Also, interestingly, if I hit F8 and run through the program step by step, it only copies each biomechanic event (each step) on the 2nd, 5th, 7th, and 9th time through the program.
So my specific question is, how do I use a variable offset within a For loop that is looping through a range variable?
I thought about using .Find and .FindNext, but the information available on the web is really lacking for an example that is close to what I need to do. Indeed, finding information on copy and paste with both variable rows and variable offset columns is also difficult for me to find. Any offered insight would be greatly appreciated. Thank you!!!!
Add a counter to identifying the destination
Dim DestCol As Long
'...
DestCol = 1
For Each FrameLTD In LeftStrike
If InStr(FrameLTD, "Foot Strike") Then
LeftTD = FrameLTD.Offset(0, 2)
ws.Range("A" & LeftTD, "D" & LastFrame).Copy ws2.Cells(4, DestCol)
DestCol = DestCol + 5
End If
Next FrameLTD

Filtering through a list of tables to only include useful lines

Given the lack of elegance with the data
the record sheet continues for many MANY rows, each entry having its own set of identical headings
I was hoping to just extract the data from rows 7, 14 and so on, then populate the data into a simple table to be used on the 'Protocol Summary' form, then sort them all into alphanumeric order based on the data that is in the A column so they all become grouped by 'Event Type'.
Because the potential data that could be under the 'Event Type' heading can vary a lot (generally has the format of [number 1-32/letter/number 1-30] but can also be all letters, with a few thousand possibilities, I thought it might be easier to filter the other lines OUT, given they don't change. I would love to redesign the table, but unfortunately it's not my table so I have to work with what I'm given.
Thanks for your time.
This will loop over your sheet up to the last used row, starting from Row 7 and stepping 7 rows each iteration.
Within each iteration, each cell in the row is written into an array which is then written to another sheet ready for sorting (however you want to do that).
This code is sample and may not work by copy/paste.
I have written this in the Sheet1 code module, so Me refers to ThisWorkbook.Sheets("Sheet1").
I have made this from a blank workbook and did not rename any sheets therefore you will need to make adjustments to any sheet references to match your appropriate sheet names.
The code will only reference columns A, B and C in the TargetRow (I only tested with 3 columns of data as I don't know your working range). I'll reference what to update to extend this after the code block.
Currently the array is put back into Sheet2 starting from cell A2. This is assuming row 1 contains table headers as this will write the data directly into the table format. Naturally if you want to change where the data is written, change the cell it is written to (when writing an array to sheet, you only need to define the top left cell of the range it is written to, Excel works out the rest based on the size and dimensions of the array).
Sub WriteEverySeventhRowToAnotherSheet()
Dim SeventhRowCount As Long
Dim myArray() As Variant
Dim lastrow As Long
Dim TargetCell As Variant
Dim TargetRow As Range
Dim ArrFirstDimension As Long
Dim ArrSecondDimension As Long
lastrow = Me.Range("A" & Me.Rows.Count).End(xlUp).Row
ReDim myArray(1 To lastrow / 7, 1 To 3)
ArrFirstDimension = 1
ArrSecondDimension = 1
'------------------Loop over every 7th row and enter row data to array---------------
For SeventhRowCount = 7 To lastrow Step 7
Set TargetRow = Me.Range("A" & SeventhRowCount & ":C" & SeventhRowCount)
For Each TargetCell In TargetRow
If Not ArrSecondDimension > UBound(myArray) Then
myArray(ArrFirstDimension, ArrSecondDimension) = TargetCell
'Debug.Print TargetCell
ArrSecondDimension = ArrSecondDimension + 1
End If
Next TargetCell
ArrFirstDimension = ArrFirstDimension + 1
ArrSecondDimension = 1
Set TargetRow = Nothing
Next SeventhRowCount
'---------------------Write array to another sheet------------------
Dim Destination As Range
Set Destination = ThisWorkbook.Sheets("Sheet2").Range("A2")
Destination.Resize(UBound(myArray, 1), UBound(myArray, 2)).Value = myArray
End Sub
To extend the number of columns the loop will write to the array, change the following instance of C to the correct column letter (in the below line the range is set from Column A to Column C):
Set TargetRow = Me.Range("A" & SeventhRowCount & ":C" & SeventhRowCount)
Also change the 2nd dimension of the Array to match the number of the Column set above (i.e Column E = 5 and Column L = 13 etc.) - You need to replace the number 3 with the correct number.
ReDim myArray(1 To lastrow / 7, 1 To 3)

Excel VBA Appending data to single Array

Am trying to parse an excel file using Excel VBA.
Here is the sample sata
I did some research and found you can assign ranges to array like
Arrayname = Range("A1:D200")
But am looking for some thing more dynamic, like add the below multiple ranges to a single array.
and my final array will be a single array/table with n is number of rows from all ranges and 4 columns.
Can any one please prvide me a example.
Thank you in adavance.
I think you are asking for more information about moving data between ranges and variables so that is the question I will attempt to answer.
Create a new workbook. Leave Sheet1 empty; set cell B3 of Sheet2 to "abc" and set cells C4 to F6 of Sheet3 to ="R"&ROW()&"C"&COLUMN()
Open the VB Editor, create a module and copy the follow code to it. Run macro Demo01().
Option Explicit
Sub Demo01()
Dim ColURV As Long
Dim InxWkSht As Long
Dim RowURV As Long
Dim UsedRangeValue As Variant
' For each worksheet in the workbook
For InxWkSht = 1 To Worksheets.Count
With Worksheets(InxWkSht)
Debug.Print .Name
If .UsedRange Is Nothing Then
Debug.Print " Empty sheet"
Else
Debug.Print " Row range: " & .UsedRange.Row & " to " & _
.UsedRange.Row + .UsedRange.Rows.Count - 1
Debug.Print " Col range: " & .UsedRange.Column & " to " & _
.UsedRange.Column + .UsedRange.Columns.Count - 1
End If
UsedRangeValue = .UsedRange.Value
If IsEmpty(UsedRangeValue) Then
Debug.Print " Empty sheet"
ElseIf VarType(UsedRangeValue) > vbArray Then
' More than one cell used
Debug.Print " Values:"
For RowURV = 1 To UBound(UsedRangeValue, 1)
Debug.Print " ";
For ColURV = 1 To UBound(UsedRangeValue, 2)
Debug.Print " " & UsedRangeValue(RowURV, ColURV);
Next
Debug.Print
Next
Else
' Must be single cell worksheet
Debug.Print " Value = " & UsedRangeValue
End If
End With
Next
End Sub
The following will appear in the Immediate Window:
Sheet1
Row range: 1 to 1
Col range: 1 to 1
Empty sheet
Sheet2
Row range: 3 to 3
Col range: 2 to 2
Value = abc
Sheet3
Row range: 4 to 6
Col range: 3 to 5
Values:
R4C3 R4C4 R4C5
R5C3 R5C4 R5C5
R6C3 R6C4 R6C5
If you work through the macro and study the output you will get an introduction to loading a range to a variant. The points I particularly want you to note are:
The variable to which the range is loaded is of type Variant. I have never tried loading a single range to a Variant array since the result may not be an array. Even if it works, I would find this confusing.
Sheet1 is empty but the used range tells you than cell A1 is used. However, the variant to which I have loaded the sheet is empty.
The variant only becomes an array if the range contains more than one cell. Note: the array will ALWAYS be two dimensional even if the range is a single row or a single column.
The lower bounds of the array are ALWAYS 1.
The column and row dimensions are not standard with the rows as dimension 1 and the columns as dimension 2.
If there is any doubt about the nature of the range being loaded, you must use IsEmpty and VarType to test its nature.
You may also like to look at: https://stackoverflow.com/a/16607070/973283. Skim the explanations of macros Demo01() and Demo02() which are not relevant to you but set the context. Macro Demo03() shows the advanced technique of loading multiple worksheets to a jagged array.
Now create a new worksheet and leave it with the default name of Sheet4.
Add the follow code to the module. Run macro Demo02().
Sub Demo02()
Dim ColOut As Long
Dim OutputValue() As String
Dim Rng As Range
Dim RowOut As Long
Dim Stg As String
ReDim OutputValue(5 To 10, 3 To 6)
For RowOut = LBound(OutputValue, 1) To UBound(OutputValue, 1)
For ColOut = LBound(OutputValue, 2) To UBound(OutputValue, 2)
OutputValue(RowOut, ColOut) = RowOut + ColOut
Next
Next
With Worksheets("Sheet4")
Set Rng = .Range("A1:D6")
End With
Rng.Value = OutputValue
With Worksheets("Sheet4")
Set Rng = .Range(.Cells(8, 2), .Cells(12, 4))
End With
Rng.Value = OutputValue
With Worksheets("Sheet4")
Stg = "C" & 14 & ":G" & 20
Set Rng = .Range(Stg)
End With
Rng.Value = OutputValue
End Sub
Although this macro writes an array to a worksheet, many of the points apply for the opposite direction. The points I particularly want you to note are:
For output, the array does not have to be Variant nor do the lower bounds have to be 1. I have made OutputValue a String array so the values output are strings. Change OutputValue to a Variant array and rerun the macro to see the effect.
I have used three different ways of creating the range to demonstrate some of your choices.
If you specify a range as I have, the worksheet is one of the properties of the range. That is why I can take Rng.Value = OutputValue outside the With ... End With and still have the data written to the correct worksheet.
When copying from a range to a variant, Excel sets the dimensions of the variant as appropriate. When copying from an array to a range, it is your responsibility to get the size of the range correct. With the second range, I lost data. With the third range, I gained N/As.
I hope the above gives you an idea of your options. If I understand your requirement correctly, you will have to:
Load the entire worksheet to Variant
Create a new Array of the appropriate size
Selectively copy data from the Variant to the Array.
Come back withh questions if anything is unclear.

Resources