Test case:
Take an empty sheet, and merge the range "D2:F2". You can do this manually.
Then, activate the macro recorder and select the column E by just clicking on the E letter on the top of the spreadsheet. You will get the following:
Columns("E:E").Select
Now, try to run this line of code from the same macro directly: you will see that it selects the three columns D, E and F.
Question:
Is this a bug of the macro recorder? Or, rather, a bug of VBA itself (that detects the merged range in my column and decides to extend the selection even if explicitly asked to select one single column)? How should I do to select only one of the columns on which a merged range lies via VBA code, exactly as I can do manually?
Need:
I have a spreadsheet with year on a line, months on the below line and days on the below line.
Hence, the days are just cells but months and especially years are shared/merged cells among the several days.
My need is just to detect the current day and select the column, in order for the user to see on which day they should look the data at. But, because of the "years" cell widely merged just above, the entire year is selected.
No, this is not a bug.
Why: Try to manually select the range E1 to E5. That is what is going on when you use Columns("E:E").select. Think of it as .Select not selecting the column, but instead selecting each cell from top to bottom.
The .select method isn't something you should depend on. What exactly are you trying to use select for? There is another (quite arguably better way) to do this.
Edit: Also, as my father always says, merged cells shouldn't be used. He uses "center across selection" instead, which looks exactly like a merged cell without any of the seemingly buggy behavior.
Need: I would use the macro to highlight the data... probably with something like this...
Range("E7").Interior.ColorIndex = RGB(0, 0, 0)
I feel that the question is genuine unlike some of the comments here. I will try to explain.
Using the test case from the question, say I want to do some action only on column D (say change its column width), without changing the same for columns E to F. I can do that in excel by selecting column D specifically by pressing on column header (press on that "D" in the column names bar). If we select column using range selection (mouse or keyboard shortcut CTRL+SPACE), it extends the selection to include E and F columns. But if we press that column D on the header, it only selects one column. I expect VBA to do the same.
Sadly, I couldn't find anything to "select" a single column or range which includes cells merging through multiple columns or range. However, I could do the action on that single column.
I tried following that didn't work. And I feel that it should work.
Range("D:D").Select
Didn't work. Extends the selection to include merged cells. I guess, this is okay.
Columns("D").Select
Didn't work. Extends the selection to include merged cells. I feel this is not okay.
Columns("D").EntireColumn.Select
Even this didn't work. This definitely should've.
So finally I directly applied the action without selecting the cells.
Column("D").ColumnWidth = 10
And this did it. Only the column D width was changed, leaving column E and F untouched. Similarly, I could do font change and other actions.
Only drawback is that I have to do all actions individually. So, I use a loop to perform action on the selection.
Something like this:
For Each x in Range("D:D")
x.font.size = 10
x.font.name = "Calibri"
'...and so on...
Next x
You probably know the row in which the days start. Therefore, instead of selecting the entire column, you could define a range starting from the first day row to the last day row and select that range.
REQUIREMENTS:
Your table should have this values and formats
Then you can loop through each column on row 4 -just assumed- and check each value if they match today. Next you can scroll to that cell using Application.Goto.
CODE:
Sub FindToday()
Dim wsTable As Worksheet '<~ worksheet with your table
Set wsTable = Sheet2
Dim Cols As Integer '<~ a variable to loop through columns
With wsTable
For Cols = 1 To .Cells(4, .Cells.Columns.Count).End(xlToLeft).Column + 1
If .Cells(4, Cols).Value = Date Then '<~ check if the date is today
Application.Goto wsTable.Cells(1, Cols), True '<~ scroll to that cell if true
Exit For
End If
Next
End With
End Sub
If you want just to hide the particular column if there is merged cell try not to select the column just use like this for example -- Columns("N").EntireColumn.Hidden = True... This will solve your doubt.
Related
I have a worksheet and I want to highlight the top two number values in each column.
I was thinking of something like this
Sub like_this()
Dim rng As Range
For Each rng In Range(1, Columns.Count).End(xlToLeft).Column
'highlight top two values in yellow
Next rng
End Sub
How would I go about looping through the cells in those two columns to find the top two values? I know conditional formatting can do it, but I don't have time to do a separate statement for top 2 in every individual column when I have hundreds of them, that's why I suggested a VBA loop.
nick, I think you can accomplish this with conditional formatting. Select the ENTIRE table (not the headers though) and then use the formula in the screenshot below. Set up the formula for the TOP LEFT cell in your selection (in the case of my screenshot that's cell A1).
Then, of course, set it to format the fill as yellow.
I am struggling with copying and pasting formula from one column to another one within using filter in another column.
I got a table with 52 columns (number of rows is changing each month).
In column AW (#49) I have applied filter. It shows only CTD.
After applying this filter I need to copy formula from column AH to column AG. Of course I need to apply this only for filtered/visible cells.
The code I have written is copying/pasting this formula into all cells in column AG (it doesn't consider my filter in column AW).
Another problem is that when applying the filter in column AW then the first visible row doesn't need to be all the time AH2 but it can be e.g. AH15 or whatever. I guess this could be avoid via some dynamic solution. Unfortunately I have no clue how to do it.
Afterwards I would like to apply the same for filters in another columns.
Many warm thanks in advance for any hint! :)
This is my code:
Sub ApplyFilterInColumnAW()
Sheets("DATA").Select
ActiveSheet.ListObjects("tb_DATA").Range.AutoFilter Field:=49, Criteria1:="CTD"
Range("AG2").Select 'dynamic solution?
ActiveCell.FormulaR1C1 = "=[#[Service/Log Formula]]" 'header name of column AH
End Sub
Range.Copy and Range.Paste only work on visible cells. In my example I target the cells in the column using the Column Header.
Sub ApplyFilterInColumnAW()
Const TagetColumnLabel = "Test"
Dim tbl As ListObject
Set tbl = Sheets("DATA").ListObjects("tb_DATA")
With Sheets("DATA")
.ListObjects("tb_DATA").Range.AutoFilter Field:=49, Criteria1:="CTD"
tbl.ListColumns(TagetColumnLabel).DataBodyRange.FormulaR1C1 = "=tb_DATA[[#This Row],[Service/Log Formula]]"
End With
End Sub
In a workbook I have, users either manually enter an account code or select one from a list and the account codes are placed in column C (C7:C446) in a sheet called "JE". The account codes look like this ####### - ### - ## - ######. In column D (D7:D446) in sheet "JE", there is a formula that captures the last 6 digits of the account code. In a sheet called "required_refs", there is a list of 6 digit codes in column A. If the value in the D column in sheet "JE" equals any of the values in column A of "required_refs" sheet, I would like the value in the D column cell to overwrite the cell value in cell D1 in a separate sheet called "references" (I know that may have been confusing, sorry)
Example: if the value of D25 matches any of the values listed in column A of sheet "required_refs", upon double clicking a red colored F25 cell, put the value of D25 (of sheet "JE"), and put it in cell D1 on sheet "references".
I've taken a crack at it as best I know how. I've placed this code in sheet JE:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim project As Range: Set project = Range("D7:D446")
Dim param As Range: Set param = Worksheets("references").Range("D1").Value
For Each cell In project
If project.Value = Worksheets("required_refs").Range("A:A").Value Then
Call gotoRef_ 'macro that simply selects/navigates to the required_ref sheet
project.Value = param
End If
End Sub
Thanks so much in advance for any suggestions on how to complete this. I can elaborate on this further if needed.
This will do what you want:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Intersect(Target, Range("F7:F446")) Is Nothing Then Exit Sub
Dim varReference As Variant
varReference = Columns("D").Cells(Target.Row).Value2
If Not IsError(Application.Match(varReference, Worksheets("required_refs").Columns("A"), 0)) Then
Worksheets("references").Range("D1").Value = varReference
End If
End Sub
Important Points:
Whenever working with event handlers, always limit the scope of the target range in the first line. Otherwise, it might not work correctly or it could slow done your spreadsheet.
Make sure your JE sheet column D values and required_refs sheet column A values are all either text or numbers. Otherwise the values won't be compared correctly.
Note the usage of Application.Match() instead of WorksheetFunction.Match() to access the worksheet function. This, coupled with the use of a Variant type variable, allows us to trap the error that occurs if the match fails.
You can always do this on the sheet. Consider the MATCH function. See here for how to use MATCH.
Or another great tool if you're searching for something in a table associated with a value in another column (not your case I don't think)--VLOOKUP formula. Place this formula in the D cell of the sheet you want to place the numbers in. VLOOKUP is in the following format:
=vlookup(lookup value,table_array,column index number, [range lookup])
The lookup value is the 6 digit code you're looking for (on the JE sheet)
The table_array is simply selecting the values you want to search for (required_refs sheet)
The column index number would be one, since the table only has 1 column. It's basically the column number of the value you're looking for.
And range lookup is for if you think there might be more than one place where it matches.
For your case I think it would look like this:
=vlookup('JE'!D1,'required_refs'!A1:A,1,FALSE)
Then just lock the values you want to keep and click and drag down.
Explanation for VLOOKUP here
Right now I have a really long table in a Word doc which I populated from an Excel worksheet. It has 6 columns and I'm trying to code something in Word VBA that will go through all the rows in the table and delete the entire row if the cell in the first column DOES NOT start with an equal sign ("=").
For example, I'm only trying to keep the rows that has texts like,
"=1+S -03F7", "=1+M -06M1", etc. etc.
How would I code this? I can't give the code anything specific to look for since the parts after the equal sign will be different for every row.
So this wouldn't work, right?:
If Not ActiveDocument.Tables(83).Columns(1).Range.Text = "=" Then
EntireRow.Select
Selection.Delete
I guess I should reference to cells in column 1, not the column itself... Also, it doesn't work because it's only looking for things with just the equal sign... And I don't know how I can get it to select the row if it find the cell without the equal sign. I don't know how to match by case in the cell of the first column.
You can loop through the rows in the table using the Rows property. You can then find the first cell in that Row using the Cells property. You can then check just the first character of the Range:
Sub DeleteUnwantedRows()
Dim t As Table
Dim r As Row
Set t = ActiveDocument.Tables(1)
For Each r In t.Rows
If r.Cells(1).Range.Characters(1) <> "=" Then r.Delete
Next r
End Sub
A lot of the solutions here on SO involve using CountIf to find duplicates. When I have a list of 100,000+ values however, it will often take minutes for CountIf to search for duplicates.
Is there a quicker way to search for duplicates within an Excel column WITHOUT using CountIf?
Thanks!
EDIT #1:
After reading the comments and replies I realize I need to go into greater detail. Let's pretend I'm a birdwatcher, and after I return from a birdwatching trip I input anywhere from 1 to 25 or 50 new birds that I saw on my trip into my "Master List of Birds Seen". This is really a dynamically growing list, and with each addition I want to make sure I'm not duplicating something that already exists in my list.
So, in column A of my file are the names of the birds. Column B-M might contain other attributes of the birds. I want to know if a bird that I just added in column A after my latest birdwatching trip ALREADY exists somewhere ELSE in my list. And, if it does, I would manually merge the data of the 2 entries and throw away some and keep some after careful review. I clearly don't want to have duplicate entries of the same bird in my database.
So, ultimately I want some indication that there is or isn't a duplicate somewhere else, and if there is duplicate please tell me what row to look in (or highlight or color both of the duplicates).
The fastest way that I know of (in case you are using Excel 2007/2010/2011) is to use Data (In Ribbon) | Remove Duplicates to find the total number of duplicates OR to remove duplicates. You might want to move data to a temp sheet before you test this.
The 2nd fastest way is to use Countif. Now Countif can be used in many ways to find duplicates. Here are two main ways.
1) Inserting a New Column next to the data and putting the formula and simply copying it down.
2) Using Countif in Conditional formatting to highlight cells which are duplicates. For more details, please see this link.
suggestions for a macro to find duplicates in a SINGLE column
EDIT:
My Apologies :)
Countif is the 3rd fastest way!
The 2nd fastest way is to use Pivot Tables ;)
What exactly is your main purpose of finding duplicates? Do you want to delete them? Or Do you want to highlight them? Or something else?
FOLLOWUP
Seems like I made a typo in the formula. Yes for large number of rows, CountIf does take minutes as you suggested.
Let me see if I can come up with a VBA code to suit your exact needs.
Sid
You can use VBA - the following function returns a list of unique entries within a list of 100,000 in less than a second. Usage: select a range, type the formula (=getUniqueListFromRange(YourRange)) and validate with CTRL+SHIFT+ENTER.
Public Function getUniqueListFromRange(parRange As Range) As Variant
' Returns a (1 to n,1 to 1) array with all the values without duplicates
Dim i As Long
Dim j As Long
Dim locKey As Variant
Dim locData As Variant
Dim locUniqueDict As Variant
Dim locUniqueList As Variant
On Error GoTo error_handler
locData = Intersect(parRange.Parent.UsedRange, parRange)
Set locUniqueDict = CreateObject("Scripting.Dictionary")
On Error Resume Next
For i = 1 To UBound(locData, 1)
For j = 1 To UBound(locData, 2)
locKey = UCase(locData(i, j))
If locKey <> "" Then locUniqueDict.Add locKey, locData(i, j)
Next j
Next i
If locUniqueDict.Count > 0 Then
ReDim locUniqueList(1 To locUniqueDict.Count, 1 To 1) As Variant
i = 1
For Each locKey In locUniqueDict
locUniqueList(i, 1) = locUniqueDict(locKey)
i = i + 1
Next
getUniqueListFromRange = locUniqueList
End If
error_handler: 'Empty range
End Function
If using Excel 2007 or later (which is likely from the 100,000+ values) you can choose:
Home Tab | Conditional Formatting > Highlight Cell Rules > Duplicate Values...
Right-click a highlighted cell and filter by selected cell color to show just the duplicates (be aware however this can be slow with conditional formatting).
Alternatively run this code and filter for colored cells which takes only a second on 100,000 cells:
Sub HighlightDupes()
Dim i As Long, dic As Variant, v As Variant
Application.ScreenUpdating = False
Set dic = CreateObject("Scripting.Dictionary")
i = 1
For Each v In Selection.Value2
If dic.exists(v) Then dic(v) = "" Else dic.Add v, i
i = i + 1
Next v
Selection.Font.Color = 255
For Each v In dic
If dic(v) <> "" Then Selection(dic(v)).Font.Color = 0
Next v
End Sub
Addendum:
To select only duplicate values without code or formulas, i have found this method useful:
Data Tab | Advanced Filter... Filter in Place, Unique Records Only, OK.
Now select the range of unique values and press Alt+; (Goto Special... Visible cells only). With this selection clear the filter and you will see that all unselected cells are duplicates, you can then press Ctrl+9 (Hide Rows) to show just the duplicates. These rows can be copied to another sheet if needed or marked with an "X".
You do not mention what you want to do when you find them. If you merely want to see where they are...
Sub HighLightCells()
ActiveSheet.UsedRange.Cells.FormatConditions.Delete
ActiveSheet.UsedRange.Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:=ActiveCell
ActiveSheet.UsedRange.Cells.FormatConditions(1).Interior.ColorIndex = 4
End Sub
Preventing Duplicates with Data Validation
You can use Data Validation to prevent you entering duplicate bird names. See Debra Dalgelish's site here
Handling existing duplicates
My free Duplicate Master addin will let you
Select
Colour
List
Delete
duplicates.
But more importantly it will let you run more complex matching than exact strings, ie
Case Insensitive / Case Sensitive searches (sample below)
Trim/Clean data
Remove all blank spaces (including CHAR(160)) see the " mapgie" and "magpie" example below
Run regular expression matches (for example the sample below replaces s$ with "" to remove plurals)
Match on any combination of columns (ie Column A, all columns, Column A&B etc)
I'm surprised that no one has mentioned the RemoveDuplicates method.
ActiveSheet.Range("A:A").RemoveDuplicates Columns:=1
This will simply remove any duplicate entries on the active worksheet in column A. It takes milliseconds to run (tested with 200k rows). Mind you, this will strictly delete all the duplicate entries. Although that isn't how the original question was worded, I do believe that this still serves your purpose.
One simple way of finding unique values is to use the advance filter and filter for unique values only and copy and paste them into other sheet as when the pivot is removed you will get the whole data with the duplicate in them.
Sort the range
and in next column put `=if(a2=a1;1;if(a2=a3;1;0))
"1" will be displayed for duplicates.