Copying row to another sheet and create unique code for duplicate - vba

I would like to know how to manipulate my excel data as I need.
I have a table with rows and a lot of field I would like to select by hand some rows and to copy them to another sheet that has predefined column ordering those rows to fit my predefined column and to create an unique code for rows that I consider duplicate based on 2 two column.
This might not be very clear so I will explain more with photo:
here I have my table with rows I selected by hand, I would like to copy column H,I,K,AA,AJ to another sheet but in some specific order to fit my other table column:
I would like my AJ column in the Column A, my AA column in the Column E My column K in the Column F etc...
I Would also want to create a unique Key based on column F and I (for example here in the first image rows 17 to 21 would have the same key in the blue sheet in column B)
For the moment I am able to take my selected rows and copy the wanted column to another sheet.
I don't know how to reorder them to fit my template in the second sheet. I also don't know how to create a key and insert it to my second sheet for each combination of columns F and I of my first sheet.
Sub ajout_commande()
Set DataSheet = ThisWorkbook.Worksheets("0")
Dim a As Range, b As Range
Set a = Selection
i = Selection.Rows.Count
For Each b In a.Rows
DataSheet.Cells(2, 1).EntireRow.Insert
Next
Dim r1 As Range, r2 As Range, r3 As Rang, r4 As Range, r5 As Range, res_range As Range
Let copyrange1 = "I1" & ":" & "I" & i
Let copyrange2 = "K1" & ":" & "K" & i
Let copyrange3 = "L1" & ":" & "L" & i
Let copyrange4 = "AA1" & ":" & "AA" & i
Let copyrange5 = "AJ1" & ":" & "AJ" & i
Set r1 = a.Range(copyrange1)
Set r2 = a.Range(copyrange2)
Set r3 = a.Range(copyrange3)
Set r4 = a.Range(copyrange4)
Set r5 = a.Range(copyrange5)
Set res_range = Union(r1, r2, r3, r4, r5)
res_range.Copy
DataSheet.Cells(2, 1).PasteSpecial xlPasteValues
End Sub
If this is to complicate to implement or impossible please tell me in comment so that I try to find another method. I am new to VBA and am trying to help my colleagues by simplifying their work.
Thanks.

Maybe try something like this.
It need some adjustements (especially in cells to copy)
Dim UniqueKeyArray() As String
Dim Counter As Long
Sub test()
Dim aRows As Range, aCell As Range
Dim Ws As Worksheet
Dim i As Long
Set Ws = ThisWorkbook.Sheets("SomeName")
ReDim UniqueKeyArray(0 To 1, 1 To 1)
For i = 1 To Selection.Areas.Count 'loop through selection
For Each aRows In Selection.Areas(i).Rows 'loop through rows of selection
For Each bCell In aRows.Columns(1).Cells 'loop through cells in column one
With Ws
.Cells(2, 1).EntireRow.Insert
'adjust offset to get source data you need
'adjust cells(x,y) to put data where you want it
.Cells(2, 2) = bCell.Offset(0, 2)
.Cells(2, 3) = bCell.Offset(0, 3)
.Cells(2, 4) = bCell.Offset(0, 5)
.Cells(2, 5) = bCell.Offset(0, 6)
.Cells(2, 1) = "'" & UniqueKey(bCell.Text) ' "'" added to prevent excel trim leading 000..
End With
Next bCell
Next aRows
Next i
'reset variables. This way you always start unique key from 1
Counter = 0
Erase UniqueKeyArray
End Sub
Function UniqueKey(SourceVal As String) As String
'creates unique key based on source string
Dim i As Long
For i = 1 To UBound(UniqueKeyArray, 2)
If UniqueKeyArray(1, i) = Format(SourceVal, "0000000000") Then
'if string is same you get unique key created before
UniqueKey = UniqueKeyArray(1, i)
Exit Function
End If
Next i
'if string is new then new unique key is created
Counter = Counter + 1
ReDim Preserve UniqueKeyArray(0 To 1, 1 To Counter)
UniqueKey = Format(Counter, "0000000000") 'adjust format to fit your needs
UniqueKeyArray(0, Counter) = SourceVal
UniqueKeyArray(1, Counter) = UniqueKey
End Function

