How to compare rows in excel (vba) and insert an other column? - excel

I need something like this.
This picture contains all what I need, I need to insert Automatically the Value column.
I'll start by sorting rows, to help me to affect the same value to the same rows.
And I need to compare row by row (row to the next) if are the same i'll affect them the same value(column value).
Sub SortMultipleColumns()
// I'll start by sorting rows, to help me to affect the same value to the same rows
With ActiveSheet.Sort
.SortFields.Clear
.SortFields.Add Key:=Range("A1"), Order:=xlAscending
.SortFields.Add Key:=Range("B1"), Order:=xlAscending
.SortFields.Add Key:=Range("C1"), Order:=xlAscending
.SortFields.Add Key:=Range("D1"), Order:=xlAscending
.SetRange Range("A1", Range("D1").End(xlDown))
.Header = xlYes
.Apply
End With
Dim bothrows As Range, i As Integer
Set bothrows = Selection
With bothrows
// here i need to compre rows and insert in the last column value start by 1++
For i = 1 To .Rows.Count
If Not StrComp(.Cells(1, i), .Cells(2, i), vbBinaryCompare) = 0 Then
// here I need to do something
End If
Next i
End With
End Sub

Try this code:
Sub SortMultipleColumns()
'Declarations.
Dim RngList As Range
Dim RngRow As Range
Dim RngCell As Range
Dim DblCounter As Double
Dim StrString01 As String
Dim StrString02 As String
'Setting RngList.
Set RngList = Range("A1")
Set RngList = Range(RngList, RngList.End(xlDown).End(xlToRight))
Set RngList = RngList.Resize(RngList.Rows.Count - 1).Offset(1, 0)
'Sorting RngList.
With ActiveSheet.Sort
.SortFields.Clear
For Each RngCell In RngList.Rows(1).Cells
.SortFields.Add Key:=RngCell, Order:=xlAscending
Next
.SetRange RngList
.Header = xlYes
.Apply
End With
'Covering each row of RngList.
For Each RngRow In RngList.Rows
'Setting variables.
StrString01 = ""
StrString02 = ""
'Covering each cell in the given row.
For Each RngCell In RngRow.Cells
'Setting variables to the contents of the given row and to its previous one.
StrString01 = StrString01 & RngCell.Value
StrString02 = StrString02 & RngCell.Offset(-1, 0).Value
Next
'Checking if the two rows differs.
If Not StrComp(StrString01, StrString02, vbBinaryCompare) = 0 Then
DblCounter = DblCounter + 1
End If
'Reporting DblCounter.
RngRow.Offset(0, RngRow.Columns.Count).Resize(1, 1) = DblCounter
Next
End Sub
Since it adapts to the width of the given list, it's meant to be used once on the list. You can just edit the setting of RngList to a list with a fixed number of column if you want to use it multiple times. Like this:
'Setting RngList.
Set RngList = Range("A1")
Set RngList = Range(RngList, RngList.End(xlDown).Offset(0,3)
Set RngList = RngList.Resize(RngList.Rows.Count - 1).Offset(1, 0)
You can also use a formula to obtain the same result. Something like this in cell E2 should do the trick:
=IF(A2&B2&C2&D2=A1&B1&C1&D1,MAX(E$1:E1),MAX(E$1:E1)+1)

Related

Fill blank cell value with non blank cell based another cell value

I have an issue with filling blank cells of a column.
I have 4 Column headings in A, B, C, D.
I am trying to create macro to fill blank cells for dynamic data as per attached Data able wherein cell value in Column D is randomly filled and blanked.. Blank cell value needs to filled based on value mentioned in Column A..
I have created the macro but it's working to fill the blank with above value only and not getting the exact result..
Can someone please help...
Below result is expected from coding...
Below is the macro which I have created
Sub FillblankCells()
lr = Cells.Find("*", Cells(1, 1), xlFormulas, xlPart, xlByRows, xlPrevious, False).Row
With Range("D2:D" & lr)
.SpecialCells(xlBlanks).FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
End Sub
A dictionary is probably overkill, but this should work.
Sub x()
Dim lr As Long, r As Range
Dim oDic As Object
lr = Cells.Find("*", Cells(1, 1), xlFormulas, xlPart, xlByRows, xlPrevious, False).Row
Set oDic = CreateObject("Scripting.Dictionary")
'store column A for each entry in D
For Each r In Range("D2:D" & lr).SpecialCells(xlCellTypeConstants)
oDic(r.Offset(, -3).Value) = r.Value
Next r
'retrieve each column A for blanks in D
For Each r In Range("D2:D" & lr).SpecialCells(xlCellTypeBlanks)
r.Value = oDic(r.Offset(, -3).Value)
Next r
End Sub
This appears to work, it's based on the values in column C.
Sub FillblankCells()
lr = Cells.Find("*", Cells(1, 1), xlFormulas, xlPart, xlByRows, xlPrevious, False).Row
With Range("D2:D" & lr)
.SpecialCells(xlBlanks).FormulaR1C1 = "=IF(R[-1]C[-1]<RC[-1], R[-1]C,R[1]C)"
.Value = .Value
End With
End Sub
You can sort the list before using your formula. Something like this might work:
Sub FillblankCells()
'Declarations.
Dim RngList As Range
Dim DblColumnQuote As Double
Dim DblColumnBuyerName As Double
'Setting.
Set RngList = Range("A1:D1")
DblColumnQuote = 1
DblColumnBuyerName = 4
'Resetting RngList.
Set RngList = Range(RngList, RngList.End(xlDown))
'Sorting RngList.
With RngList.Parent.Sort
.SortFields.Clear
.SortFields.Add Key:=RngList.Columns(DblColumnQuote), _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
.SortFields.Add Key:=RngList.Columns(DblColumnBuyerName), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange RngList
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
.SortFields.Clear
End With
'Filling the blank cells of the Buyer Name column in RngList.
With RngList.Columns(DblColumnBuyerName)
.SpecialCells(xlBlanks).FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
End Sub

Is it possible to have a dynamic sort range?

I was wondering if I can create a macro that sorts my worksheet with a range that is constantly changing? Instead of the last row constantly changing, the top row count will be changing.
Is it possible to have my .setrange be a variable which gets updated with the new range based on arguments?
For example the current code I have sets my range starting at A5 but if rows 6 - 10 have an interior color of green I want it to leave those at the top and only sort starting from A11.
activesheet.Sort.SortFields.Clear
activesheet.Sort.SortFields.Add Key:=Range("M6"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With activesheet.Sort
.SetRange Range("A5:O150") ' I want this range to change constantly without me manually changing, for example it would be from ("A10:0150") now
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
This UDF will return a range object representing your sort range as defined by the first row of data in the fifth column containing a "white" interior (not including the first row, unless that is the ONLY row with a white interior):
Option Explicit
Private Function SortRange(baseRange As Range) As Range
Dim firstCell As Range, lastCell As Range, C As Range
With baseRange
Set lastCell = .Worksheet.Cells(.Row + .Rows.Count - 1, .Column + .Columns.Count - 1)
End With
With Application.FindFormat
.Clear
.Interior.Color = vbWhite
End With
'find the first cell in fifth column that is white
'As implemented, this should be column E, but if you move the range, it will adjust to the 5th column
'after the first row in baseRange
With baseRange
Set firstCell = .Columns(5).Find(what:="*", after:=.Cells(1, 5), searchorder:=xlByRows, searchdirection:=xlNext, searchformat:=True)
If Not firstCell Is Nothing Then
With .Worksheet
Set SortRange = .Range(.Cells(firstCell.Row, 1), lastCell)
End With
Else
MsgBox "no cells to sort"
Exit Function
End If
End With
End Function
You could then use it like:
.SetRange SortRange(activesheet.range("A3:O150"))
but I would suggest replacing activesheet with the actual worksheet.

Sort on font color using vba

I am trying to implement a button that, when pressed sorts an array first alphabetically, then based on font color. The column that I am using to sort has 3 possible values (enrolled, waitlisted, and cancelled). The font color for 'cancelled' is grey. I want to get enrolled at the top of the list, then waitlisted, then cancelled at the bottom. Shouldn't be that difficult, but I can't get the code to work. Here's the code I wrote. Many thanks!
Private Sub btnSort_Click()
Dim SortArray As Range
Dim SortColumn As Range
Set SortArray = Range("A3").CurrentRegion
Set SortColumn = Range(Range("A3").End(xlToRight), Range("A3").End(xlToRight).End(xlDown))
SortArray.Sort Key1:=SortColumn, Header:=xlYes
With SortArray.Sort
.SortFields.Clear
.SortFields.Add Key:=SortColumn
.xlSortOnFontColor
.SortOnValue.Color = RGB(192, 192, 192)
.SortOrder = xlAscending
.Header = xlYes
.Apply
End With
Since there are only 3 values, we use a helper column and then assign values to it. We then sort and then finally delete the helper column.
Let's say, your data looks like this
Try this code. I have commneted the code so you should not have a problem in understanding it.
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, lCol As Long
Dim rng As Range
Dim ColName As String
'~~> Change this to the relevant sheet
Set ws = Sheet1
With ws
'~~> Insert a helper column in Col A
.Columns(1).Insert Shift:=xlToRight
.Cells(1, 1).Value = "TmpHeader"
'~~> Get Last Row and last Column
'~~> I am assuming that headers are in row 1
lRow = .Range("B" & .Rows.Count).End(xlUp).Row
lCol = .Cells(1, Columns.Count).End(xlToLeft).Column
ColName = Split(Cells(, lCol).Address, "$")(1)
'~~> Insert the formula in Col A
.Range("A2:A" & lRow).Formula = "=IF(RC[1]=""enrolled"",1,IF(RC[1]=""waitlisted"",2,3))"
'~~> Set your range
Set rng = .Range("A1:" & ColName & lRow)
'~~> Sort it
rng.Sort Key1:=.Range("A2"), Order1:=xlAscending, Header:=xlYes, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
'~~> Delete the helper column
.Columns(1).Delete
End With
End Sub
When you run the above code, it inserts a helper column and then inserts a formula =IF(B2="enrolled",1,IF(B2="waitlisted",2,3)) What this basically does is assigns a value of 1,2 and 3 based on the value whether it is enrolled, waitlisted or cancelled.
Once the formula is inserted, we sort on Col A in ascending order and then finally delete the helper column.
Figured it out:
ActiveSheet.Range("A3").CurrentRegion.Sort Key1:=Range("I3"), Header:=xlYes
ActiveSheet.Sort.SortFields.Clear
ActiveSheet.Sort.SortFields.Add(Range("I3"), _
xlSortOnFontColor, xlDescending, , _
xlSortNormal).SortOnValue.Color = RGB(192, 192, 192)
With ActiveSheet.Sort
.SetRange Range("A3").CurrentRegion
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With

My sort procedure runs on the first sheet but not on the other

A sorting procedure must be applied to several worksheets with different contents. So I opted for the solution "for each sh in worksheets...".
This sorting procedure runs perfectly for the first selected sheet.
On the second sheet, a message "Run time error 1004 The sort reference is not valid" is displayed on the Apply instruction despite the fact that the different variables display correct values.
Referring to "'1004': "The sort reference is not valid."", I changed "With sh.Sort" to "With sh.Range(startcell, lastcell).Sort" which generates the error "Unable to get the sort property of the range class".
Could a member of the forum help me solve this problem?
In advance, thank you
This sort procedure runs on the first sheet but not on the others.
Sub sortData()
Dim startcell As Range, lastcell As Range
Dim sh As Worksheet
Dim x_Birth As Long, lastcell_Birth As Long
For Each sh In Worksheets
With sh
If Left(sh.Name, 2) = "B_" Then
.Columns(5).Insert
.Cells(1, 5) = "Y_Birth"
lastcell_Birth = sh.Cells(Rows.count, "A").End(xlUp).Row
For x_Birth = 2 To lastcell_Birth
.Cells(x_Birth, 5) = Right(.Cells(x_Birth, 4), 4)
Next
Set startcell = Range(.Cells(1, 1), .Cells(1, 1))
Set lastcell = Range(.Cells(lastcell_Birth, 7), .Cells(lastcell_Birth, 7))
With sh.Sort
.SortFields.Add Key:=sh.Range("F1"), Order:=xlAscending
.SortFields.Add Key:=sh.Range("E1"), Order:=xlAscending
.SetRange Range(startcell, lastcell)
.Header = xlYes
.Apply
End With
sh.Columns("E:E").Select
Selection.Columns.EntireColumn.Delete
End If
End With
Next
End Sub
You could try:
Option Explicit
Sub sortData()
Dim startcell As Range, lastcell As Range
Dim sh As Worksheet
Dim x_Birth As Long, lastcell_Birth As Long
For Each sh In Worksheets
With sh
If Left(.Name, 2) = "B_" Then
.Columns(5).Insert
.Cells(1, 5) = "Y_Birth"
lastcell_Birth = .Cells(Rows.Count, "A").End(xlUp).Row
For x_Birth = 2 To lastcell_Birth
.Cells(x_Birth, 5).Value = Right(.Cells(x_Birth, 4).Value, 4)
Next
Set startcell = .Range(.Cells(1, 1), .Cells(1, 1))
Set lastcell = .Range(.Cells(lastcell_Birth, 7), .Cells(lastcell_Birth, 7))
.Sort.SortFields.Clear
With .Sort
.SortFields.Add Key:=sh.Range("E1"), Order:=xlAscending
.SortFields.Add Key:=sh.Range("F1"), Order:=xlAscending
.SetRange sh.Range(startcell, lastcell)
.Header = xlYes
.Apply
End With
.Columns("E:E").Columns.EntireColumn.Delete
End If
End With
Next
End Sub

Excel: Duplicate Merging

So I've got an incredibly large contact list, originally an export from Outlook so it follows the same formatting. I've an incredibly large number of duplicates down to records holding the same name, but separate addresses / mobile numbers.
I'm looking for a macro that can help me merge these duplicates so I don't lose things like different addresses under the same name.
http://i.stack.imgur.com/EaI6e.png
In this case I'd love a macro to see that A3 is a duplicate of A2, so take J3 to O3 and paste these in Q2 to V2. Then repeat this process for any duplicate pair found.
This is a bit rudimentary, but it works. Note, since you didn't say what should happen where there are more than 2 duplicates, this macro won't really work if that's the case.
Sub moveDuplicates()
Dim i As Integer, lastRow As Integer
Dim primaryRange As Range, copyToRange As Range, cel As Range, cel2 As Range, rng As Range
Dim ws As Worksheet
Set ws = ActiveSheet
'First, sort by "Key" to get duplicates all in a row
With ws.Sort
.SortFields.Clear
.SortFields.Add Key:=Range( _
"A2:A" & ws.UsedRange.Rows.Count), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
.SetRange Range("A1:P" & ws.UsedRange.Rows.Count)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lastRow = ws.UsedRange.Rows.Count
Set rng = ws.Range(ws.Cells(2, 1), ws.Cells(lastRow, 1))
Dim lastDupRow As Integer, startDupRow As Integer, copyRow As Integer
For Each cel In rng
'First, see if the cell has a duplicate anywhere, if not, then goto next cel
cel.Select
If WorksheetFunction.CountIf(rng, cel.Value) > 1 Then
Debug.Print "Duplicate exists"
'So, since we know a duplicate exists, we need to copy the duplicate rows' info.
startDupRow = cel.Row + 1
lastDupRow = ws.Columns(1).Find(cel.Value, searchDirection:=xlPrevious).Row
If lastDupRow - startDupRow = 0 Then
copyRow = lastDupRow
Else
End If
For Each cel2 In ws.Range(Cells(startDupRow, 1), ws.Cells(lastDupRow, 1))
' pasteRange(cel.Row).Select
' copyRange(cel2.Row).Select
pasteRange(cel.Row).Value = copyRange(cel2.Row).Value
copyRange(cel2.Row).EntireRow.Delete
Next cel2
End If
Next cel
End Sub
Private Function copyRange(ByVal iRow As Integer) As Range
Set copyRange = Range(Cells(iRow, 10), Cells(iRow, 15))
End Function
Private Function pasteRange(ByVal xRow As Integer) As Range
Set pasteRange = Range(Cells(xRow, 17), Cells(xRow, 22))
End Function

Resources