5

I have two columns, Column (A) and Column (B) in a spreadsheet.

Column (A) contains names extracted from a query (ex. Brian, Bob, Bill, etc...) and column (B) contains one of three statuses (Assigned, In Progress, or Pending).

However, this query sometimes pulls up some line items showing "Assigned" for the status with no name, therefore corresponding cell representing the name in Column (A) is blank. So I manually fill in those empty cells with "Unknown".

What I want to do is to create a macro that finds the every empty cell in column (A) and fill in the word "Unknown" if the cell to its right contains the word "Assinged".

So the conditions are:

  1. Blank cell in column (A)

  2. Correspoding cell to its right (column B) contains the word "assinged"

This is my Code:

Private Sub CommandButton2_Click()

    For Each cell In Columns("A")
        If ActiveCell.Value = Empty And ActiveCell.Offset(0, 1).Value = "Assigned" Then ActiveCell.Value = "Unknown"
    Next cell

End Sub   
Community
  • 1
  • 1
user1663562
  • 53
  • 1
  • 1
  • 4

3 Answers3

8

There is no need to loop here, take advantage of excels built in methods which will execute faster.

Private Sub CommandButton2_Click()

    Application.ScreenUpdating = False

    With ActiveSheet.UsedRange
        .AutoFilter Field:=1, Criteria1:=""
        .AutoFilter Field:=2, Criteria1:="Assigned"

        If WorksheetFunction.CountBlank(.Columns(1)) > 0 Then
            If .Columns(1).SpecialCells(xlCellTypeVisible).Count > 1 Then
                .Columns(1).SpecialCells(xlCellTypeBlanks).Value = "Unknown"
            End If
        End If

        .AutoFilter
    End With

    Application.ScreenUpdating = True

End Sub
Reafidy
  • 8,240
  • 5
  • 51
  • 83
  • +1 -- this is indeed a little faster than the version I posted with a loop. I timed both in a sheet with over a million rows. This code ran in 2 seconds on my laptop; my loop ran in 5. On a range with a few thousand rows they both ran in under a second and I couldn't tell a difference. I did notice a problem with this though -- it crashes if there aren't any blanks in column 1. – Jon Crowell Sep 11 '12 at 23:39
  • @Head of Catering, I don't usually bother providing error handling in my code samples at SO. Not because I'm lazy! but in general I think there are just to many unknowns to be providing production worthy code and I think in particular it is outside the desired scope of the SO forum. If someone asks though I am happy to help, I will update the answer. – Reafidy Sep 11 '12 at 23:53
  • Very nice! I think it is a worthwhile thing to put in the code, after all, it isn't unlikely a user would click a button twice. I also appreciate learning your technique. Over the years I've made the journey from Recorder to Selector to Iterator and am now on my way to the next level: Autofilterer! – Jon Crowell Sep 12 '12 at 01:14
  • @Head Yes it should definitely be in the code no doubt. But I think the idea at SO is to provide a solution to a specific problem not to provide fully working code blocks. The OP needs to take the sample, test it and then modify it to suit including adding error handling. It shouldn't be a cut & paste job. Well done on your progression through the ranks! ;) One technique I use is to think to myself: "if I'm not able to use VBA how could I do it with just excel?" then use VBA to execute the method you come up with and use VBA to fill in any blanks which aren't possible using simply excel. – Reafidy Sep 12 '12 at 02:02
  • 1
    Have you run that idea by Sid? His answers are frequently fully working code blocks with screen shots and thorough blog-style explanations. I don't disagree with your approach, but I think saying "the idea at SO" is way too broad a brush to use. – Jon Crowell Sep 12 '12 at 12:44
  • +1 - much more simple and easy to understand then what I did. Same concept, better execution. @SiddharthRout - I see now what you were suggesting. – Scott Holtzman Sep 12 '12 at 13:04
  • However, code should be modified to satisfy users requirements of only filling in blanks cells in the usedrange with "unknown", not the whole column. – Scott Holtzman Sep 12 '12 at 13:48
  • @Head, You have taken my comment out of context. I said "I think the idea". Meaning "its my interpretation". I was not expecting others to do the same as me and how Sid answers questions is respectfully his business. Many users post "give me the code" style questions. I prefer to help those who just need a hint in the right direction, I don't have a lot of spare time. The speciallcells method throws an error if no cells are found. You expected me to provide error handling, I explained why I didn't. If this was production code it would probably be 10 lines longer depending on requirements. – Reafidy Sep 12 '12 at 22:46
  • @Scott, In my testing it does only fill the blank cells not the whole column. And only the cells with "Assigned" in the adjacent column. Perhaps the OP wasn't using my edited code or am I missing something? And thanks for the comments you made regarding using my code. – Reafidy Sep 12 '12 at 22:47
  • @Reafidy, I did in fact misinterpret your comment. Your clarification is one of the reasons I love SO -- the different approaches that all lead to a significant improvement in knowledge and coding standards. I don't prefer Sid's approach to yours. They are simply different, and they both work. – Jon Crowell Sep 13 '12 at 01:31
  • @Reafidy - Ah, yes, now I see how it just fills the blank cells in the usedrange in column A. I think I misread yesterday :) – Scott Holtzman Sep 13 '12 at 14:16
