VBA merge non-adjacent cells containing same text in Excel - vba

I am building a calendar in Excel that automatically maps event information from a table to a dynamic calendar view. Each row represents a time from 8AM - 6PM, and each column represents a day of the week from Sunday - Saturday. I was able to map information for each unique event to two separate cells in each column, one for start time and one for end time. I am looking for help with building a macro to merge the cells containing the same information into one so the calendar is cohesive. E.g. Event A starts at 9AM and ends at 11AM. There is currently one cell populated at 9AM and one at 11AM, but the cell for 10AM is blank and I'd like to merge the two populated cells from the 9AM cell to 11AM cell. As the populated cells will not always be adjacent, the offset function doesn't seem to work in this case.
Here is the pseudo code I'm trying to accomplish:
For each column in a specified region
loop through each row
if two cells contain identical text
merge those two cells
Here is the little bit of code I've managed to come up with so far. You can tell there are many gaps and probably syntax errors:
Sub MergeCells
Dim cells As String
cells = ActiveSheet.Range("C8:V28,C31:V51,C54:V74,C77:V97,C100:V120")
If ActiveSheet.Range(cells).??? Then
ActiveSheet.Range(cells).Merge
End If
End Sub
Any help would be greatly appreciated!
Before picture
After picture

Okay - this may be overkill, and you may need to tweak, but this was fun to work on.
Sub combine_Same()
Application.DisplayAlerts = False
Dim tableRng As Range
Dim i As Long, k As Long, lastRow As Long
Dim curText As Range, prevText As Range
Dim tableRanges As Variant
tableRanges = Split("b3:e20,C31:V51,C54:V74,C77:V97,C100:V120", ",")
Dim rng As Long
For rng = LBound(tableRanges) To UBound(tableRanges)
Debug.Print "Working with: " & tableRanges(rng)
Set tableRng = Range(tableRanges(rng))
' tableRng.Select
lastRow = tableRng.Rows(tableRng.Rows.Count).Row
For k = tableRng.Columns(1).Column To tableRng.Columns.Count
For i = lastRow To tableRng.Rows(1).Row Step -1
Set curText = Cells(i, k)
Set prevText = curText.End(xlUp)
If curText.Value = prevText.Value And Not IsEmpty(curText) Then
'curText.Select
Range(curText, prevText).Merge
curText.VerticalAlignment = xlCenter
ElseIf curText.Value = curText.Offset(-1, 0).Value And Not IsEmpty(curText) Then
'curText.Select
Range(curText, curText.Offset(-1, 0)).Merge
curText.VerticalAlignment = xlCenter
End If
Next i
Next k
Next rng
Application.DisplayAlerts = True
End Sub

Related

Copy values from Range and paste each one with different given (row) offset in another sheet

First of all I would like to introduce myself. Iam Miguel and I recently started to learn VBA in order to improve and save time at work. I am familiar with formulas, all types, but when turning to VBA I get sometimes stuck.
I am trying to loop the range A2:A355 from Sheet"Aux" and copy each value to sheet "CS", and each value shall be pasted in Column A:A, but with the offset given in range B2:B355 Sheet "Aux". For Example I give the example attached.
Sample Code:
This is the code:
Sub cablexsection()
Dim s As Integer
Dim smax As Integer
smax = Sheets("Aux").Range("b1").Value
Sheets("CS").Activate
For s = 3 To smax
Sheets("CS").Cells(s, 1).Value = Sheets("Aux").Cells(s, 1).Value
'here I have to set the offset to down in order to paste cells given Sheets("Aux").Cells(s, 2) values
Next s
End Sub
And under the link you can find the file to be worked in:
Original File
Thank you very much and sorry if this question is repeated. I have tried to look through the forum but maybe I do not know what to write exactly.
Try this
Option Explicit
Sub CableXsection()
Dim wsAux As Worksheet, wsCS As Worksheet
Dim s As Long, sMax As Long, offSetCell As Range
Set wsAux = ThisWorkbook.Worksheets("Aux")
Set wsCS = ThisWorkbook.Worksheets("CS")
sMax = wsAux.Range("B1").Value
Application.ScreenUpdating = False
For s = 3 To sMax
Set offSetCell = wsAux.Cells(s, 2) '2 is the offset column from the same row
If Not IsError(offSetCell) And IsNumeric(offSetCell) Then
wsCS.Cells(offSetCell.Value2 + s, 1).Value = wsAux.Cells(s, 1).Value
End If
Next
Application.ScreenUpdating = True
End Sub