Related

How To Have VBA Insert Formula Result as a Value

I got help last week getting my syntax and ranges correct, and thought I could just do a vlookup to finish it but apparently I was mistaken. It just seems like when I try to research how to accomplish this, I find various examples but I don't have the background to translate it to my code.
The macro runs and does almost everything its supposed to do. But in addition to inserting the arrays, there are 3 other cells that need values when there are blank cells in my ‘sourcerng’.
This is the logic for the cells that need values (the values are already in my worksheet, I just need to get them to these blank cells). I tried to do an IIF statement for these but I still have no idea what I'm doing. Would that be the best way? Should it just be another IF THEN statement?
rngBE - IF Column Z = 0 Then copy value from corresponding row in column O. Otherwise copy value from column Z
rngBG - IF Column AA = "Unknown" Then copy value from corresponding row in column I. Otherwise copy value from column AA.
rngBK - IF Column AB = "Unknown" Then copy value from corresponding row in column N. Otherwise copy value from column AB.
Sub AutomateAllTheThings6()
Dim arr3() As String
Dim arr11() As String
'Dim resBE As String
Dim rng3 As Range
Dim rng11 As Range
Dim rngBE As Range
Dim rngBG As Range
Dim rngBK As Range
Dim sourcerng As Range
'Dim firstRow As Long
Dim lastRow As Long
'Dim i As Long
Call OptimizeCode_Begin
'firstRow = 2
lastRow = ActiveSheet.Range("D1").End(xlDown).Row
Set rng3 = ActiveSheet.Range("BH2:BJ" & lastRow)
Set rng11 = ActiveSheet.Range("BL2:BV" & lastRow)
Set rngBE = ActiveSheet.Range("BE2:BE" & lastRow)
Set rngBG = ActiveSheet.Range("BG2:BG" & lastRow)
Set rngBK = ActiveSheet.Range("BK2:BK" & lastRow)
Set sourcerng = ActiveSheet.Range("BE2:BE" & lastRow)
arr3() = Split("UNKNOWN,UNKNOWN,UNKNOWN", ",")
arr11() = Split("UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,00/00/0000, _
00/00/0000,00/00/0000,00/00/0000,NEEDS REVIEW", ",")
For Each cell In sourcerng
If IsEmpty(cell) Then
Intersect(rng3, ActiveSheet.Rows(cell.Row)).Value = arr3
Intersect(rng11, ActiveSheet.Rows(cell.Row)).Value = arr11
'***PLS HELP***
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = "WEEEEE"
Intersect(rngBG, ActiveSheet.Rows(cell.Row)).Value = "WOOOOO"
Intersect(rngBK, ActiveSheet.Rows(cell.Row)).Value = "WAAAAA"
End If
Next
Range("BR2:BU2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.NumberFormat = "mm/dd/yyyy"
Columns("BF:BF").Select
Selection.Delete Shift:=xlToLeft
Call OptimizeCode_End
End Sub
'*********TESTING***********
'resBE = IIf(Cells(13,Z).Value = 0, Cells(13,BE).Value = Cells(13,Z), Cells(13,BE).Value = Cells(13,O))
'***************************************
'For i = firstRow To lastRow
' valZ = Range("Z" & i)
' valOh = Range("O" & i)
'
' If valZ = 0 Then
' rngBE.Value = valOh
' Else rngBE.Value = valZ
' End If
There are several ways to do your task. If you're more of an "Excel" person than VBA you might consider this approach: You can inject the syntax of any "regular" formula in R1C1 Format.
So the formula mentioned above =if($Z2=0,$O2,$Z2) is .FORMULA format for any value in row 2.
But in .FORMULAR1C1 it can be inserted in ANY cell as: =IF(RC26=0,RC15,RC26) (basically no rows up or down, but always columns O (15) and Z(26).
So, your modified code would have something like this:
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).FormulaR1C1 = "=IF(RC26=0,RC15,RC26)"
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value = _
Intersect(rngBE, ActiveSheet.Rows(cell.Row)).Value
Again, this is NOT the most efficient way to accomplish your task, but if you're dealing with thousandsof rows, versus tens to hundreds of thousands, I wouldn't worry about it and it gives you a new tool to use.

Iterating through row range group data and take an action

I am getting to know Excel VBA. I have a working program that uses an action button on one sheet opens a source workbook and data worksheet, selects data and puts that into a second workbook and destination sheet. I then sort the data as needed and it looks like this
Destination sheet, sorted and annotated duplicates
I am now trying to select the data based on col 2 "B" where the items are duplicated and/or not duplicated then perform an action (send an email to the manager about the staff under their control). I can get an email to work but its selecting the data that I'm having trouble with.
the output data would be col 1 & col 3 to 5 e.g.
Dear Manager1,
you staff member/s listed below have achieved xyz
Person1 22/06/2017 11/08/2017 22/08/2017
Person11 22/06/2017 11/08/2017 22/08/2017
Person15 22/06/2017 11/08/2017 22/08/2017
congratulations....
So what I hope somebody can help me with is a clue how I get to look at the data in col 2
add the Row data required to an array or something then to check the next Row add it to the same something until it is different to the next Row Pause do the action then do the next iteration. Resulting in:
Manager1 .....Person 1,11,15action
Manager10 ..... Person 10action
Manager2 ..... Person 12,16,2,25,28action
Manager3 ..... Person 13,17,26,29,3action
until last line is reached.
I am so confused with arrays / lookups and loops I have lost the plot somewhere along the way.
I have a variable lastTmp which tells me the last line of data in the set, this will vary each month.
The Range is:
Set rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).row)
The last piece of my working code is:
Dim lp As Integer
lp = 1
For Each cell In rng1
If 1 < Application.CountIf(rng1, cell.Value) Then
With cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next cell
You will be better placed to confront confusion if you do your indenting more logically. Related For / Next, If / Else / End If and With / End With should always be on the same indent level for easier reading. I rearranged your original code like this:-
For Each Cell In Rng1
If 1 < Application.CountIf(Rng1, Cell.Value) Then
With Cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With Cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next Cell
It now becomes apparent that the With Cell / End With need not be duplicated. I have further presumed that your variable lp actually was intended to hold the count. That made me arrive at the following compression of your code.
Dim Rng1 As Range
Dim Cell As Range
Dim lp As Integer
' the sheet isn't specified: uses the ActiveSheet
Set Rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
For Each Cell In Rng1
With Cell
lp = Application.CountIf(Rng1, .Value)
.Offset(0, 4) = IIf(lp, "", "NOT ") & "duplicate : "
.Offset(0, 5) = lp
End With
Next Cell
Consider using a Dictionary or Collection, whenever, checking for duplicates.
Here I use a Dictionary of Dictionaries to compile lists of Persons by Manager.
Sub ListManagerList1()
Dim cell As Range
Dim manager As String, person As String
Dim key As Variant
Dim dictManagers As Object
Set dictManagers = CreateObject("Scripting.Dictionary")
For Each cell In Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
manager = cell.Value
person = cell.Offset(0, -1).Value
If Not dictManagers.Exists(manager) Then
dictManagers.Add manager, CreateObject("Scripting.Dictionary")
End If
If Not dictManagers(manager).Exists(person) Then
dictManagers(manager).Add person, vbNullString
End If
Next
For Each key In dictManagers
Debug.Print key & " -> "; Join(dictManagers(key).Keys(), ",")
Next
End Sub
I recommend you wanting Excel VBA Introduction Part 39 - Dictionaries
Assuming your data is as in the image
Then following code will give you result as in the image below.
Sub Demo()
Dim srcSht As Worksheet, destSht As Worksheet
Dim lastRow As Long, i As Long
Dim arr1(), arr2()
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
Set srcSht = ThisWorkbook.Sheets("Sheet2") 'change Sheet2 to your data sheet
Set destSht = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your output sheet
arr1 = Application.Index(srcSht.Cells, [row(1:7000)], Array(2, 1)) 'See note below
arr2 = arr1
For i = 1 To UBound(arr1, 1)
If Not dict.exists(LCase$(arr1(i, 1))) Then
dict.Add LCase$(arr1(i, 1)), i
Else
arr2(i, 1) = vbNullString
arr2(dict.Item(LCase$(arr1(i, 1))), 2) = arr2(dict.Item(LCase$(arr1(i, 1))), 2) & "," & arr1(i, 2)
End If
Next
destSht.Range("A1").Resize(UBound(arr1, 1), UBound(arr1, 2)) = arr2 'display result
destSht.Columns("a").SpecialCells(xlBlanks).EntireRow.Delete
End Sub
Note : For details on assigning range to array see this.

