1

My brain is fried and this is easy points for the usual suspects. div is an array holding sheet names. I am looping through sheets in a master book and if one of the master sheets match one of the sheets in the div array, I want to transfer some data from master sheet to a sheet in thisworkbook.

In the event the sheet does not exist in thisworkbook, add one and name it after the master sheet. What's the most efficient way to do this? I feel like nested loops is a bad idea -_- A collection perhaps?

For i = 0 To UBound(div())

    For Each s In book.Worksheets

        wsName = Left(s.Name, 5)

        If div(i) = wsName Then
        If wsExists(wsName) Then
            Set ws = ThisWorkbook.Worksheets(wsName)
            Exit For
            'Debug.Print "true " & ws.name
        Else
            Set ws = ThisWorkbook.Worksheets.Add
            ws.Name = Left(s.Name, 5)
            'Debug.Print "false " & ws.name
        End If
        end if
    Next

    With ws
    .Columns(Start).Resize(, 2).Value = s.Columns("A:B").Value
    .Columns(Start + label).Resize(, cols).Value = s.Columns(Start + label).Resize(, cols).Value
    End With

Next

Do I even need to check if sheet exists? Code stolen from Tim.

Function wsExists(sName As String) As Boolean
Dim sht As Worksheet

On Error Resume Next
Set sht = ThisWorkbook.Sheets(sName)
On Error GoTo 0
wsExists = Not sht Is Nothing

End Function

Edit: I am calling the loop from a separate routine.

Call drop(thisWB, thisRange, ccArr)

where ccArr is

Dim ccArr() As Variant
ccArr = Array("30500", "30510", "30515", "30530", "30600", "30900", "40500")

The routine where above loop resides opens with

Sub drop(book As Workbook, cols As Integer, div As Variant, Optional startCol As Integer)

but I am getting a byref error trying to pass the array ;_;

Community
  • 1
  • 1
findwindow
  • 3,133
  • 1
  • 13
  • 30
  • What will happen if you don't check for it's existence? Will it error with some sort of "sheet with that name already exists. You could just handle the error. But there should be no reason you can't just check for it. – MatthewD Oct 16 '15 at 19:42
  • Should be efficient if your number of sheets is reasonable. Would put a few test together with example but I have to run. – MatthewD Oct 16 '15 at 19:47
  • Just out of curiosity, what percentage of the time (roughly) would an element of `div` not match an existing sheet name? – Excel Hero Oct 16 '15 at 19:57
  • Excellent question. Thisworkbook is actually barren to begin. BUT I will run it again with a second master book where 90%+ of the sheets will match. Edit: the two master books hold 300+ sheets. The array will never hold more than, say 30ish. Edit2: I realize I could do this far more intelligently but I can't ask broad questions on SO =P – findwindow Oct 16 '15 at 19:59
  • change `div As Variant` to `div() as Variant` in your `Sub drop`. You can also change `Dim ccArr() as Variant` to `Dim ccArr() as String` then change your argument in `drop` to `div() as String` as well. – Scott Holtzman Oct 16 '15 at 20:01
  • A slightly different question... will it ever happen that 100% of the elements in `div` will match existing sheets when your proc runs? – Excel Hero Oct 16 '15 at 20:02
  • Hmmm reluctant to commit to 100% but yes it's possible. I guess I would know after I run the macro XD – findwindow Oct 16 '15 at 20:04
  • 1
    ah ... its because you div(i) is variant type and the function requires a string ... you can change `div(i) to string` or maybe `Cstr(div(i))` will work (not tested) – Scott Holtzman Oct 16 '15 at 20:05
  • lol Scott. It worked but you killed part of my code and didn't replace it =P Getting object error on `s.Columns("A:B").Value`. You killed `s` which was the sheet of masterbook =P Edit: wait, I dont wanna pass thisworkbook. I want master XD Edit2: wait, maybe not... brain toooooo fried ;_; – findwindow Oct 16 '15 at 20:07
  • I think I may have solved that, too ... see my edited answer ... – Scott Holtzman Oct 16 '15 at 20:11

2 Answers2

1

Your nested loop is superfluous. You can check the sheet name from div directly against the workbook you want to check it against, then add it if needed.

See the code below, which also addresses the concerns in the edits to your OP. I modified the wsExists function to include a set reference to a particular workbook, which I think makes it more dynamic.

'assumes thisWB  and thisRange set above

Dim ccArr() As String, sList As String
sList = "30500,30510,30515,30530,30600,30900,40500"
ccArr = Split(sList, ",")

drop thisWB, thisRange, ccArr 'assumes thisWb and thisRange are set already

' rest of code
'==================================================

Sub drop(book As Workbook, cols As Integer, div() As String, Optional startCol as Integer)

For i = 0 To UBound(div())

    If wsExists(ThisWorkbook, div(i)) Then
        Set ws = ThisWorkbook.Worksheets(div(i))
        Exit For
        'Debug.Print "true " & ws.name
    Else
        Set ws = ThisWorkbook.Worksheets.Add
        ws.Name = div(i)
    End If

    'i think you need this here, otherwise, it will only work on the last worksheet in your loop
    With ws
        Dim s As Worksheet
        Set s = book.Sheets(div(i))
        .Columns(Start).Resize(, 2).Value = s.Columns("A:B").Value
        .Columns(Start + Label).Resize(, cols).Value = s.Columns(Start + Label).Resize(, cols).Value
    End With