Select cells between bold cells using a loop

I am working with data where the only consistency is the layout and the bold headings to distinguish between a new date.
I am trying to find the cells in between these cells in bold, find the value "Individual" (in column A) in the selected rows, then sum the values of the given rows in column D (as there can be more then 1 row with "Individual"), and copy this new value to a different cell.
Since the cells between the bold is one date, if the value is not there, the output cell needs to shift down one without filling in anything.
Here is what I have so far:
Sub SelectBetween()
Dim findrow As Long, findrow2 As Long
findrow = range("A:A").Find("test1", range("A1")).Row
findrow2 = range("A:A").Find("test2", range("A" & findrow)).Row
range("A" & findrow + 1 & ":A" & findrow2 - 1).Select
Selection.Find("Individual").Activate
range("D" & (ActiveCell.Row)).Select
Selection.copy
sheets("Mix of Business").Select
range("C4").Select
ActiveSheet.Paste
Exit Sub
errhandler:
MsgBox "No Cells containing specified text found"
End Sub
How can I loop through the data and each time it loops through a range, no matter if it finds the value (e.g. individual) or not, shifts down one row on the output cell? Also, how can I change the findrow to be a format (Bold) rather then a value?
Here is some data for reference:
This is what I am trying to get it to look like:
So you have a good start to trying to work through your data. I have a few tips to share that can hopefully help get you closer. (And please come back and ask more questions as you work through it!)
First and foremost, try to avoid using Select or Activate in your code. When you look at a recorded macro, I know that's all you see. BUT that is a recording of your keystrokes and mouseclicks (selecting and activating). You can access the data in a cell or a range without it (see my example below).
In order to approach your data, your first issue is to figure out where your data set starts (which row) and where it ends. Generally, your data is between cells with BOLD data. The exception is the last data set, which just has a many blank rows (until the end of the column). So I've created a function that starts at a given row and checks each row below it to find either a BOLD cell or the end of the data.
Private Function EndRowOfDataSet(ByRef ws As Worksheet, _
ByVal startRow As Long, _
Optional maxRowsInDataSet As Long = 50) As Long
'--- checks each row below the starting row for either a BOLD cell
' or, if no BOLD cells are detected, returns the last row of data
Dim checkCell As Range
Set checkCell = ws.Cells(startRow, 1) 'assumes column "A"
Dim i As Long
For i = startRow To maxRowsInDataSet
If ws.Cells(startRow, 1).Font.Bold Then
EndRowOfDataSet = i - 1
Exit Function
End If
Next i
'--- if we make it here, we haven't found a BOLD cell, so
' find the last row of data
EndRowOfDataSet = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
End Function
To show you how to use that with your specific data, I've created a test subroutine indicating how to loop through all the different data sets:
Option Explicit
Public Sub DataBetween()
Dim thisWB As Workbook
Dim dataWS As Worksheet
Set thisWB = ThisWorkbook
Set dataWS = thisWB.Sheets("YourNameOfSheetWithData")
'--- find the first bold cell...
'Dim nextBoldCell As Range
'Set nextBoldCell = FindNextBoldInColumn(dataWS.Range("A1"))
'--- now note the start of the data and find the next bold cell
Dim startOfDataRow As Long
Dim endOfDataRow As Long
Dim lastRowOfAllData As Long
startOfDataRow = 3
lastRowOfAllData = dataWS.Cells(ws.Rows.Count, "A").End(xlUp).Row
'--- this loop is for all the data sets...
Loop
endOfDataRow = EndRowOfDataSet(dataWS, startOfDataRow)
'--- this loop is to work through one data set
For i = startOfDataRow To endOfDataRow
'--- work through each of the data rows and copy your
' data over to the other sheet here
Next i
startOfDataRow = endOfDataRow + 1
Do While endOfDataRow < lastRowOfAllData
End Sub
Use both of those together and see if that can get you closer to a full solution.
EDIT: I should have deleted that section of code. It was from an earlier concept I had that didn't completely work. I commented out those lines (for the sake of later clarity in reading the comments). Below, I'll include the function and why it didn't completely work for this situation.
So here's the function in question:
Public Function FindNextBoldInColumn(ByRef startCell As Range, _
Optional columnNumber As Long = 1) As Range
'--- beginning at the startCell row, this function check each
' lower row in the same column and stops when it encounters
' a BOLD font setting
Dim checkCell As Range
Set checkCell = startCell
Do While Not checkCell.Font.Bold
Set checkCell = checkCell.Offset(1, 0)
If checkCell.Row = checkCell.Parent.Rows.Count Then
'--- we've reached the end of the column, so
' return nothing
Set FindNextBoldInColumn = Nothing
Exit Function
End If
Loop
Set FindNextBoldInColumn = checkCell
End Function
Now, while this function works perfectly well, the situation is DOES NOT account for is the end of the last data set. In other words, a situation like this:
The function FindNextBoldInColumn will return nothing in this case and not the end of the data. So I (should have completely) deleted that function and replaced it with EndRowOfDataSet which does exactly what you need. Sorry about that.