VBA-Excel Look for column names, return their number and use column letters in function

I'm quite new at VBA. I've used it in excel for a couple macros, but this one is way above my head.
I'm looking to create a macro that will find the appropriate column, then based on the value in this columns, changes the values in three other columns. I already have a static macro:
Sub AdjustForNoIntent()
'Adjusts columns Role(U) (to C-MEM), REV Profile Follow-up Date(AJ) (to N/A) and deletes Follow-up Date(Y) when column Survey: Intent to Participate = No
Dim lastrow As Long
Dim i As Long
lastrow = Range("AE" & Rows.Count).End(xlUp).Row
For i = 2 To lastrow
If Not IsError(Range("AE" & i).Value) Then
If Range("AE" & i).Value = "No" And Range("U" & i).Value = "MEM" Then
Range("U" & i).Value = "C-MEM"
Range("Y" & i).ClearContents
Range("AJ" & i).Value = "N/A"
ElseIf Range("AE" & i).Value = "No" And Range("U" & i).Value = "VCH" Then
Range("U" & i).Value = "C-VCH"
Range("Y" & i).ClearContents
Range("AJ" & i).Value = "N/A"
End If
End If
Next i
End Sub
But this is a shared workbook, so people are adding columns randomly and every time I need to go back to the code and modify the columns refereces. What I want is, for instance, to look for column with "Role" header in row A3 and to insert it where the macro looks for column "U". That way other users can add/delete columns but I won't have to modify the macro every time.
In other macros, I manage to have this thing working:
Function fnColumnNumberToLetter(ByVal ColumnNumber As Integer)
fnColumnNumberToLetter = Replace(Replace(Cells(1,ColumnNumber).Address, "1", ""), "$", "")
End Function
Dim rngColumn As Range
Dim ColNumber As Integer
Dim ColName As String
ColName = "Email Address"
Sheets("Tracking").Select
Set rngColumn = Range("3:3").Find(ColName)
ColNumber = Sheets("Tracking").Range(rngColumn, rngColumn).Column
Sheets("Combined").Range(ActiveCell, "W2").FormulaLocal = "=IF(ISERROR(INDEX(Tracking!$A:$A,MATCH(O:O,Tracking!" & fnColumnNumberToLetter(ColNumber) & ":" & fnColumnNumberToLetter(ColNumber) & ",0))), INDEX(Tracking!$A:$A,MATCH(U:U,Tracking!" & fnColumnNumberToLetter(ColNumber) & ":" & fnColumnNumberToLetter(ColNumber) & ",0)), INDEX(Tracking!$A:$A,MATCH(O:O,Tracking!" & fnColumnNumberToLetter(ColNumber) & ":" & fnColumnNumberToLetter(ColNumber) & ",0)))"
However, I am unable to link the latter to the first and much less to get it to find multiple columns. Any help is appreciated.
EDIT:
Following suggestions, here is the new code. Doesn't return an error, but doesn't do anything either. It loops through the c loop ok, but jumps from For i =2 ... line to End Sub.
Sub Adjust()
Dim lastrow As Long
Dim i As Long
Dim headers As Dictionary
Dim c As Long
Set headers = New Scripting.Dictionary
For c = 1 To Cells(3, Columns.Count).End(xlToLeft).Column
headers.Add Cells(3, c).Value, c
Next c
lastrow = Cells(headers.Item("Survey: Interest to Participate") & Rows.Count).End(xlUp).Row
For i = 2 To lastrow
If Not IsError(Cells(i, headers.Item("Survey: Interest to Participate")).Value) Then
If Cells(i, headers.Item("Survey: Interest to Participate")).Value = "No" And Cells(i, headers.Item("Role")).Value = "MEM" Then
Cells(i, headers.Item("Role")).Value = "C-MEM"
Cells(i, headers.Ittem(" Follow-up date")).ClearContents
Cells(i, headers.Item("REV profile follow-up date")).Value = "N/A"
ElseIf Cells(i, headers.Item("Survey: Interest to Participate")).Value = "No" And Cells(i, headers.Item("Role")).Value = "VCH" Then
Cells(i, headers.Item("Role")).Value = "C-VCH"
Cells(i, headers.Ittem(" Follow-up date")).ClearContents
Cells(i, headers.Item("REV profile follow-up date")).Value = "N/A"
End If
End If
Next i
End Sub
The way I'd go about this would be to create a Dictionary with header names as keys and column numbers as values:
Dim headers As Dictionary
Set headers = New Scripting.Dictionary
Dim c As Long
'Assuming headers are in row 1 for sake of example...
For c = 1 To Cells(1, Columns.Count).End(xlToLeft).Column
headers.Add Cells(1, c).Value, c
Next
Then, instead of using hard-code column letters with the Range, use the Cells collection and index it by column number using the Dictionary to look it up based on the header. For example, if your code expects column "U" to be under that header "Role" here:
Range("U" & i).Value = "C-MEM"
You can replace it with a column lookup like this using the Dictionary like this:
Cells(i, headers.Item("Role")).Value = "C-MEM"
Note that this requires a reference to the Microsoft Scripting Runtime (Tools->References... then check the box).
But this is a shared workbook, so people are adding columns randomly and every time I need to go back to the code and modify the columns refereces.
Protect the workbook to prevent this undesired behavior?
I would personally prefer to use Named Ranges, which will adjust with insertions and re-sorting of the data columns.
From Formulas ribbon, define a new name:
Then, confirm that you can move, insert, etc., with a simple procedure like:
Const ROLE As String = "Role"
Sub foo()
Dim rng As Range
Set rng = Range(ROLE)
' This will display $B$1
MsgBox rng.Address, vbInformation, ROLE & " located:"
rng.Offset(0, -1).Insert Shift:=xlToRight
' This will display $C$1
MsgBox rng.Address, vbInformation, ROLE & " located:"
rng.Cut
Application.GoTo Range("A100")
ActiveSheet.Paste
' This will display $A$100
MsgBox rng.Address, vbInformation, ROLE & " located:"
End Sub
So, I would define a Named Range for each of your columns (presently assumed to be AE, U, Y & AJ). The Named Range can span the entire column, which will minimize changes to the rest of your code.
Given 4 named ranges like:
Role, representing column U:U
RevProfile, representing column AJ:AJ
FollowUp, representing column Y:Y
Intent, representing column AE:AE
(NOTE: If you anticipate that users may insert rows above your header rows, then I would change the Named range assignments to only the header cells, e.g., "$AE$1", "$U$1", etc. -- this should require no additional changes to the code below)
You could do like this:
'Constant strings representing named ranges in this worksheet
Public Const ROLE As String = "Role"
Public Const REVPROFILE As String = "RevProfile"
Public Const FOLLOWUP As String = "FollowUp"
Public Const INTENT As String = "Intent"
Sub AdjustForNoIntent()
'Adjusts columns Role(U) (to C-MEM), REV Profile Follow-up Date(AJ) (to N/A) and deletes Follow-up Date(Y) when column Survey: Intent to Participate = No
Dim lastrow As Long
Dim i As Long
lastrow = Range(INTENT).End(xlUp).Row
For i = 2 To lastrow
If Not IsError(Range(INTENT).Cells(i).Value) Then
If Range(INTENT).Cells(i).Value = "No" And Range(ROLE).Cells(i).Value = "MEM" Then
Range(ROLE).Cells(i).Value = "C-MEM"
Range(FOLLOWUP).ClearContents
Range(REVPROFILE).Cells(i).Value = "N/A"
ElseIf Range(INTENT).Cells(i).Value = "No" And Range(ROLE).Cells(i).Value = "VCH" Then
Range(ROLE).Cells(i).Value = "C-VCH"
Range(FOLLOWUP).Cells(i).ClearContents
Range(REVPROFILE).Value = "N/A"
End If
End If
Next
End Sub
I would go with David Zemens answer but you could also use Range().Find to get the correct columns.
Here I refactored you code to find and set references to your column headers. Everything is based relative to these references.
Here I set a reference to Row 3 of the Survey column where your column header is:
Set rSurvey = .Rows(3).Find(What:="Survey", MatchCase:=False, Lookat:=xlWhole)
Because everything is relative to rSurvey the last row is = the actual last row - rSurvey's row
lastrow = rSurvey(.Rows.Count - rSurvey.Row).End(xlUp).Row - rSurvey.Row
Since rSurvey is a range we know that rSurvey.Cells(1, 1) is our column header. What isn't apparent is that since rSurvey is a range rSurvey(1, 1) is also our column header and since column and row indices are optional rSurvey(1) is also the column header cell.
Know all of that we can iterate over the cells in each column like this
For i = 2 To lastrow
rSurvey( i )
Sub AdjustForNoIntent()
'Adjusts columns Role(U) (to C-MEM), REV Profile Follow-up Date(AJ) (to N/A) and deletes Follow-up Date(Y) when column Survey: Intent to Participate = No
Dim lastrow As Long
Dim i As Long
Dim rRev As Range 'AJ
Dim rRole As Range 'U
Dim rFollowUp As Range 'Y
Dim rSurvey As Range 'AE
With Worksheets("Tracking")
Set rRev = .Rows(3).Find(What:="REV", MatchCase:=False, Lookat:=xlWhole)
Set rRole = .Rows(3).Find(What:="Role", MatchCase:=False, Lookat:=xlWhole)
Set rFollowUp = .Rows(3).Find(What:="Follow-up", MatchCase:=False, Lookat:=xlWhole)
Set rSurvey = .Rows(3).Find(What:="Survey", MatchCase:=False, Lookat:=xlWhole)
lastrow = rSurvey(.Rows.Count - rSurvey.Row).End(xlUp).Row - rSurvey.Row
End With
For i = 2 To lastrow
If Not IsError(rSurvey(i).value) Then
If rSurvey(i).value = "No" And rRole(i).value = "MEM" Then
rRole(i).value = "C-MEM"
rFollowUp(i).ClearContents
rRev(i).value = "N/A"
ElseIf rSurvey(i).value = "No" And rRole(i).value = "VCH" Then
rRole(i).value = "C-VCH"
rFollowUp(i).ClearContents
rRev(i).value = "N/A"
End If
End If
Next i
End Sub