2

Welcome to SO.

Try this code. It will work a bit faster and should get you what you want.

Update: Made the code more bullet proof!

Private Sub CommandButton2_Click()

Dim cel As Range, rngFind As Range, rngFilter As Range
Dim wks As Worksheet

Set wks = Sheets("sheet1")

With wks

    '-> Error check to make sure "blanks" exist
    Set rngFind = .Range("A1:A" & .Range("B" & Rows.Count).End(xlUp).Row).Find("", lookat:=xlWhole)

    If Not rngFind Is Nothing Then

        Set rngFilter = .Range("A1:B" & .Range("B" & Rows.Count).End(xlUp).Row)

        rngFilter.AutoFilter 1, "="

        '-> Error check to make sure "assigned" exists for blank cells
        Set rngFind = .Columns("B:B").SpecialCells(xlCellTypeVisible).Find("Assigned", lookat:=xlWhole)

        If Not rngFind Is Nothing Then
        '-> okay, it exists. filter and loop through cells

            rngFilter.AutoFilter 2, "Assigned"

            Set rngFind = Intersect(.UsedRange, .UsedRange.Offset(1), .Columns(1)).SpecialCells(xlCellTypeVisible)

            For Each cel In rngFind

                If cel.Offset(0, 1).Value = "Assigned" Then cel.Value = "Unknown"

            Next cel

        End If

    End If

End With