Next

End Sub

Function wsExists(wb As Workbook, sName As String) As Boolean
Dim sht As Worksheet

On Error Resume Next
Set sht = wb.Sheets(sName)
On Error GoTo 0
wsExists = Not sht Is Nothing

End Function
Scott Holtzman
  • 27,099
  • 5
  • 37
  • 72
  • 1
    I think I need to change `s.Columns("A:B").Value` to `book.Worksheets(wsName).Columns("A:B").Value` yes? – findwindow Oct 16 '15 at 20:10
  • Downvoted and flagged. Edit: you also killed the `left`. I was not supposed to think. I expect turnkey answers. Gonna ask for you to get banned from SO. – findwindow Oct 16 '15 at 20:17
  • lolol I kid =P Upvoted. Question, it works but it's slow? Edit: ooo you set `s`. Guess that works too. Edit2: actually, still no `left` so still fail =P No big deal. More importantly, the `split` forces a string I guess? – findwindow Oct 16 '15 at 20:20
  • bizarre joke - that through me through a loop (no pun intended) as I was just reading a thread about a less than helpful person on SO ... Anyway, I killed the left because it didn't seem to be needed if you were only use div() to get the sheet name. Do you need it? And you say slow ... please define? – Scott Holtzman Oct 16 '15 at 20:24
  • oops lol I joke too much XD You can ask @macroman ^_^; I will refrain from going forward. The master book sheets have extra chars that don't match array. Not big deal. I fixed that. Well, array had 7 sheets? Took maybe 30 secs to run. Feels slow. Could be the `resize` perhaps and not the loop? – findwindow Oct 16 '15 at 20:28
  • Also just realized what happens if array has a sheet not in master. Unlikely but possible.... sigh headache -_- – findwindow Oct 16 '15 at 20:29
  • 1
    it could be the column resizing ... You can test that by commenting out that code and seeing how long it takes to run without those lines. – Scott Holtzman Oct 16 '15 at 20:30
  • lolololol instantaneously. So what's better than resize.... I asked [this](http://stackoverflow.com/questions/33153910/set-variable-columns-between-worksheets) yesterday XD Edit: well, that's another question. Thank you for the help ^_^ – findwindow Oct 16 '15 at 20:31
  • 1
    i'll look into that question for you and see if I can help. – Scott Holtzman Oct 16 '15 at 20:39
  • first thing is that pasting entire columns can be **very** slow and is usually not necessary (because there are usually many blank rows at the bottom - i.e - are you really using all 1,048,576 rows? ... If you can find the last used row of data you can limit your statement to only set the values for the columns and rows you actually need. If you want help with this, post another question to make it easier to work with. – Scott Holtzman Oct 16 '15 at 20:43
  • yes, I meant to say setting, not pasting ... either way, working with entire column can be **very** slow, as I mentioned. reducing it to only the rows you need will most likely speed it up to ~instantaneous. – Scott Holtzman Oct 16 '15 at 20:54
  • Please see [here](http://stackoverflow.com/questions/33179390/quicker-alternative-to-resize) – findwindow Oct 16 '15 at 20:55
  • @findwindow: you deleted your question related to re-sizing and I was going to provide some details - I'll place the info in a new answer here, just as a reference – paul bica Oct 16 '15 at 23:03
1

Related to the re-sizing code:

This statement ws.Columns(1).Resize(, 2) translates to "2 million+ rows from column 1 and 2"

The solution you found works well but it's not dynamic (hard-coded last row)

This is how I'd setup the copy of columns:

Option Explicit

Public Sub copyCols()
    Dim ws1 As Worksheet, ws2 As Worksheet, rng1 As Range, rng2 As Range
    Dim cols As Long, lr As Long

    Dim col1 As Long    'renamed from "Start" (VBA keyword - property)
    Dim lbl As Long     'renamed from "label" (VBA keyword - Control object)

    Set ws1 = Sheet1    'ws
    Set ws2 = Sheet2    'book.Worksheets(wsName & "-F")

    col1 = 1
    cols = 2
    lbl = 1

    lr = ws2.Cells(ws2.UsedRange.Row + ws2.UsedRange.Rows.Count, "A").End(xlUp).Row

    Set rng1 = ws1.Range(ws1.Cells(1, col1), ws1.Cells(lr, col1 + 1))
    Set rng2 = ws2.Range("A1:B" & lr)

    rng1.Value2 = rng2.Value2

    Set rng1 = ws1.Range(ws1.Cells(1, col1 + lbl), ws1.Cells(lr, col1 + lbl + cols))
    Set rng2 = ws2.Range(ws2.Cells(1, col1 + lbl), ws2.Cells(lr, col1 + lbl + cols))

    rng1.Value2 = rng2.Value2 
End Sub
paul bica
  • 10,557
  • 4
  • 23
  • 42