VBA that copies rows into new sheet based on each row's cell contents (example included)

So I'm hoping for some help to automate a process that will otherwise involve copying and editing some 10,000 rows.
This is stuff relating to location data. Essentially, there are tons of these Master Rows but they do not have individual rows for Unit Numbers. I am hoping to get something to expand these into individual Unit Number rows based on what is in Column N. Column N is intended to follow a strict format of being a comma-seperated single cell list for each row.
Below is an example from Sheet 1 of what each row will have and needs to be expanded upon. Note that Column N is green and follows a consistent formatting and this will be the determinant for how many times these rows will each be expanded upon.
Below is Sheet 2 and what I want the VBA to create from Sheet 1. You can see that each row has been expanded based on the contents of Column N from Sheet 1.
Like I said, it is expected that this will involve some several thousand rows to create.
Option Explicit
Sub Tester()
Dim sht1, sht2, rwSrc As Range, rwDest As Range, v, arr, n
Set sht1 = ThisWorkbook.Sheets("Sheet1")
Set sht2 = ThisWorkbook.Sheets("Sheet2")
sht2.Range("A2:M2").Resize(3, 13).Value = sht1.Range("A2:M2").Value
Set rwDest = sht2.Range("A2:M2") 'destination start row
Set rwSrc = sht1.Range("A2:M2") 'source row
Do While Application.CountA(rwSrc) > 0
v = rwSrc.EntireRow.Cells(1, "N").Value 'list of values
If InStr(v, ",") > 0 Then
'list of values: split and count
arr = Split(v, ",")
n = UBound(arr) + 1
Else
'one or no value
arr = Array(v)
n = 1
End If
'duplicate source row as required
rwDest.Resize(n, 13).Value = rwSrc.Value
'copy over the unit values
rwDest.Cells(1, "G").Resize(n, 1).Value = Application.Transpose(arr)
'offset to next destination row
Set rwDest = rwDest.Offset(n, 0)
'next source row
Set rwSrc = rwSrc.Offset(1, 0)
Loop
End Sub
This does the work in same sheet... Pls copy the value to "Sheet2" before executing this. Not sure about efficiency though.
Public Sub Test()
Dim lr As Long ' To store the last row of the data range
Dim counter As Long
Dim Str As String ' To store the string in column N
lr = Range("N65536").End(xlUp).Row 'Getting the last row of the data
For i = lr To 2 Step -1
Str = Range("N" & i).Value ' Getting the value from Column N
counter = 1
For Each s In Split(Str, ",")
If counter > 1 Then
Range("A" & (i + counter - 1)).EntireRow.Insert ' Inserting rows for each value in column N
Range("G" & (i + counter - 1)).Formula = s ' Updating the value in Column G
Else
Range("G" & i).Formula = s ' No need to insert a new row for first value
End If
counter = counter + 1
Next s
Next i
lr = Range("G65536").End(xlUp).Row
' Pulling down other values from the first value row other rows
Range("A1:N" & lr).Select
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.FormulaR1C1 = "=R[-1]C"
' Pasting the data as Values to avoid future formula issues.
Range("A1:N" & lr).Copy
Range("A1:N" & lr).PasteSpecial xlPasteValues
MsgBox "Done"
End Sub