End Sub
Scott Holtzman
  • 27,099
  • 5
  • 37
  • 72
  • I get an **Object required** error with `.Text`, but it works with `.Value`. Could be my version of Excel, could be the code. – LittleBobbyTables - Au Revoir Sep 11 '12 at 17:59
  • Thank you for you're quick response. However, the code doesn't seem to work. It highlights "Set rng = Range(Range("A1"), Range("A" & Rows.Count).End(xlUp)).SpecialCells(xlCellTypeBlanks)" – user1663562 Sep 11 '12 at 18:06
  • 2
    @LittleBobbyTables: No `.Text` is a readonly property. You have to use `.Value` – Siddharth Rout Sep 11 '12 at 18:08
  • @Scott: Why not double filter it? Col A for Blank and then Col B for "assigned" in one go? – Siddharth Rout Sep 11 '12 at 18:10
  • @LittleBobbyTables / @SiddharthRout: my bad on the `.Text` vs `.Value` - thanks for the keen eyes – Scott Holtzman Sep 11 '12 at 18:10
  • @SiddharthRout - re: 'Why not double filter it?` - See my updated answer! – Scott Holtzman Sep 11 '12 at 18:10
  • @user1663562 see my updated code - also, do you have blank cells in column A? – Scott Holtzman Sep 11 '12 at 18:11
  • @ScottHoltzman: No that is not a double filter. I meant this For example ` ActiveSheet.Range("$A$1:$B$9").AutoFilter Field:=1, Criteria1:="=" ActiveSheet.Range("$A$1:$B$9").AutoFilter Field:=2, Criteria1:="<>" – Siddharth Rout Sep 11 '12 at 18:14
  • The Blank Cells are in Column (A) so for example Cell (a2).value = bob amd Cell (b2).Value = Assigned, and Cell (a3).value = empty and cell (b3).Value = assigned. I want to located all cells in column A that have empty values and the cell next to it has the word assinged(such as cells (a3) and (a4) – user1663562 Sep 11 '12 at 18:16
  • @user1663562 - my edited answer should work then. Are you getting an error? – Scott Holtzman Sep 11 '12 at 18:17
  • @SiddharthRout - that is what I do. Expect I check for "Assigned" in column B first before I apply the 2nd filter. Really, should check for blanks in column A first as well... – Scott Holtzman Sep 11 '12 at 18:18
  • Ok so no errors pop up when running the macro, however nothing is happening to the spreadsheet. The Blank Values remain empty – user1663562 Sep 11 '12 at 19:10
  • @user1663562 => make sure `Set wks = Sheets("sheet1")` refers to the sheet you need to work with. So you may need to change `"sheet1"` to whatever sheet name you are using :) – Scott Holtzman Sep 11 '12 at 19:17
  • I did, its was set wks = Sheets("Sheet3") and still nothing – user1663562 Sep 11 '12 at 19:22
  • hmmm... what happens if you filter on blanks, manually? Does it work? In other words, are your blanks really "blanks" or does excel not read them as blanks -> which happens sometimes when you pull data from other sources. If you can post a link to your data source (or dummy version of it) that would be great. – Scott Holtzman Sep 11 '12 at 19:28
  • Heres a link to a Dummy file mimicing the actual spreadsheet http://dl.dropbox.com/u/104835256/Dummy.xls – user1663562 Sep 12 '12 at 12:46
  • Ah! I see what happens. All the blanks are at the end of column A, which makes this line `Set rngFind = .Range("A1:A" & .Range("A" & Rows.Count).End(xlUp).Row).Find("", lookat:=xlWhole)` miss them altogether, because it finds the last used row in column A. So, to be safe chance the line i just referenced to this: `Set rngFind = .Range("A1:A" & .Range("B" & Rows.Count).End(xlUp).Row).Find("", lookat:=xlWhole)` which uses column B to get the real last used row. – Scott Holtzman Sep 12 '12 at 12:58
  • @user1663562 - you should know that Readify's solution is the one to go with here. – Scott Holtzman Sep 12 '12 at 13:10
  • Thanks Scott, your version works perfectly :). Readify's solution works however his macro doesnt really satisfy the second condition (only target cells that have "assinged" to its right) it fills in the word unknown for all empty cells in Column A. But anyways the whole purpose of this Macro is to learn more about VBA in excel. Anyways, thanks again. – user1663562 Sep 12 '12 at 13:34
  • @user1663562 -> yes, I see that it fills the whole column's blank cells, but that can easily be modified. Anyway, you are right. And you have certainly learned a lot, it looks like! I think the point I was trying to make is Readify's solution is more efficient (and easier to read!) – Scott Holtzman Sep 12 '12 at 13:49
1

If you only need to do this a few times you could

  1. format your used range as a table
  2. on column A filter to only show "(Blanks)"
  3. on column B filter to only show "assinged"
  4. select all the resulting cells in column B
  5. press alt + : to select only the visible cells
  6. press F2
  7. type "unknown"
  8. press ctrl + enter

Your bad data should be good now!

Obviously this is a non-vba based solution but if you can avoid coding it's probably for the best.

Brad
  • 11,934
  • 4
  • 45
  • 73
  • I usually Sort the List, Scroll down, and then insert the Word Unknown to the Blank Items. I created a Macro that extract the relevant data from a seperate WS and sorts it. Now all i need is to create this second macro and this process will literally be done in two click. How can i post an excel sheet on this form? or should i send it to you directly? – user1663562 Sep 11 '12 at 19:38
  • @user1663562 - So the data you are working with is assembled dynamically by you already? and will need to be assembled dynamically in the future (not a one time operation)? If you do need this then you should pursue Scott's answer. My method is just the fast solution if you don't want to involve VBA. – Brad Sep 11 '12 at 19:48
  • @user1663562 - Also, it's not advisable (or really possible as far as I know) to post full files on SO. It might solve the problem for you to have it worked out in the background but it removes the solution from the public domain. Future folks won't be able to benefit as you would have. – Brad Sep 11 '12 at 19:49
  • @user1663562 - you can use www.dropbox.com as a file drop location. Then you can post the link here on SO. – Scott Holtzman Sep 11 '12 at 19:54
  • Heres a link to a Dummy file mimicing the actual spreadsheet http://dl.dropbox.com/u/104835256/Dummy.xls – user1663562 Sep 12 '12 at 12:46