Updating the sheets with other sheet

I am having three Sheets, Sht1 having my Basic data, sht2, having an data from last week, sht3 having an data with present week.
I am copying the data from sht1 to sht3, which ever has an Status red in columnJ.
After copying, i look into the lastweek sht and check for value in L and then fill the column N,O,P in sheet thisweek.
In the next step, whenever i click a button, the lastweeksht should be update by the data in present week sheet and present week should be deleted and evaluated with the new result. How can i do it. I should do the last step of moving the data to last week and updating thisweek to new result ? I am confused how to do this.
below are the codes, where i am using for copying the Status from Basic sht to this week and the vlookup formula i am using in thisweek.
Sub red()
Dim cell As Range
Dim nextrow As Long
Application.ScreenUpdating = False
For Each cell In Sheets("Red").Range("J5:J" & Sheets("Red").Cells(Sheets("Red").Rows.Count, "J").End(xlUp).Row)
If cell.Value = "rd" Then
nextrow = Sheets("ThisWeek").Cells(Sheets("ThisWeek").Rows.Count, "J").End(xlUp).Row
Rows(cell.Row).Copy Destination:=Sheets("ThisWeek").Range("A" & nextrow + 1)
End If
Next
Application.ScreenUpdating = True
End Sub
Sub lookup()
Dim tr As Long
Dim trsh As Long
tr = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
trsh = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("ThisWeek").Range("M2:M" & tr).Formula = Application.WorksheetFunction.IfError(Application.VLookup(Sheets("ThisWeek").Range("L2:L" & trsh), Sheets("LastWeek").Range("$A:$P"), 13, 0), "")
End Sub
Sub lookupredaction()
Dim tr1 As Long
Dim trsh1 As Long
tr1 = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
trsh1 = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("ThisWeek").Range("N2:N" & tr1).Formula = Application.WorksheetFunction.IfError(Application.VLookup(Sheets("ThisWeek").Range("L2:L" & trsh1), Sheets("LastWeek").Range("$A:$P"), 14, 0), "")
End Sub
Sub lookupredoverdue()
Dim tr2 As Long
Dim trsh2 As Long
tr2 = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
trsh2 = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("ThisWeek").Range("O2:O" & tr2).Formula = Application.WorksheetFunction.IfError(Application.VLookup(Sheets("ThisWeek").Range("L2:L" & trsh2), Sheets("LastWeek").Range("$A:$P"), 15, 0), "")
End Sub
Sub lookupcomments()
Dim tr3 As Long
Dim trsh3 As Long
tr3 = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
trsh3 = Sheets("ThisWeek").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("ThisWeek").Range("P2:P" & tr3).Formula = Application.WorksheetFunction.IfError(Application.VLookup(Sheets("ThisWeek").Range("L2:L" & trsh3), Sheets("LastWeek").Range("$A:$P"), 16, 0), "")
End Sub
this how the Basic data of my sheet Looks like. I always take the data from database and paste in this way
Image 1
First half is the Image of the second sheet. I call this sheet as last week. This sheet has an extra column E and F with Actions and no Actions evaluated. For now, this sheet is taken from the calculations of last week. in column G i have an formula, which calculates the CW. below is the sample Image of my third sheet, i call this as this week. In column G again i have an formula that calculates the CW. Here i use the first code, which always copy the Status with red to my this week.then, I use lookup codes to look into the ID in column A (as per dumy sheet) and copy the Actions and no Actions, accordingly.
Image 2
Now what i would like to have is, an button in my Basic sheet, and when i click the button, it should delete the last week and update the data of this week to last week . This week data should be deleted.
Later i have an button in my Basic sheet, which will include all the codes i have in the post and i could calculate for the new set of data. I have half done and left with other half struck how to achieve. Could someone help me achieve this. I would be thankful to you. and would improve myself with programing. Thank you in advance.
Posting this as an answer as it worked for you,
Use Range.ClearContents to clear the data in your lastweek sheet
Use Range.Copy Destination to copy the data from presentweek to lastweek
Clear the data in presentweek using clear contents.
You can use Sheets("ThisWeek").Range("A1:B10") (example) to operate different sheets with the same macro.