Excel: How to split a column into two columns based off a third, odd/even parameter column?

I will be referencing the below picture:
I seek to split up the FirstValue Column into the two columns right of it; however, I want to split the columns based off the Parameter column. When the Parameter value is odd, I want to copy the values only to the OtherValue1 column. When the Parameter value is even, I want to copy the values only to the OtherValue2 column. After reading forums and trying excel's "Text to Columns" feature, I am unable to find a solution.
Is there a way implement this using VBA?
*Note: The worksheet is actually about 10,000 rows long, so speed would also be helpful.
EDIT:
Here is the code I have so far. I am getting Object errors in this line of code: .Cells(2, MF1Col).Formula = "=IF(MOD(paraformula,2)=1,WTRfor,"")"
Dim rw As Worksheet
Dim secondCell, MF1Cell, MF2Cell, paraCell, MF1formula, MF2formula, paraformula, WTRfor As Range
Dim secondCol As Long, MF1Col As Long, MF2Col As Long, paraCol As Long
Set rw = ActiveSheet
With rw
Set secondCell = .Rows(1).Find("FirstValue”)
' Check if the column with “FirstValue” is found
'Insert Two Columns after FirstValue
If Not secondCell Is Nothing Then
secondCol = secondCell.Column
.Columns(secondCol + 1).EntireColumn.Insert
.Columns(secondCol + 2).EntireColumn.Insert
.Cells(1, secondCol + 1).Value = "OtherValue1"
.Cells(1, secondCol + 2).Value = "OtherValue2"
.Activate
Set MF1Cell = .Rows(1).Find("OtherValue1")
MF1Col = MF1Cell.Column
Set MF2Cell = .Rows(1).Find("OtherValue2")
MF2Col = MF2Cell.Column
Set paraCell = .Rows(1).Find("Parameter")
paraCol = paraCell.Column
Set paraformula = Range(.Cells(2, paraCol).Address(RowAbsolute:=False, ColumnAbsolute:=False))
Set MF1formula = Range(.Cells(2, MF1Col).Address(RowAbsolute:=False, ColumnAbsolute:=False))
Set WTRfor = Range(.Cells(2, secondCol).Address(RowAbsolute:=False, ColumnAbsolute:=False))
.Cells(2, MF1Col).Formula = "=IF(MOD(" & paraformula & ",2)=1," & WTRfor & ","""")"
Range(.Cells(2, MF1Col).Address).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
ActiveSheet.Paste
Set MF2formula = Range(.Cells(2, MF2Col).Address(RowAbsolute:=False, ColumnAbsolute:=False))
.Cells(2, MF2Col).Formula = "=IF(MOD(" & paraformula & ",2)=0," & WTRfor & ","""")"
Range(.Cells(2, MF2Col).Address).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
ActiveSheet.Paste
End If
End With
in C2, =IF(MOD(E2,2)=1,B2,"")
in D2, =IF(MOD(E2,2)=0,B2,"")
copy these down to the end of your data
assuming the same format (Data,Col1,Col2,Parameter), but using relative addressing
Column 1: =IF(MOD(OFFSET(C2,0,2),2)=1,OFFSET(C2,0,-1),"") replace C2 with the current cell
Column 2: =IF(MOD(OFFSET(D2,0,1),2)=0,OFFSET(D2,0,-2),"") replace D2 with the current cell
again, copy and paste - once you have the first one correct, excel will adjust the formula for the current cell
For Cell D2:
=IF(MOD(E2,2),B2,"")
Explanation:
If Range E2 is not divisible by two, the display value from B2, otherwise display nothing.
you can reverse this by inserting a 'NOT' around the MOD for Cell C2:
=IF(NOT(MOD(E2,2)),B2,"")
VBA:
Sub odd_even()
a = 1 ' start row
b = 10 ' end row
c = 1 ' column with values inputs
For d = a To b ' FOR loop from start row to end row
If ActiveSheet.Cells(d, c) Mod 2 Then 'mod becomes high when value is odd
ActiveSheet.Cells(d, c + 2) = ActiveSheet.Cells(d, c) 'odd value gets copied to the odd-column ( two to the right of the values)
ActiveSheet.Cells(d, c + 3) = "" 'same row on even-column gets cleared
Else:
ActiveSheet.Cells(d, c + 3) = ActiveSheet.Cells(d, c) 'even value gets copied to the even-column ( three to the right of the values)
ActiveSheet.Cells(d, c + 2) = "" 'same row on odd-column gets cleared
End If
Next d ' go to next row
End Sub