I've written some code that deals with triggers. the main idea is to create dynamic triggers to cells of a sheet and based on the value of the cell the counter add or subtract from the total selected. Let me present it with an example and the code to make it clearer:
This is how my excel sheets appear.
A B C D E
1
2
3
to limit the scope, i have 4 columns and 3 rows, i want to assign triggers to A1, B1, C1 D1, E1 and more. therefore i wrote this that dynamically assign triggers to the cells.
For j = 1 To 3 ' row
For I = 1 To 4 ' columns
If Target.Column = I And Target.Row = j Then
If Target.Value = "Yes" Then
Yes_4 = Yes_4 + 1
ElseIf Target.Value = "No" Then
No_4 = No_4 + 1
ElseIf Target.Value = "Not applicable" Then
Not_Applicable_4 = Not_Applicable_4 + 1
End If
If Target.Value = "Green - Sufficient" Then
Green_4 = Green_4 + 1
ElseIf Target.Value = "Orange - Largely sufficient with points for follow-up" Then
Orange_4 = Orange_4 + 1
ElseIf Target.Value = "Red - Insufficient" Then
Red_4 = Red_4 + 1
End If
Range("O" & j).Value = "The ratings you selected are as follows:" + vbNewLine + "Yes: " & Str(Yes_4) + vbNewLine + "No: " & Str(No_4) + vbNewLine + "Green: " & Str(Green_4) _
+ vbNewLine + "Orange: " & Str(Orange_4) + vbNewLine + "Red: " & Str(Red_4) + vbNewLine + "No Applicable: " & Str(Not_Applicable_4)
End If
Next I
Next j
If the filled value of the cell is "Yes", the yes variable becomes yes + 1. not only for one cell, for every cell in the row, the summary is given in column O. for example: yes= 1, no =3, gree= 2 and so on. it functions well. The problem is that when a "Yes" is selected in a cell, the value in column O shows that yes = 1, but if you change your mind and change it from "Yes" to "No", the value in column O shows that yes = 1, no =1. Instead of "No" = 1 and "Yes" = 0.
Assign formulas to the result cells that reference the appropriate range, with the appropriate value, such as...
=countif(your range spec, "Yes")
=countif(your range spec, "Green")
If you need to adjust the range spec based on data conditions, you can update that formula in VBA reacting to the size of the range. Otherwise you don't need vba to compute the totals.
If you add a global variable to record the current value of the cell on SelectionChange, you can then check for changes and decrease the counter accordingly.
Dim sPrevious as string
Public Sub Worksheet_SelectionChange(ByVal Target As Range)
sPrevious = Target.Value
End Sub
Then add into the existing Change event:
Private Sub Worksheet_Change(ByVal Target As Range)
For j = 1 To 3 ' row
For I = 1 To 4 ' columns
If Target.Column = I And Target.Row = j Then
If sPrevious <> Target.Value Then ''check if value has changed
If sPrevious = "Yes" Then
yes_4 = yes_4 - 1
ElseIf sPrevious = "No" Then
no_4 = no_4 - 1
ElseIf sPrevious = "Not applicable" Then
Not_Applicable_4 = Not_Applicable_4 - 1
End If
If sPrevious = "Green - Sufficient" Then
Green_4 = Green_4 - 1
ElseIf sPrevious = "Orange - Largely sufficient with points for follow-up" Then
Orange_4 = Orange_4 - 1
ElseIf sPrevious = "Red - Insufficient" Then
Red_4 = Red_4 - 1
End If
End If
Here's a different approach using a dictionary to track the counts, and also to store the display values for the message:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngCheck As Range, rng As Range, rw As Range, c As Range, k, tmp
Dim dict As Object, msg As String, v
Set dict = GetTrackerDict()
Set rngCheck = Me.Range("A2:D10")
For Each rw In rngCheck.Rows
'change in this row?
If Not Application.Intersect(rw, Target) Is Nothing Then
For Each c In rw.Cells
v = c.Value
If dict.exists(v) Then 'counting this value?
tmp = dict(v) 'get array from dict
tmp(1) = tmp(1) + 1 'increment count
dict(v) = tmp 'return array
End If
Next c
'Build the message
msg = "The ratings you selected are as follows:"
For Each k In dict
tmp = dict(k)
msg = msg & vbNewLine & tmp(0) & ": " & tmp(1)
Next k
rw.EntireRow.Columns("O").Value = msg
Set dict = GetTrackerDict() 'reset tracking
End If
Next rw
End Sub
'Return a dictionary and with keys as the values to be counted
Function GetTrackerDict() As Object
Dim dict As Object
' (add in the order to be displayed)
Set dict = CreateObject("scripting.dictionary")
dict.Add "Yes", Array("Yes", 0) 'array = (displayName, count)
dict.Add "No", Array("No", 0)
dict.Add "Green - Sufficient", Array("Green", 0)
dict.Add "Orange - Largely sufficient with points for follow-up", Array("Orange", 0)
dict.Add "Red - Insufficient", Array("Red", 0)
dict.Add "Not applicable", Array("N/A", 0)
Set GetTrackerDict = dict
End Function
Related
ColumnsExample
I'm trying to compare four columns for information. First Matching Location 1 data to Location 2 data, then comparing the Rented out Columns.
If Location 2 Rented Out Column D (for a specific car that matches column A with Column C) is greater than Rented out Column B then highlight cell (column D) yellow. Also if Rented out Column D
An example pic (ColumnsExample above) would be Honda and Dodge Rented out Column D would be highlighted for failing this.
I'm assuming I'll have to assign Daily, Weekly and Monthly a number value to compare against. Just not sure where to start!
Dim Alert As Range
Dim Daily, Weekly, Monthly As Integer
Set Daily = 1
Set Weekly = 2
Set Monthly = 3
Set ws = ActiveSheet
Set w = ws.Rows(1).Find("Rented Out 2", lookat:=xlWhole)
If Not w Is Nothing Then
For Each Alert In ws.Range(w, ws.Cells(Rows.Count,
w.Column).End(xlUp)).Cells
If Alert <= "Daily" Then
'Not sure how I can set this condition based on matching
'Location 1 with location 2 as well as Rented1 out vs
'Rented out 2
Alert.Interior.Color = 65535
End If
Next Alert
End If
Use a Dictionary for the comparison and a Function for the converting the strings to numbers.
Option Explicit
Sub MyMacro()
Dim ws As Worksheet, iLastRow As Long, r As Long
Dim dict As Object, key As String, s As String
Dim i As Integer
Set dict = CreateObject("Scripting.Dictionary")
Set ws = ActiveSheet
' scan col A & B
iLastRow = ws.Cells(Rows.Count, "A").End(xlUp).Row
For r = 2 To iLastRow
key = Trim(ws.Cells(r, "A"))
If Len(key) > 0 Then
s = Trim(ws.Cells(r, "B"))
i = TextToNo(s) ' convert text to number
If i = 0 Then
MsgBox "ERROR col B = '" & s & "'", vbCritical, "Row = " & r
Exit Sub
End If
' add to dictionery
If dict.exists(key) Then
MsgBox "ERROR col A duplicate key = '" & key & "'", vbCritical, "Row = " & r
Exit Sub
Else
dict.Add key, i
End If
End If
Next
' scan col C & D
iLastRow = ws.Cells(Rows.Count, "C").End(xlUp).Row
For r = 2 To iLastRow
key = Trim(ws.Cells(r, "C"))
If Len(key) > 0 Then
If dict.exists(key) Then
s = Trim(ws.Cells(r, "D"))
i = TextToNo(s)
If i = 0 Then
MsgBox "ERROR col D = '" & s & "'", vbCritical, "Row = " & r
Exit Sub
End If
' compare col D with col B
If i > dict(key) Then
ws.Cells(r, "D").Interior.Color = vbYellow
Else
ws.Cells(r, "D").Interior.Color = vbWhite
End If
End If
End If
Next
MsgBox "Finished"
End Sub
Function TextToNo(s As String) As Integer
Select Case LCase(s)
Case "daily": TextToNo = 1
Case "weekly": TextToNo = 2
Case "monthly": TextToNo = 3
Case Else: TextToNo = 0
End Select
End Function
Each of the cells in the “O2: O20” range is populated with numerical values. Next to each one of these cells there is a cell that is also populated with numerical values depending on the value that exists in "O2: 020". For example: If "O2" = 10.2 then the cell on its side "P2" = 1000 but then "P2" = 500, then "P2" = 600, then "P2" = 50; in short, "P2" can take any positive Natural value. I would like to calculate the difference between the previous value that "P2" takes and the current value that it can take as long as "O2" remains with the same value. If the value of "O2" changes, then the difference is not important to me: For example: if "O2" = 10.2 and "P2" = 50 and then "O2" = 10 and "P2" = 3000, in this case, no I want to know the difference, because "O2" is not the same for both cells.
I hope I could understand your problem. Please see this solution.
It is using Option Base 1.
Updated program for writing the difference into the Q column.
If the message is not needed please delete or Rem the line of the last MsgBox.
Option Base 1
Private Sub Worksheet_Change(ByVal Target As Range)
'Static declaration to safe the previous values and change status
Static vO As Variant
Static vP As Variant
Static bolOChanged() As Boolean
'Set up the ranges
Dim rngO As Range, rngP As Range, rngQ As Range
Set rngO = ThisWorkbook.ActiveSheet.Range("O2:O20")
Set rngP = ThisWorkbook.ActiveSheet.Range("P2:P20")
Set rngQ = ThisWorkbook.ActiveSheet.Range("Q2:Q20") 'Range for results
'If the change is not in the range then ignore
If Intersect(Union(rngO, rngP), Target) Is Nothing Then Exit Sub
'Prevent unhandelt multiply changes. If multiply changes required than the
'Target range shall be loop through
If Target.Cells.Count > 1 Then
Application.EnableEvents = False
rngO.Value = vO
rngP.Value = vP
Application.EnableEvents = True
MsgBox "You cannot change more the one cell in the range of: " & Union(rngO, rngP).Address
Exit Sub
End If
'At the firs occasion the current status has to be saved
If VarType(vO) < vbArray Then
vO = rngO.Value
vP = rngP.Value
ReDim bolOChanged(1 To UBound(vO))
End If
Dim iIndex As Long 'Adjust the index of the array to the Row of Target cell
iIndex = Target.Row - rngO(1).Row + 1
If Not Intersect(rngO, Target) Is Nothing Then
'Change was in O range, so next P change shall be ignored
bolOChanged(iIndex) = True
Else
'rngP changed
If bolOChanged(iIndex) Then
'There was a previous O range change, ignore
bolOChanged(iIndex) = False 'Delete the recent change flag
vP(iIndex, 1) = Target.Value 'Store the value
Else
rngQ(iIndex).Value = Target.Value - vP(iIndex, 1)
MsgBox "Value change from: " & vP(iIndex, 1) & ", to: " & Target.Value & ". Difference is: " & Target.Value - vP(iIndex, 1)
vP(iIndex, 1) = Target.Value 'Store the value
End If
End If
End Sub
UPDATE: This version is working with multiply entries.
Option Base 1
Private Sub Worksheet_Change(ByVal Target As Range)
'Static declaration to safe the previous values and change status
Static vO As Variant
Static vP As Variant
Static bolOChanged() As Boolean
'Set up the ranges
Dim rngO As Range, rngP As Range, rngQ As Range
Set rngO = ThisWorkbook.ActiveSheet.Range("O2:O20")
Set rngP = ThisWorkbook.ActiveSheet.Range("P2:P20")
Set rngQ = ThisWorkbook.ActiveSheet.Range("Q2:Q20") 'Range for results
'If the change is not in the range then ignore
If Intersect(Union(rngO, rngP), Target) Is Nothing Then Exit Sub
'At the firs occasion the current status has to be saved
If VarType(vO) < vbArray Then
vO = rngO.Value
vP = rngP.Value
ReDim bolOChanged(1 To UBound(vO))
End If
Dim iIndex As Long 'Adjust the index of the array to the Row of Target cell
Dim item As Variant
For Each item In Target
iIndex = item.Row - rngO(1).Row + 1
If Not Intersect(rngO, item) Is Nothing Then
'Change was in O range, so next P change shall be ignored
bolOChanged(iIndex) = True
Else
'rngP changed
If bolOChanged(iIndex) Then
'There was a previous O range change, ignore
bolOChanged(iIndex) = False 'Delete the recent change flag
vP(iIndex, 1) = item.Value 'Store the value
Else
rngQ(iIndex).Value = item.Value - vP(iIndex, 1)
MsgBox "Value changed in cell " & item.Address & " from: " & vP(iIndex, 1) & ", to: " & item.Value & ". Difference is: " & item.Value - vP(iIndex, 1)
vP(iIndex, 1) = item.Value 'Store the value
End If
End If
Next item
End Sub
This solution uses more columns of your worksheet to store previous values to be compared to actual values. In my example, the values in cells O2 and O3 will always be the same.
Sub Populate_OandP()
'Store previous values
Call PreviousValues
'This code just simulates the data population in columns O and P
Dim intRndNumber As Integer
Range("O2").Value = 10.2
Range("O3").Value = 10
intRndNumber = Int((10 - 1 + 1) * Rnd + 1)
For i = 4 To 20
intRndNumber = Int((10 - 1 + 1) * Rnd + 1)
Cells(i, 15).Value = intRndNumber * 10
Next i
For i = 2 To 20
intRndNumber = Int((10 - 1 + 1) * Rnd + 1)
Cells(i, 16).Value = intRndNumber * 10
Next i
'Check differences
Call CheckDifferenceIfOChanges
End Sub
Sub PreviousValues()
For i = 2 To 20
Cells(i, 18).Value = Cells(i, 15).Value
Cells(i, 19).Value = Cells(i, 16).Value
Next i
End Sub
Sub CheckDifferenceIfOChanges()
For i = 2 To 20
If Cells(i, 18).Value = Cells(i, 15).Value Then
Cells(i, 20).Value = Cells(i, 19).Value - Cells(i, 16).Value
Else: Cells(i, 20).Value = "O columns value changed"
End If
Next i
End Sub
I have 3 sheets that need to check if they have same value. All value on column B6 until last row should be same in Sheets MM, PP and CO. If there's difference value, the different value should be on highlight (the color is red).
But, my syntax didn't run. The syntax just can read if there's an empty column in range. This is my syntax.. Not including highlight. First, i tried to place the difference value to the other sheets. But, failed. Thank you.
Sub MatchValue()
Dim x As Integer
Dim y As Integer
Dim z As Integer
LastRowB = Cells(Rows.Count, "B").End(xlUp).Row
x = ActiveWorkbook.Worksheets("MM").Range("B6:B" & LastRowB).Cells.SpecialCells(xlCellTypeConstants).Count
y = ActiveWorkbook.Worksheets("PP").Range("B6:B" & LastRowB).Cells.SpecialCells(xlCellTypeConstants).Count
z = ActiveWorkbook.Worksheets("CO").Range("B6:B" & LastRowB).Cells.SpecialCells(xlCellTypeConstants).Count
If x <> y Then
MsgBox "MM <> PP", vbCritical, "Error Report"
End If
If y <> z Then
MsgBox "PP <> CO", vbCritical, "Error Report"
End If
If z <> x Then
MsgBox "CO <> MM", vbCritical, "Error Report"
End If
SheetMM = "MM"
DataColumnMM = "B6"
SheetPP = "PP"
DataColumnPP = "B6"
SheetCO = "CO"
DataColumnCO = "B6"
SheetUnmatched = "Data Unmatched"
DataColumnUnmatched = "A1"
DataRowMM = Range(DataColumnMM).Row
DataColMM = Range(DataColumnMM).Column
DataRowPP = Range(DataColumnPP).Row
DataColPP = Range(DataColumnPP).Column
DataRowCo = Range(DataColumnCO).Row
DataColCo = Range(DataColumnCO).Column
DataRowUnmatched = Range(DataColumnUnmatched).Row
DataColUnmatched = Range(DataColumnUnmatched).Column
LastDataMM = Sheets(SheetMM).Cells(Rows.Count, DataColMM).End(xlUp).Row
LastDataPP = Sheets(SheetPP).Cells(Rows.Count, DataColPP).End(xlUp).Row
LastDataCO = Sheets(SheetCO).Cells(Rows.Count, DataColCo).End(xlUp).Row
LastDataUnmathced = Sheets(SheetUnmatched).Cells(Rows.Count, DataColUnmatched).End(xlUp).Row
For counter = DataRowMM To LastDataRowMM
If WorksheetFunction.CountIf(LastDataPP, counter) = 0 Then
LastDataUnmathced.Offset(1) = counter
End If
Next
For counter = DataRowMM To LastDataRowMM
If WorksheetFunction.CountIf(LastDataCO, counter) = 0 Then
LastDataUnmathced.Offset(1) = counter
End If
Next
For counter = DataRowPP To LastDataRowPP
If WorksheetFunction.CountIf(LastDataCO, counter) = 0 Then
LastDataUnmathced.Offset(1) = counter
End If
Next
End Sub
Based on the information you've provided, you want to:
Check three tables across three sheets in the ActiveWorkbook
Check to see if the same number of constants exists in the table ranges
Highlight cells red where the values between the three sheets aren't the same
I've simplified the code in order to achieve these targets
Sub MatchValue()
Dim Range1 As Range, Range2 As Range, Range3 As Range
With ActiveWorkbook
With .Sheets("MM") 'First Sheet Name
Set Range1 = .Range("B6") 'Address of first row on First Sheet
Set Range1 = .Range(Range1, .Cells(.Rows.Count, Range1.Column).End(xlUp))
End With
With .Sheets("PP") 'Second Sheet Name
Set Range2 = .Range("B6") 'Address of first row on second Sheet
Set Range2 = .Range(Range2, .Cells(.Rows.Count, Range2.Column).End(xlUp))
End With
With .Sheets("CO") 'Third Sheet Name
Set Range3 = .Range("B6") 'Address of first row on third Sheet
Set Range3 = .Range(Range3, .Cells(.Rows.Count, Range3.Column).End(xlUp))
End With
End With
'Delete this part if you don't want to remove the existing fill (might be handy)
Range1.Interior.Pattern = xlNone
Range2.Interior.Pattern = xlNone
Range3.Interior.Pattern = xlNone
'Checks to see if the same number of constants exist within the test ranges
If Range1.SpecialCells(xlCellTypeConstants).Count <> _
Range2.SpecialCells(xlCellTypeConstants).Count Then
MsgBox "Range 1 and Range 2 constant count doesn't match", vbCritical, "Error Report"
ElseIf Range2.SpecialCells(xlCellTypeConstants).Count <> _
Range3.SpecialCells(xlCellTypeConstants).Count Then
MsgBox "Range 1 and Range 2 constant count doesn't match", vbCritical, "Error Report"
End If
Dim Temp1 As Variant, Temp2 As Variant, Temp3 As Variant, x As Long
'Checks to see if all the values entered are the same, if not, fills them red
Temp1 = Range1.Value
Temp2 = Range2.Value
Temp3 = Range3.Value
For x = 1 To UBound(Temp1, 1)
If Temp1(x, 1) <> Temp2(x, 1) Or _
Temp2(x, 1) <> Temp3(x, 1) Then
Range1.Cells(x, 1).Interior.Color = RGB(255, 0, 0)
Range2.Cells(x, 1).Interior.Color = RGB(255, 0, 0)
Range3.Cells(x, 1).Interior.Color = RGB(255, 0, 0)
End If
Next x
End Sub
I have a system which inputs a code to a cell on a spreadsheet. It does this by using a Vlookup to determine which date it is. If you look at the code below the nput is what does this Vlookup.
What I want it to do is move down a cell per amount the amount that will be in a combo box value called DayAmount. What would I need to enter for it to look at the next cell?
For example if the 5th of January is in A24 I want it to also enter the same code in the 6th and 7th of January which the Vlookup knows is A25 and A26.
Private Sub Submitplan_Click()
' This searches for the selected engineer
Dim EngineerFound As Range
Dim Front As Worksheet
Dim wb As Workbook
Dim area As Worksheet
Set wb = ThisWorkbook
Dim tabchange As String
Set Front = wb.Worksheets("Front")
x = Front.Cells(Front.Rows.Count, "F").End(xlUp).Row
With Front.Range("F8:F" & x)
Set EngineerFound = .Find(Engbox.Value, LookIn:=xlValues)
End With
EngRow = EngineerFound.Row
'This is the section which enters the data into the right date
tabchange = ("Area") & Front.Range("B8")
Set area = wb.Worksheets(tabchange)
y = WorksheetFunction.VLookup(CLng(CDate(Datebox.Value)), area.Range("A:B"), 2, 0)
nPut = WorksheetFunction.VLookup(Key, area.Range("A:B"), 2, 0) &
Hoursbox.Value
z = area.Range("C:C").Find(Engbox.Value).Row
If area.Cells(z, y).Value = " B/H" Then
area.Cells(z, y).Value = nPut & " " & "B/H"
ElseIf area.Cells(z, y).Value = " WK" Then
area.Cells(z, y).Value = nPut & " " & "WK"
Else: area.Cells(z, y).Value = nPut
End If
' If DayAmount <> "" Then
'End If
Call Update
Unload Me
End Sub
If I'm reading this correctly, you have a value in a combobox (will say DayAmount) which will be assigned until a that value is met.
Dim i as Long, j as Long, k as Long
i = ActiveCell.Row
j = DayAmount
k = 1
If j > 1 Then
Do until k = j-1
Cells(k+1,1).Value = Cells(i,1)>Value
k = i + k
Loop
End If
Or you could use a filldown, or .value match, and when you enter the line to the destination cell, you use:
Dim i as Long, j as Long
i = ActiveCell.Row
j = DayAmount
Range(Cells(i,1),Cells(i+j,1)).Value = "" 'input here
Note the arbitrary activecell and column 1 usage as i'm unsure exactly where this would be for you.
Regarding, specifically, the use of nPut, you can use offset to help, such as:
Range(nPut, nPut.Offset(DayAmount,0)) = WorksheetFunction.VLookup(Key, area.Range("A:B"), 2, 0) & Hoursbox.Value
Note that I haven't tested the latter and it's off the top of my head.
The group column in my table contains a value as either 1 or 2 . I want to copy the row with value as 1 to Sheet2 and rows with values as 2 to sheet3 using a button. Also it should show error message if cells are left blank or if value is neither 1 nor 2.
Roll no meter width group
112 150 130 1
Since i am new to coding i have following this approach
check if the cell is empty and generate an error message
check if the cell contains value other than 1 or 2 and generate error message
finally copy the row with values as 1 to Sheet2 and rest all in sheet3
I need help in doing this is an effective way. As i have to keep the size of file down
enter code here
Private Sub CommandButton2_Click()
Dim i As Integer
p = Sheet1.Range("l1").Value 'no. of filled cells in the range
Application.DisplayAlerts = False
Sheet1.Activate
''checking if the range is empty
For i = 29 To p + 29
If Sheet1.Range("l" & i).Value = "" Then
MsgBox ("PLEASE ENTER THE SHRINKAGE GROUP FOR CELL NO. l" & i)
Range("L" & i).Activate
End
End If
Next i
'' checking if the range contains values other than 1 or 2
For i = 29 To p + 29
If Sheet1.Range("l" & i).Value <> 1 And Sheet1.Range("l" & i).Value <> 2 Then
MsgBox ("SHADE GROUP DOES NOT EXIST FOR CELL NO. l" & i)
Range("L" & i).Activate
End
End If
Next i
' sort based on the group
Range("a29:L300").Sort _
Key1:=Range("l29"), Header:=xlYes
'count the number of rolls in group 1
Dim x, y As Long
Dim a, b As Integer
x = Range("L" & Rows.Count).End(xlUp).Row
If x < 29 Then x = 29
a = Application.WorksheetFunction.CountIf(Range("L12:L" & x), 1) + 28
Range("M1").Value = a
' count the number of rolls in group 2
y = Range("L" & Rows.Count).End(xlUp).Row
If y < 29 Then y = 29
b = Application.WorksheetFunction.CountIf(Range("L12:L" & x), 2)
Range("n1").Value = b
'' copying groupwise to different sheet
Sheet1.Range("a29", "l" & a).Copy
Sheet2.Range("a5").PasteSpecial xlPasteAll
Sheet2.Range("a5").PasteSpecial xlPasteValuesAndNumberFormats
'' copying group 2
Sheet1.Range("a" & a + 1, "l" & a + b).Copy
Sheet5.Range("a5").PasteSpecial xlPasteAll
Sheet5.Range("a5").PasteSpecial xlPasteValuesAndNumberFormats
End Sub
Create named ranges for your source data and for the rows after which you want it to be copied. In this example I've used "source", "range1" and "range2". Then the following code copies the source data into the appropriate place:
Sub copyData()
Dim source As Range, range1 As Range, range2 As Range
Dim r As Range
Set source = Range("source")
Set range1 = Range("range1")
Set range2 = Range("range2")
For Each r In source.Rows
If r.Cells(1, 4).Value = 1 Then
copyRow r, range1
ElseIf r.Cells(1, 4).Value = 2 Then
copyRow r, range2
Else
' handle error here
End If
Next r
End Sub
Sub copyRow(data As Range, targetRange As Range)
Set targetRange = targetRange.Resize(targetRange.Rows.Count + 1, targetRange.Columns.Count)
For i = 1 To 3
targetRange.Cells(targetRange.Rows.Count, i).Value = data.Cells(1, i).Value
Next i
End Sub
There's probably a much more elegant way of doing this involving array formulae, but this should do the trick.
For validating that each cell contains only "1" or "2", you can include additional code where I've put a comment, but you'd be better off handling this as a data validation.