Merging Rows of column B with the count of already merged rows A

I want to merge cells in one row (belongs to Column B) with the count of already merged different cell(belongs to Column A) .How can i start coding ?
this is the screenshot that i want
Merging cells in a spreadsheet means taking two or more cells and
constructing a single cell out of them. When you merge two or more
adjacent horizontal or vertical cells, the cells become one larger
cell that is displayed across multiple columns or rows. When you
merge multiple cells, the contents of only one cell (the upper-left
cell for left-to-right languages, or the upper-right cell for
right-to-left languages) appear in the merged cell. The contents of
the other cells that you merge are deleted. For more details please
go through this MSDN article Merge and unmerge
cells
Simple VBA code for Merging Cell
Sub merg_exp_1()
ActiveSheet.Range("A1:C10").Merge
End Sub
Sample data before and after running the program is shown.
Now let us see, If we merge a row what happens. Sample code for this
exercise though general is being tested for one situation only and
it as follow :
Sub Merge_Rows()
Dim rng As Range
Dim rrow As Range
Dim rCL As Range
Dim out As String
Dim dlmt As String
dlmt = ","
Set rng = ActiveSheet.Range("A1:C5")
For Each rrow In rng.Rows
out = ""
For Each rCL In rrow.Cells
If rCL.Value <> "" Then
out = out & rCL.Value & dlmt
End If
Next rCL
Application.DisplayAlerts = False
rrow.Merge
Application.DisplayAlerts = True
If Len(rrow.Cells(1).Value) > 0 Then
rrow.Cells(1).Value = Left(out, Len(out) - 1)
End If
Next rrow
End Sub
Sample data before and after running the program is shown. You can see this won't meet your objective.
Next we can try merging by column approach. Here also we are trying
for one column i.e. Column B to see the effect. Sample code as
follows.
Sub Merge_col_exp()
Dim cnum As Integer
Dim rng As Range
Dim str As String
For i = ActiveSheet.UsedRange.Rows.Count To 1 Step -1
cnum = Cells(i, 1).MergeArea.Count
Set rng = Range(Cells(i, 2), Cells(i - cnum + 1, 2)) ' only to demonstrate working in 2nd column
For Each cl In rng
If Not IsEmpty(cl) Then str = str + "," + cl
Next
If str <> "" Then str = Right(str, Len(str) - 1)
Application.DisplayAlerts = False
rng.Merge
rng = str
Application.DisplayAlerts = True
str = ""
i = i - cnum + 1
Next i
End Sub
Sample data before and after running the program is shown. You can see this is closer to your requirement. You can extend functionality of this program by finding Last Column in the Actively used range. Extend program functionality to cover upto last column.

Hide rows based on the date

