I've been trying to re-work this code I found to combine and sum rows in a sheet.
I have a sheet with values in columns columns A-G. And a Dynamic number of rows.
If an exact duplicate is found in column D, I want to add (sum) the "column G" and "column H" values from the duplicate row, with the "G" and "H" values from the original row. With the result being in the original row.
For all other columns, I want the duplicate row to overwrite the original. (Or, overwrite if the exact same, and place next to the original value in the same cell if different, but this is beyond my knowledge.)
To clarify, the code will loop through column 'D' until it finds duplicate values. It will then delete the row of this duplicate value, after copying/pasting its values over those of the original. Except for "G" and "H", where it will sum its values with the original rows "G" and "h".
ie.
June 1
A
-----
1234
Walmart
6
7
June 2
B
BA
1234
Walmart
4
4
Would turn into
June 2
B
BA
1234
Walmart
10
11
In place of the original, for all duplicate (column "D") rows in the worksheet.
Thanks for any input.
This code I've been trying to change: works for 4 columns where column A is the ID as opposed to column D, and doesn't include the sum condition. I'm having trouble fitting it to my conditions. Specifically, why is the Cl range 'B' when this range isn't consequential, and what format the offset follows.
Sub mergeRows()
Dim Dic As Object: Set Dic = CreateObject("Scripting.Dictionary")
Dic.Comparemode = vbTextCompare
Dim Cl As Range, x$, y$, i&, Key As Variant
For Each Cl In Range("B2:B" & Cells(Rows.Count, "B").End(xlUp).Row)
x = Cl.Value & "|" & Cl.Offset(, 1).Value
y = Cl.Offset(, 2).Value
If Not Dic.exists(x) Then
Dic.Add x, Cl.Offset(, -1).Value & "|" & y & "|"
ElseIf Dic.exists(x) And Not LCase(Dic(x)) Like "*|" & LCase(y) & "|*" Then
Dic(x) = Dic(x) & "|" & y & "|"
End If
Next Cl
Range("A2:D" & Cells(Rows.Count, "B").End(xlUp).Row).ClearContents
i = 2
For Each Key In Dic
Cells(i, "A") = Split(Dic(Key), "|")(0)
Range(Cells(i, "B"), Cells(i, "C")) = Split(Key, "|")
Cells(i, "D") = Replace(Split(Replace(Dic(Key), "||", ";"), "|")(1), ":", ";")
i = i + 1
Next Key
Set Dic = Nothing
End Sub
The example you are trying to copy seems overly complex and relies on objects only available on the Windows platform. I started from scratch to meet your needs with simpler code--I hope you are able to follow it and adjust as needed.
It combines data in this sheet:
into this:
Sub merge_rows()
Dim last_row As Long
Dim row As Long
Dim s As Worksheet
Dim col As Integer
Set s = ActiveSheet 'use this line to process the active sheet
'set s = thisworkbook.Worksheets("Sheet1") ' use this line to process a specific sheet
last_row = s.Cells(s.Rows.Count, 1).End(xlUp).row 'find the last row with data
For row = last_row To 3 Step -1
If s.Cells(row, "D").Value = s.Cells(row - 1, "D").Value Then
' found a match in column d
' add column G
s.Cells(row - 1, "G").Value = s.Cells(row - 1, "G").Value + s.Cells(row, "G").Value
' add column H
s.Cells(row - 1, "H").Value = s.Cells(row - 1, "H").Value + s.Cells(row, "H").Value
'append all other columns if different
For col = 1 To 6
If Not s.Cells(row, col).Value = s.Cells(row - 1, col).Value Then
s.Cells(row - 1, col).Value = s.Cells(row - 1, col).Value & " " & s.Cells(row, col).Value
End If
Next
' now delete the duplicate row
s.Rows(row).Delete
End If
Next
End Sub
Related
If this sub finds finds matching values in column A, it will merge those two values into one row, and then sum columns B & C into run row.
I've been trying to change it so that for this to happen, it needs to find a match in columns A & B, then C & D will be summed.
For example:
A A 5 5
A A 5 5
A B 6 1
Will Become
A A 10 10
A B 6 1
Sub Consolidate()
Application.ScreenUpdating = False
Dim s As Worksheet, last_row As Long
Dim row As Long
Dim col As Integer, v, m
Set s = Worksheets("Sheet12")
s.Activate
last_row = s.Cells(s.rows.Count, 1).End(xlUp).row 'find the last row with data
For row = last_row To 3 Step -1
v = s.Cells(row, "A").Value
m = Application.Match(v, s.Columns("A"), 0) 'find first match to this row
If m < row Then 'earlier row?
'combine rows `row` and `m`
s.Cells(m, "B").Value = s.Cells(m, "B").Value + s.Cells(row, "B").Value
s.Cells(m, "C").Value = s.Cells(m, "C").Value + s.Cells(row, "C").Value
s.rows(row).Delete
End If 'matched a different row
Next row
End Sub
Slight modification using a dictionary:
Sub Consolidate()
Application.ScreenUpdating = False
Dim s As Worksheet, last_row As Long
Dim row As Long, dict As Object, k As String, m As Long
Set dict = CreateObject("scripting.dictionary") 'for tracking A+B vs first row occurence
Set s = Worksheets("Sheet12")
s.Activate
last_row = s.Cells(s.Rows.Count, 1).End(xlUp).row 'find the last row with data
'map all the A+B combinations to the first row they occur on
For row = 3 To last_row
k = s.Cells(row, "A").Value & "~~" & s.Cells(row, "B").Value
If Not dict.exists(k) Then dict.Add k, row
Next row
For row = last_row To 3 Step -1
k = s.Cells(row, "A").Value & "~~" & s.Cells(row, "B").Value
m = dict(k) 'find first match to this row from the dictionary
If m < row Then 'earlier row?
'combine rows `row` and `m`
s.Cells(m, "C").Value = s.Cells(m, "C").Value + s.Cells(row, "C").Value
s.Cells(m, "D").Value = s.Cells(m, "D").Value + s.Cells(row, "D").Value
s.Rows(row).Delete
End If 'matched a different row
Next row
End Sub
I have 3 sheets which name are Sheet1, Sheet2 and Sheet3. All sheets have the same columns. I have been using a SUMIFS formula in Sheet2.Range("C2"):
=SUMIFS(Sheet1!$C$2:$C$15,Sheet1!$B$2:$B$15,'Sheet2'!B2,
Sheet1!$A$2:$A$15,'Sheet2'!A2,Sheet1!$D$2:$D$15,"="&'Sheet2'!D2)-
SUMIFS(Sheet3!$C$2:$C$15,Sheet3!$B$2:$B$15,'Sheet2'!B2,
Sheet3!$A$2:$A$15,'Sheet2'!A2,Sheet3!$D$2:$D$15,"="&'Sheet2'!D2)
I have prepared below code which takes the value of Sheet1 Column C by matching Column A, B and D with Sheet2, and paste the result in Sheet2 Column C.
I have been looking for a way to to subtract the Sheet3 Column C quantity from Sheet1 Column C by matching Column A, B and D then its result should be pasted into Sheet2 Column C.
The file and data are attached here:
Dim dict As Object
Dim searchrange As Range
With Sheet1
Dim last_y As Long
Dim i As Long
Set dict = CreateObject("Scripting.Dictionary")
last_y = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To last_y
dict(.Cells(i, 1).Value & .Cells(i, 2).Value & .Cells(i, 4).Value) = _
dict(.Cells(i, 1).Value & .Cells(i, 2).Value & .Cells(i, 4).Value) + _
.Cells(i, 3).Value
Next i
End With
With Sheet3
last_y = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 2 To last_y
.Cells(i, 3).Value = dict(.Cells(i, 1).Value & .Cells(i, 2).Value & _
.Cells(i, 4).Value)
Next i
End With
If all you need is a direct comparison then using a Dictionary is probably not the best method. I would just iterate through all 3 sheets as long as its under 100k rows. You could get fancy with .Find and .FindNext but the easiest is just a good old For Loop.
If I'm understanding your request, you need all values from Sheet 1 to be subtracted by a corresponding value from sheet 3 (if it exists) and then outputted into the matching row of sheet 2 (if it exists). Here's how I would do that:
Sub rowMatch()
Dim a As String, b As String, c As Double, d As String
For i = 2 To Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Row
'Grab the Sheet1 values
a = Sheet1.Cells(i, 1).Text
b = Sheet1.Cells(i, 2).Text
c = Sheet1.Cells(i, 3).Value
d = Sheet1.Cells(i, 4).Text
'Check Sheet3
For j = 2 To Sheet3.Cells(Sheet3.Rows.Count, 1).End(xlUp).Row
If Sheet3.Cells(j, 1).Text = a _
And Sheet3.Cells(j, 2).Text = b _
And Sheet3.Cells(j, 4).Text = d _
Then
c = c - Sheet3.Cells(j, 3).Value
End If
Next j
'Put into Sheet2
For k = 2 To Sheet2.Cells(Sheet2.Rows.Count, 1).End(xlUp).Row
If Sheet2.Cells(k, 1).Text = a _
And Sheet2.Cells(k, 2).Text = b _
And Sheet2.Cells(k, 4).Text = d _
Then
Sheet2.Cells(k, 3) = c
End If
Next k
Next i
End Sub
Edit: To further improve this, you may want to make the code more flexible by only comparing the strings after doing operations like UCASE() and TRIM(). And you may want to add in exceptions for if a, b, or d are blank. Maybe in those cases you want to allow partial matches. My example was just the basic idea.
Following code is suggested by a helpful user, this works well to Calculate "From", "To", "MAX" etc values of a range. But this code gives results in every row of a range. I want to get the results in only first row of each row. Please help with this.
Sub trial()
Dim lastrow As Long
Dim destSht As Worksheet
Dim i As Long, j As Long
Set destSht = Worksheets("Final")
With Worksheets("Source")
lastrow = .Range("B" & .Rows.Count).End(xlUp).Row
For i = 2 To lastrow + 1 'loop whole range (column C)
If .Cells(i, "C") <> "" Then 'If column C is not empty then
For j = i To lastrow + 1 'Loop "group" range to find next empty cell. Start from current loop i to last row and add one row to get to next empty cell.
If .Cells(j, "C") = "" Then 'When next empty cell is found (i.e. end of small group range) then apply formulas
.Cells(i, "E").Value = .Cells(i, "B").Value 'From
.Cells(i, "F").Value = .Cells(j - 1, "B").Value 'To
.Cells(i, "G").Formula = "=MAX(C" & i & ":C" & j - 1 & ")" 'MAX
.Cells(i, "H").Formula = "=MIN(C" & i & ":C" & j - 1 & ")" 'MIN
.Cells(i, "I").Formula = "=AVERAGE(C" & i & ":C" & j - 1 & ")" 'AVG
Exit For
End If
Next j
End If
Next I
End With
End Sub
This Code gives following result
Desired Result
Try changing this line:
If .Cells(i, "C") <> "" Then 'If column C is not empty then
To this line:
If .Cells(i, "C") <> "" AND .Cells(i-1, "C") = "" Then 'If column C is not empty AND the column C above is empty then
Hope You are all Safe
I'm trying to calculate MAX, MIN and AVG Values of filled cells which are continued without blank cell (As you can see it in the left side of the sample image ).
I'm facing problem in selecting these randomly placed cells and calculate the above values and also "From" and "To" values of respective range.
Please let me know how to do it. So far I've constructed following code
Dim Cel As Range
Dim lastrow As Long
Dim destSht As Worksheet
Set destSht = Worksheets("Final")
With Worksheets("Source")
lastrow = .Range("B" & .Rows.Count).End(xlUp).Row
For Each Cel In .Range("C2:C" & lastrow)
If .Cells(Cel.Row, "C") <> "" Then
Cel.Offset(0, -1).Copy Destination:=destSht.Cells(destSht.Rows.Count, 1).End(xlUp).Offset(0, 1)
'It will give "From" Column
'' Plz suggest for "To" Column
Range("G5").Select
ActiveCell.FormulaR1C1 = "=MAX(RC[-4]:R[4]C[-4])" 'It will give values "MAX" Column
Range("H5").Select
ActiveCell.FormulaR1C1 = "=MIN(RC[-5]:R[4]C[-5])" 'It will give values "MIN" Column
Range("I5").Select
ActiveCell.FormulaR1C1 = "=AVERAGE(RC[-6]:R[4]C[-6])" 'It will give values "AVG" Column
End If
Next
Did some quick, which should work.
I don't know what you want to do in the "Final" worksheet, so haven't focused on that line.
Logic is to have one big loop (For i...) that go through the whole Column C. When a value is found in column C (If .Cells(i, "C") <> "" Then), we perform a "small loop" (For j = i To lastrow + 1) to check next empty cell to decide the "small group" range. When that range is decided we perform the To, From, MAX, MIN and AVG formulas, which has to be dynamic.
Option Explicit
Sub trial()
Dim lastrow As Long
Dim destSht As Worksheet
Dim i As Long, j As Long
Set destSht = Worksheets("Final")
With Worksheets("Source")
lastrow = .Range("B" & .Rows.Count).End(xlUp).Row
For i = 2 To lastrow + 1 'loop whole range (column C)
If .Cells(i, "C") <> "" Then 'If column C is not empty then
For j = i To lastrow + 1 'Loop "group" range to find next empty cell. Start from current loop i to last row and add one row to get to next empty cell.
If .Cells(j, "C") = "" Then 'When next empty cell is found (i.e. end of small group range) then apply formulas
.Cells(i, "E").Value = .Cells(i, "B").Value 'From
.Cells(i, "F").Value = .Cells(j - 1, "B").Value 'To
.Cells(i, "G").Formula = "=MAX(C" & i & ":C" & j - 1 & ")" 'MAX
.Cells(i, "H").Formula = "=MIN(C" & i & ":C" & j - 1 & ")" 'MIN
.Cells(i, "I").Formula = "=AVERAGE(C" & i & ":C" & j - 1 & ")" 'AVG
Exit For
End If
Next j
End If
Next i
End With
End Sub
Result:
I currently have a macro that inserts 3 rows when the value in Column E changes (Course Department). In the 3 rows I am trying to merge the middle row and add the department into this row. I can't work out how to get it to merge, any help would be appreciated.
With Range("e" & myHeader + 2, Range("e" & Rows.Count).End(xlUp)).Offset(, 1)
.Formula = _
"=if(and(r[-1]c[-1]<>"""",rc[-1]<>"""",r[-1]c[-1]<>rc[-1])," & _
"if(r[-1]c=1,""a"",1),"""")"
.Value = .Value
On Error Resume Next
For i = 1 To 3
.SpecialCells(2, 1).EntireRow.Insert
.SpecialCells(2, 2).EntireRow.Insert
Next
This is how it is currently:
This is what I would like to have:
When inserting or deleting rows, work from the bottom up. Some simple offsets and resizing should be sufficient to insert the three rows, merge the cells and transfer the values.
Option Explicit
Sub insertDept3()
Dim i As Long
With Worksheets("sheet10")
For i = .Cells(.Rows.Count, "E").End(xlUp).Row - 1 To 1 Step -1
If .Cells(i, "E").Value <> .Cells(i + 1, "E").Value Or i = 1 Then
.Cells(i + 1, "A").Resize(3, 5).Insert shift:=xlDown
.Cells(i + 2, "A").Resize(1, 5).Merge
.Cells(i + 2, "A") = .Cells(i + 4, "E").Value
End If
Next i
End With
End Sub
I will leave the cell alignment and font formatting to you.
The below code loop column E, import three lines when the value change, merger Column A to column E , import and format value in the middle line.
Try:
Option Explicit
Sub test()
Dim i As Long, Lastrow As Long
Dim Department As String, NextDepartment As String
With ThisWorkbook.Worksheets("Sheet1")
Lastrow = .Cells(.Rows.Count, "E").End(xlUp).Row
For i = Lastrow To 2 Step -1
Department = .Range("E" & i).Value
NextDepartment = .Range("E" & i).Offset(-1, 0).Value
If Department <> NextDepartment Then
.Rows(i).EntireRow.Resize(3).Insert
.Range("A" & i + 1 & ":E" & i + 1).Merge
With .Range("A" & i + 1)
.Value = Department
.Font.Bold = True
.HorizontalAlignment = xlLeft
.VerticalAlignment = xlCenter
End With
End If
Next i
End With
Output: