Return column header based on row header and cell value - excel

I have the following grid of data:
---------Header 1 Header 2 Header 3 Header 4
Row 1 x x x
Row 2 x x
Row 3 x
Row 4 x x x x
I then have a second sheet that looks like this:
Row 1 Row 2 Row 3 Row 4
I would like the second sheet to end up looking like this:
Row 1 Row 2 Row 3 Row 4
Header 1 Header 2 Header 3 Header 1
Header 3 Header 3 Header 2
Header 4 Header 3
. Header 4
Ignore that last period, I just used it to format it properly.
I've been playing with MATCH and INDEX for a couple hours and while I can get pieces of it, I can't seem to get it to all work together.
EDIT:
I use 'Header 1' and 'Row 1' as examples only. The actual data is text in Column A and Row 1, respectively. Also, since the source data will be modified, I'd prefer to have something that would automatically update the second sheet.

Here is one way to do it with a VBA function:
In the Developer Tab(*) Click on Visual Basic, then click on the "Insert" menu there and choose "Module" to insert a new module. Then paste in the Following code:
Option Explicit
Public Function GetHeaderMatchingRow(RowText As String, _
SearchRange As Range, _
iHdrNo As Integer) As String
Dim rng As Range
Set rng = SearchRange
Dim cel As Range
'Get the Row to scan
Dim i As Long, rowOff As Long
For i = 2 To rng.Rows.Count
Set cel = rng.Cells(i, 1)
If cel.Value = RowText Then
rowOff = i
Exit For
End If
Next i
'Now, scan horizontally for the iHdrNo'th non-blank cell
Dim cnt As Integer
For i = 2 To rng.Columns.Count
Set cel = rng.Cells(rowOff, i)
If Not CStr(cel.Value) = "" Then
cnt = cnt + 1
If cnt = iHdrNo Then
GetHeaderMatchingRow = rng.Cells(1, i).Value
Exit Function
End If
End If
Next i
GetHeaderMatchingRow = ""
End Function
Click on the "Debug" menu and select "Compile VBAProject".
Now go back to Excel and in your first sheet define a Named Range to cover all of your data in the grid. The Row names should be the first column in this range and the Header text should be the first row in it.
Now go to your second sheet and enter a formula like this in every output cell:
=GetHeaderMatchingRow(A$1, RowHeaderRange, 1)
Where the First parameter is the Row text that it will try to match in the first column of the range. I have "A$1" here because the in my test, my second sheet's column headers are also the Row-names in my first sheet, just like yours.
The second argument is the range to search (in this case, the Named Range we defined earlier), and the third argument is the count of the match that it is looking for (1st, 2nd, 3rd, etc.).
Note that the first and third parameters should change based on what column and row the output is for.

Does it have to use worksheet functions? It would be quite a bit simpler to create a macro to do it (I've made an example)
Edited the function to work with row headers in col a and column headers in row 1 and changed it to read from "Source" sheet and write the result to "Output" sheet
Public Sub Example()
Dim Output As Worksheet
Dim Sheet As Worksheet
Dim Row As Integer
Dim Column As Integer
Set Sheet = ThisWorkbook.Worksheets("Source")
Set Output = ThisWorkbook.Worksheets("Output")
Output.Cells.Clear ' Since were going to rebuild the whole thing, just nuke it.
For Row = Sheet.UsedRange.Rows(Sheet.UsedRange.Rows.Count).Row To 2 Step -1
Output.Cells(1, Row - 1).Value = Sheet.Cells(Row, 1).Value
For Column = Sheet.UsedRange.Columns(Sheet.UsedRange.Columns.Count).Column To 1 Step -1
If Not IsEmpty(Sheet.Cells(Row, Column)) Then
Sheet.Cells(1, Column).Copy
Output.Cells(2, Row - 1).Insert xlShiftDown
End If
Next Column
Next Row
End Sub
I had a look at doing it with worksheet functions and as others have said its going to be pretty tricky to do it without some vba mixed in there.
If you add this to a new module then you can access it as a workbook function. (not that this is the best way to do it, just fancied having a go)
'Return The Column Header of the Nth Non-Blank Cell on Specified Row
Public Function NonBlankByIndex(ByVal Row As Integer, ByVal Index As Integer) As Range
Dim Sheet As Worksheet
Dim Column As Integer
Dim Result As Range
Set Sheet = ThisWorkbook.Worksheets("Source") ' Change to your source sheet's name
Set Result = Nothing
Column = 2 ' Skip 1 as its the header
Do
If Column > Sheet.UsedRange.Columns(Sheet.UsedRange.Columns.Count).Column Then
Exit Do
End If
If Sheet.Cells(Row, Column) = "" Then
Column = Column + 1
Else
If Index = 1 Then
Set Result = Sheet.Cells(1, Column)
Exit Do
Else
Column = Column + 1
Index = Index - 1
End If
End If
Loop
Set NonBlankByIndex = Result
End Function

If you are happy with blanks in the listing try this in sheet2!A2:
=IF(INDEX(Sheet1!$B$2:$E$5,MATCH(A$1,Sheet1!$A$2:$A$5,0),ROW()-1)="x",INDEX(Sheet1!$B$1:$E$1,1,ROW()-1),"")
Just copy the formula over range A2:D5

Related

VBA copy non blank columns based on value

I tried this kind of code, but didnt work
Sub CopyRow()
'Declare variables
Dim DataRg As Range
Dim DataCell As Range
Dim P As Long
Dim J As Long
Dim I As Long
'Set variables
P = Worksheets("Data").UsedRange.Rows.Count
Q = Worksheets("Hello").UsedRange.Rows.Count
''Type If condition to relate the variables I and Q
If I = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Hello").UsedRange) = 0 Then Q = 0
End If
'Set range for Data
Set DataRg = Worksheets("Data").Range("A2:A" & P)
On Error Resume Next
Application.ScreenUpdating = False
'Apply the For loop
For I = 1 To DataRg.Count
'Set Condition for "Hello" value
If CStr(DataRg(I).Value) = "Hello" Then
'Apply command to copy cells
DataRg(I).EntireRow.Copy Destination:=Worksheets("Hello").Range("A" & Q + 1)
Q = Q + 1
End If
Next
Application.ScreenUpdating = True
End Sub
Any idea how to create one module so that I can copy rows (not blank rows)? I have the "data" sheet, and the conditonal value/text is in row A.
For example I want:
-For each cell of A row, if text is "Hello", then copy row (only non blanks) to sheet "Hello", beginning from second row (first row has headers)
-For each cell of A row, if text is "Bye", then copy row (only non blanks) to sheet "Bye", beginning from second row (first row has headers)
-For each cell of A row, if text is "Greetings", then copy row (only non blanks) to sheet "Greeting", beginning from second row (first row has headers)
If it is complicated, I could use 3 different modules for each condition

Table skipping rows when using a userform for data entry

I am trying to create an on call schedule where employees are scheduled for 7 consecutive days on rotation. For example if we have 4 staff, staff member one would be on call for 7 days, then be off for 21 days while staff 2, 3 and 4 are on call, before being back on call for 7 days.
I have the formula for that portion. I am trying to make it dynamic, where employees can be added/ removed and the formula updates to reflect changes.
My plan is to use a table to track employees. I am using a userform to add employees to a table. When an employee is added, they also get a number to be associated with. In the above example of four employees, I would number them 1 to 4 in a separate column.
My issue is, my add to table function skips cells.
My program fills the first row of the table, then skips a cell for the second entry. It then skips 4 cells for the third value, and when I try to enter a 4th or 5th value, the third value is replaced.
Sub AddDataRow(tableName As String, value As Variant)
Dim lastRow As Range
Dim sheet As Worksheet
Dim table As ListObject
Dim col As Integer
Set sheet = ActiveWorkbook.Worksheets("Jobs and Shifts")
Set table = sheet.ListObjects.Item(tableName)
'First check if the last row is empty; if not, add a row
If table.ListRows.Count > 0 Then
Set lastRow = table.ListRows(table.ListRows.Count).Range
For col = 1 To lastRow.Columns.Count
If Trim(CStr(lastRow.Cells(1, col).value)) <> "" Then
table.ListRows.Add
Exit For
End If
Next col
Else
table.ListRows.Add
End If
'Adds values to the table. Column 2 should add a number 1 greater then previous
Set lastRow = table.ListRows(table.ListRows.Count).Range
lastRow.Cells(table.ListRows.Count, 1).value = value
lastRow.Cells(table.ListRows.Count, 2).value = lastRow.Cells((lastRow.Count - 1), 2).value + 1
End Sub
The attached code is my add to table sub. value is the userform entry, generally a string, but I left it as a variant in case I get something else.
The code above mixes up cell ranges and listobject ranges. Work directly with the listobject and it should work.
Sub AddDataRow(tableName As String, value As Variant)
Dim lastRow As ListRow
Dim sheet As Worksheet
Dim table As ListObject
Dim col As Integer
Set sheet = ActiveWorkbook.Worksheets("Jobs and Shifts")
Set table = sheet.ListObjects(tableName)
'First check if the last row is empty; if not, add a row
If table.ListRows.Count > 0 Then
Set lastRow = table.ListRows(table.ListRows.Count)
If worksheetFunction.CountBlank(lastRow.Range) <> lastRow.Columns.Count
Dim useRow as ListRow
Set useRow = table.ListRows.Add
Else
set useRow = lastRow
End If
Else
Set useRow = table.ListRows.Add
End If
useRow.Range(1,1).Value = value
useRow.Range(1,2).Value = useRow.Range(1,2).Offset(-1).Value +1
'apologies if I didn't interpret this correctly. hopefully you can adjust easily
End Sub

VBA Code to link checkboxes to certain columns

I have three columns E(insufficient QTY) F(Too Slow) and G(Not Listed) They all have checkboxes in them. I need to link
E to H
F to I
G to J
The following code works nicely if there was only 1 column of checkboxes but I don't know how to improve the code to run by checkboxes in a certain column. Right now it just searches the entire sheet for checkboxes and links them to the desired column.
Sub LinkChecks()
'Update 20150310
i = 2
For Each cb In ActiveSheet.CheckBoxes
cb.LinkedCell = Cells(i, "I").Address
i = i + 1
Next cb
End Sub
Edit
Ok... let's try again:
Since the Check Box object does not have cell information for the cell it's located in, we will have to use the Offset property more creatively.
Since we know there are 3 check boxes per row, we can find the total number of check boxes and divide by 3 to find out how many rows there are.
Then by setting a Range to a single cell at the top of column "E", you can use the offset property on that cell.
Assuming you placed your Check Boxes on the sheet down column "E" sequentially, and then down column "F" next, then down "G", we can reset the offsets once we get to the last row of each column. (If you place the check boxes on the sheet in row order, you'll have to invert the loop logic.) (If you placed the check boxes on randomly, you are out of luck and will have to set your linked cells manually.)
Sub LinkChecks()
Dim rng As Range
Dim strColumn As String
Dim i As Integer
Dim intCount As Integer
Dim intRowCnt As Integer
Dim intRowOffset As Integer
Dim intColumnOffset As Integer
Dim dCnt As Double
i = 1 ' Your initial row offset
intCount = 0 ' A counter for total number of check boxes
intRowCnt = 0 ' A Row counter to find last row
intRowOffset = i ' Current Row offset from initial rng cell
intColumnOffset = 3 ' Current Column Offset (3 over from first check box column)
strColumn = "E" ' Set a starting Column of your first check box
Set rng = ActiveSheet.Cells(1, strColumn) ' Set initial rng cell
' Count how many check boxes are on the active sheet
For Each cb In ActiveSheet.CheckBoxes
intCount = intCount + 1
Next cb
' Since you know you have 3 check boxes per row,
' you can divide by 3 to get your row count
dCnt = intCount / 3
' *** Put test for remainder problems here ***
For Each cb In ActiveSheet.CheckBoxes
cb.LinkedCell = rng.Offset(intRowOffset, intColumnOffset).Address
intRowOffset = intRowOffset + 1
' Increment your row count until you get to last row
intRowCnt = intRowCnt + 1
If intRowCnt >= dCnt Then
intRowCnt = 0 ' Reset you row counter
intColumnOffset = intColumnOffset + 1 ' Increment Offset to the next column
intRowOffset = i ' Reset Row offset back to top row
End If
Next cb
End Sub
As long as your check boxes were placed on the sheet down each column, the above program should find the correct Linked Cell for each box.
If they were placed in a different order, then at least this code shows you how to set an initial Range cell and how you can reference other cells with an offset.
Hopefully this code or a combination of these ideas will help you with your problem. :)

Better way to find last used row

I am trying to find the last row the same way I found the last column:
Sheets("Sheet2").Cells(1,Sheets("Sheet2").Columns.Count).End(xlToLeft).Column
I know this way but it is not as helpful as the prior would be:
u = Sheets("Sheet1").Range("A65536").End(xlUp).Row
I tried:
Sheets("Sheet2").Cells(Sheets("Sheet2",1).Rowa.Count).End(xlToUP).Column
Synopsis: I would like the below way for last row.
Sheets("Sheet2").Cells(1,Sheets("Sheet2").Columns.Count).End(xlToLeft).Column
You should use a with statement to qualify both your Rows and Columns counts. This will prevent any errors while working with older pre 2007 and newer 2007 Excel Workbooks.
Last Column
With Sheets("Sheet2")
.Cells(1, .Columns.Count).End(xlToLeft).Column
End With
Last Row
With Sheets("Sheet2")
.Range("A" & .Rows.Count).End(xlUp).Row
End With
Or
With Sheets("Sheet2")
.Cells(.Rows.Count, 1).End(xlUp).Row
End With
How is this?
dim rownum as integer
dim colnum as integer
dim lstrow as integer
dim lstcol as integer
dim r as range
'finds the last row
lastrow = ActiveSheet.UsedRange.Rows.Count
'finds the last column
lastcol = ActiveSheet.UsedRange.Columns.Count
'sets the range
set r = range(cells(rownum,colnum), cells(lstrow,lstcol))
This function should do the trick if you want to specify a particular sheet. I took the solution from user6432984 and modified it to not throw any errors. I am using Excel 2016 so it may not work for older versions:
Function findLastRow(ByVal inputSheet As Worksheet) As Integer
findLastRow = inputSheet.cellS(inputSheet.Rows.Count, 1).End(xlUp).Row
End Function
This is the code to run if you are already working in the sheet you want to find the last row of:
Dim lastRow as Integer
lastRow = cellS(Rows.Count, 1).End(xlUp).Row
I use this routine to find the count of data rows. There is a minimum of overhead required, but by counting using a decreasing scale, even a very large result requires few iterations. For example, a result of 28,395 would only require 2 + 8 + 3 + 9 + 5, or 27 times through the loop, instead of a time-expensive 28,395 times.
Even were we to multiply that by 10 (283,950), the iteration count is the same 27 times.
Dim lWorksheetRecordCountScaler as Long
Dim lWorksheetRecordCount as Long
Const sDataColumn = "A" '<----Set to column that has data in all rows (Code, ID, etc.)
'Count the data records
lWorksheetRecordCountScaler = 100000 'Begin by counting in 100,000-record bites
lWorksheetRecordCount = lWorksheetRecordCountScaler
While lWorksheetRecordCountScaler >= 1
While Sheets("Sheet2").Range(sDataColumn & lWorksheetRecordCount + 2).Formula > " "
lWorksheetRecordCount = lWorksheetRecordCount + lWorksheetRecordCountScaler
Wend
'To the beginning of the previous bite, count 1/10th of the scale from there
lWorksheetRecordCount = lWorksheetRecordCount - lWorksheetRecordCountScaler
lWorksheetRecordCountScaler = lWorksheetRecordCountScaler / 10
Wend
lWorksheetRecordCount = lWorksheetRecordCount + 1 'Final answer
This gives you the last used row in a specified column.
Optionally you can specify the worksheet, otherwise it will take the active sheet.
Function getLastRow(col As Integer, Optional ws As Worksheet) As Long
If ws Is Nothing Then Set ws = ActiveSheet
If ws.Cells(ws.Rows.Count, col).Value <> "" Then
getLastRow = ws.Cells(ws.Rows.Count, col).Row
Exit Function
End If
getLastRow = ws.Cells(Rows.Count, col).End(xlUp).Row
If shtRowCount = 1 Then
If ws.Cells(1, col) = "" Then
getLastRow = 0
Else
getLastRow = 1
End If
End If
End Function
Sub test()
Dim lgLastRow As Long
lgLastRow = getLastRow(2) 'Column B
End Sub
This is the best way I've seen to find the last cell.
MsgBox ActiveSheet.UsedRage.SpecialCells(xlCellTypeLastCell).Row
One of the disadvantages to using this is that it's not always accurate. If you use it then delete the last few rows and use it again, it does not always update. Saving your workbook before using this seems to force it to update though.
Using the next bit of code after updating the table (or refreshing the query that feeds the table) forces everything to update before finding the last row. But, it's been reported that it makes excel crash. Either way, calling this before trying to find the last row will ensure the table has finished updating first.
Application.CalculateUntilAsyncQueriesDone
Another way to get the last row for any given column, if you don't mind the overhead.
Function GetLastRow(col, row)
' col and row are where we will start.
' We will find the last row for the given column.
Do Until ActiveSheet.Cells(row, col) = ""
row = row + 1
Loop
GetLastRow = row
End Function
Problems with normal methods
Account for Blank Rows / Columns -
If you have blank rows or columns at the beginning of your data then methods like UsedRange.Rows.Count and UsedRange.Columns.Count will skip over these blank rows (although they do account for any blank rows / columns that might break up the data), so if you refer to ThisWorkbook.Sheets(1).UsedRange.Rows.Count you will skip lines in cases where there are blank rows at the top of your sheet, for example on this sheet:
This will skip the top row from the count and return 11:
ThisWorkbook.Sheets(1).UsedRange.Rows.Count
This code will include the blank row and return 12 instead:
ThisWorkbook.Sheets(1).UsedRange.Cells(ThisWorkbook.Sheets(1).UsedRange.Rows.Count, 1).Row
The same issue applies to columns.
Full Sheets -
Identifying the last row or column can be difficult if your sheet is full (this only matters if either your data contains over a million lines or might have values in the final rows or columns of your data). For example, if you use xlEndUp or similar and the cell you're referring to is populated then the code will skip over data, in extreme cases your entire data set can be skipped if for example the data continues from the last row of the sheet (where you start your xlEndUp) solidly up to the first row (in this case the result would be 1).
'This code works, but...
'Will not function as intended if there is data in the cell you start with (Cell A:1048576).
Dim Sht1 as Range: Set Sht1 = ThisWorkbook.Sheets(1)
Sht1.Cells(Sht1.Rows.Count, 1).End(xlUp).Row
Columns with blank rows -
The above code also assumes that your data extends the entire way down column 1, if you have blank entries in column 1 you may lose rows as the code will find the first filled row from the bottom only for column 1.
Unnecessary Looping -
Self explanatory, best to avoid looping where possible as if you're dealing with a lot of data and repeating the looping process often it can slow down your code.
Solution
Note that this is targeted at finding the last "Used" Row or Column on an entire sheet, this doesn't work if you just want the last cell in a specific range.
I've setup some Functions here
Private Function GetLastRow(Sheet As Worksheet)
'Gets last used row # on sheet.
GetLastRow = Sheet.UsedRange.Cells(Sheet.UsedRange.Rows.Count, 1).Row
End Function
Private Function GetLastCol(Sheet As Worksheet)
'Gets last used column # on sheet.
GetLastCol = Sheet.UsedRange.Cells(1, Sheet.UsedRange.Columns.Count).Column
End Function
Examples of calling these Functions:
Sub CallFunctions()
'Define the Target Worksheet we're interested in:
Dim Sht1 As Worksheet: Set Sht1 = ThisWorkbook.Sheets(1)
'Print the last row and column numbers:
Debug.Print "Last Row = "; GetLastRow(Sht1)
Debug.Print "Last Col = "; GetLastCol(Sht1)
End Sub
I preferred search last blank cell:
Il you want last empty cell of column you can do that
Dim sh as Worksheet, r as range
set sh = ActiveWorksheet 'if you want an other it's possible
'find a value
'Columns("A:D") 'to check on multiple columns
Set r = sh.Columns("A").Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
'no value return first row
If r Is Nothing Then Set r = sh.Cells(1, "A") Else Set r = sh1.Cells(r.Row + 1, "A")
If this is to insert new row, find on multiple columns is a good choice because first column can contains less rows than next columns
I use the following function extensively. As pointed out above, using other methods can sometimes give inaccurate results due to used range updates, gaps in the data, or different columns having different row counts.
Example of use:
lastRow=FindRange("Sheet1","A1:A1000")
would return the last occupied row number of the entire range. You can specify any range you want from single columns to random rows, eg FindRange("Sheet1","A100:A150")
Public Function FindRange(inSheet As String, inRange As String) As Long
Set fr = ThisWorkbook.Sheets(inSheet).Range(inRange).find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
If Not fr Is Nothing Then FindRange = fr.row Else FindRange = 0
End Function

Excel Find and replace a single match value with multiple new values

I have 2 excel sheets, where I would like to find and replace values, however I would like to have multiple replace values take the spot of one match value.
Sheet 1: Sheet 2:
Match Value Match Value New Value
28045000 28045000 28051560
39162010 28045000 28056549
39269000 39162010 39596000
All Match Values in sheet 1 are unique, while match values in sheet 2 may have duplicates because they correspond to multiple new values. So, if the match value in sheet 1 and sheet 2 are the same, then I want to replace the match value in sheet 1 with all of the new values that correspond to the match value. Sheet 1 after the replacements have been made should look like this:
Sheet 1:
Match Value
28051560
28056549
39596000
39269000
So as we can see, 28045000 was replaced by 2 values, 28051560 and 28056549 in 2 separate cells, while 39162010 was replaced by 39596000, while 39269000 which did not have a match value in sheet 2, remained the same.
I would typically do this manually, but there are about 30,000 rows of data, some with over to 10 values matching a single match value. I have the following code, however, this does not properly replace the match value with all of the new values. Is there a way to get Excel to search through the entire range of both sheets and make the proper changes automatically?
Sub multiFindNReplace()
Dim myList, myRange
Set myList = Sheets("sheet 1").Range("A1:A5000")
Set myRange = Sheets("sheet2").Range("A1:A5000")
For Each cel In myList.Columns(1).Cells
myRange.Replace what:=cel.Value, replacement:=cel.Offset(0, 1).Value
Next cel
End Sub
I would do it like this:
The macro just loop through the first sheet and compare it with the second sheet. If it matches, it replace the value in the first, add c+1 and go on with searching. Because the orginal value is replaced then, the orignal value is stored in d, if it find a second match it dones't replace it because of c+1, it goes to the else clause, insert a row and put the value in the new row. Like this it loops through the whole column on sheet1.
PS: I hope you can understand it, i didn't had that much time, will edit later for more readability.
Update:
So here we go again, i added the maxrow counter and overcomment it for a easy understanding.
Update 2:
Now with While-Loop because of for-loops doesn't regconize limit changes
Sub CompareLoop()
'Iterator Worksheet 1, is the counter for the ws1 column
Dim iWS1 As Integer
'Iterator Worksheet 2, is the counter for the ws1 column
Dim iWS2 As Integer
'Switch New Row, is the switch if the next value need a new row
Dim sNR As Integer
'Maximal Row Count, need to be extend when new rows are added
Dim MaxRows As Integer
'valueHolder, is the holder for the orginal value, the orginal value might be replaced on the sheet
Dim valueHolder As Long
'Worksheet1
Dim ws1 As Worksheet
'Worlsheet2
Dim ws2 As Worksheet
Set ws1 = ActiveWorkbook.Worksheets("table1")
Set ws2 = ActiveWorkbook.Worksheets("table2")
'Set iWS1 to the first row
iWS1 = 1
'Get MaxRows
MaxRows = ws1.Cells(Rows.Count, 1).End(xlUp).Row
'Loop through the Rows on WS1 setting switch to 0 and store the value from the ws1 row in the holder
While iWS1 <= MaxRows
sNR = 0
valueHolder = ws1.Cells(iWS1, 1).Value
'Loop through the Rows on WS2, searching for a value that match with the value from ws1
For iWS2 = 1 To ws2.Cells(Rows.Count, 1).End(xlUp).Row
'When it matches, then look if there was already a match with the value, if not replace it on the ws1 and increase the sNr to 1
If valueHolder = ws2.Cells(iWS2, 1).Value Then
If (sNR < 1) Then
ws1.Cells(iWS1, 1).Value = ws2.Cells(iWS2, 2).Value
sNR = sNR + 1
'When the sNR is already > 0, increase the Iterator for the ws1 that he will point on the new line
'increase the maxrows because we got one more soon, finally insert the new row and store the value from ws2 in it
Else
iWS1 = iWS1 + 1
MaxRows = MaxRows + 1
Range(ws1.Cells(iWS1, 1), ws1.Cells(iWS1, 1)).EntireRow.Insert
ws1.Cells(iWS1, 1).Value = ws2.Cells(iWS2, 2)
End If
End If
Next iWS2
iWS1 = iWS1 + 1
Wend
End Sub
Assuming Columns start at A are contiguous and are labelled, in Sheet 1, B2 and copied down to suit:
=IF(ISERROR(MATCH(A2,'Sheet 2'!A:A,0)),A2,"")
Copy range containing all values from Sheet 1 Column B and Paste Special, Values below last entry in Sheet 2 Column B.
Copy Sheet 2 Column B into A1 of Sheet 1 and filter to remove blanks in Column A. Delete Sheet 1 Column B.

Resources