I am trying to keep the last 365 days visible in a micro tracking worksheet. As a new date gets inputted it would hide the first visible entry on the sheet so that only 365 cells are constantly displayed with the newest date at the bottom(ex Jan 15, 2015) and the oldest date at the top(Jan 15, 2014). When the user inputs Jan 16, 2015 it would hide Jan 15, 2014 so that the first entry is now Jan 16, 2014, and so on.
It has probably been about 15 years since I last used VBA but currently the code shown below will hide row 3 (where the first date and data is entered) but after that I can't get it to then hide row 4 once row 369 has text entered. Some insight into what I might be doing wrong would be greatly appreciated.
I would also assume that as this sheet became progressively larger it would start to slow down in opening or running smoothly so I would have to start fresh unless there is a way to make sure it always stays fast.
Dim i As Integer
Dim j As Integer
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
i = 3
j = 368
If Cells(j, j) = "" Then
Rows(i).Hidden = True
End If
i = i + 1
j = j + 1
End Sub
This is a completely different approach, but might suit you better in the long run.
Instead of hiding the rows to view what you are interested in. This approach uses two sheets.
Log Sheet: Contains all days
Report Sheet: Re-fills with only the last 365 days.
Setup Involved:
Set a second sheet up for your report, and give it the same headers as your Log Sheet.
Place the code provided in a module
If you want, you can add a workbook event so when the workbook opens, you can call this sub and have it update itself, or attach it to a hotkey or button.
This gives you plenty of room to create new formulas and charts to work over the set range of the report sheet. You can hide the Log Sheet.
Code:
Sub lastYearReportFill()
Dim lastRow As Long, lastCol As Long, lRow As Long, rRow As Long
Dim log As String, report As String
Dim today As Date, tempDate As Date
Dim daysTest As Long
log = "Log" 'Name your worksheets here
report = "Report"
today = Now
lastRow = Sheets(log).Range("A" & rows.count).End(xlUp).row
lastCol = Sheets(log).Cells(2, Columns.count).End(xlToLeft).column 'Using Header Row
For lRow = 3 To lastRow
tempDate = Sheets(log).Cells(lRow, 1)
daysTest = DateDiff("d", tempDate , today)
If daysTest = 365 Then
Exit For
End If
Next lRow
For rRow = 3 To 368
For lCol = 1 To lastCol
Sheets(report).Cells(rRow, lCol).Value = Sheets(log).Cells(lRow, lCol).Value
Next lCol
lRow = lRow + 1
Next rRow
End Sub
This should do the trick:
Sub HideRows()
Dim lngLastRow As Long
lngLastRow = Sheets("Sheet1").Cells(1, 1).End(xlDown).Row
If lngLastRow < 365 Then End
Rows(lngLastRow - 365).Hidden = True
End Sub
This is assuming that:
you are working in a sheet named "Sheet1" (if not, change the name in line 3 of the code accordingly)
the dates are in column A and start in row 1 (even the hidden ones). If the dates are in a different column, then change the second number in the cells(1, 1) statement to the number of the row. And if the dates do not start in row 1, change the first number of the cells(1, 1) statement to the row number of the first date.
And if you want to keep the row for the day exactly one year ago (as in, keep 1-15-14 on 1-15-15), you may need to change 365 in line 5 of the code to 366. This code also assumes that all other rows besides the one that was visible yesterday but doesn't need to be today are already hidden.
If you are concerned with speed, use Range AutoFilter Method which I answered HERE.
Applying it to your case:
Private Sub UpdateVisibleDates(sh As Worksheet, drng As Range)
With sh
Dim latest As Date
latest = .Range("A:A").Find("*", .Range("A1"), , , , xlPrevious).Value2
.AutoFilterMode = False
drng.AutoFilter 1, ">" & (latest - 365), xlAnd, "<=" & latest, False
End With
End Sub
Then just call it in your Worksheet_Change Event.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo halt
Application.EnableEvents = False
If Not Intersect(Target, Me.Range("A:A")) Is Nothing Then
Dim r As Range
Set r = Me.Range("A1:A" & Me.Range("A:A") _
.Find("*", Me.Range("A1"), , , , xlPrevious).Row)
UpdateVisibleDates Me, r
End If
forward:
Application.EnableEvents = True
Exit Sub
halt:
MsgBox "Error: " & Err.Number & vbCrLf & _
Err.Description, vbExclamation
Resume forward
End Sub
This is considering you have a complete date in Column A and your input doesn't skip dates.
But regardless, it will still hide dates not within the 365 date difference of the last entered date. HTH.