tags:

views:

824

answers:

6

I have a loop that runs through each column and sets the value to an R1C1 sum of a few rows above it. I'm using a for loop, but I want to skip a few columns, as they contain formulae already in the cell. How can I set up a loop that only cycles through a non-contiguous set or numbers?

For reference, I want it to cycle through columns 1 to 80, but skip cols 25, 36, 37, 44, 60, 63, 64, 67, 68, 73, 75 and 76.

Edit: thanks guys, but I;ve already got it working as you described; I was looking for a shorter and more elegant method.

Edit 2: Is that even VBA?!

+1  A: 

I'd do one of two things. The first is to only execute the body for specific loop numbers:

for i = 1 to 70
    skipIt = false
    skipIt = skipIt or (i = 25)
    : : :
    skipIt = skipIt or (i = 76)
    if not skipIt then
        ' Put your body here. '
    end if
next

Or, you can jump straight to the next:

for i = 1 to 70
    if i = 25 goto nextIter
    : : :
    if i = 76 goto nextIter

    ' Put your body here. '
nextIter:
next
paxdiablo
Are you sure you want to recommend GoTo? Sure, it works, but...
Tomalak
I'm not recommending it, just saying it's a possibility. And GOTO is oft-maligned for no good reason. It's the overuse which leads to spaghetti code that's evil. A single goto target such as the second example is no different from locating the end-if of an if.
paxdiablo
+1  A: 

you could put an if in the beginning of your loop, and skip the columns you don't want to iterate through

Brann
+1  A: 
  for i = 1 to 70
  {
    switch (i)
    {
      case 25, 36, 37, 44, 60, 63, 64, 67, 68, 73, 75 76 : break;
      default : // process break;
    }
  }
Learning
I believe this is not VBA.
Tomalak
true ... i just thought of the flow and forgot the language :(
Learning
You were not too far off, though. ;-)
Tomalak
+2  A: 

A VBA version of Learning's C# answer:-

Dim col As Integer: For col = 1 To 70

    Select Case col

    Case 25, 36, 37, 44, 60, 63, 64, 67, 68, 73, 75, 76
        'do nothing'

    Case Else
        'do something'

    End Select

Next col
AdamRalph
I find an array where you put the collumnnumbers easier to maintain, and better codestyle. A codepath that does nothing is not really good.
Ikke
@Ikke: You can always post your own answer. ;-) Changing the code to be inclusion based, instead of exclusion based is trivial. A code path that *actively* does nothing is not a bad thing, IMHO. Doing nothing implicitly (like a missing else clause) is worse.
Tomalak
I think the "active nothing" is okay; I'd like to see a comment there saying why, such as "Skip these cases as those cells will be empty" but even "do nothing" implies intent (so you know the empty case is not a bug).
paxdiablo
+1  A: 

You can set up an array containing the column numbers that you need to process.

Your loop can then loop over the array values.

Another solution is to define a boolean array with a value for every column and mark the columns that you want to skip. In the loop over all the columns check the boolean array.

Yet another solution is if you can detect in the loop whether the column is a column that you need to skip (by detecting the formula that is there).

Renze de Waal
+1 for array. Easiest way to maintain.
Ikke
@Renze de Waal: Arrays are a pain to use in VBA. Your proposal (your loop can then loop...) would be O(n²) for no good reason. Pre-constructing an array with booleans would require you to know all values in advance, not only those you want to exclude, or you create an index-out-of-bounds situation.
Tomalak
Out of interest, how would I easily set up an array with my column numbers? Without having cols = Array(1,2,3,4 ... 23, 26, 27, ...) etc, obviously. Is there a way to have it accept Array(1:23, 26:35 ...) etc?
Chris Gunner
@Tomalak, it's not O(n^2) at all, @Renze is proposing an array containing the values [1..24,26..35,38..43,45..59,61,62,65,66,69..70] and iterating over that array - there's no loop within a loop that would imply O(n^2).
paxdiablo
@Chris: Sorry, I don't know of a easy way to set this up, except listing them.
Renze de Waal
@Pax: That is exactly what I meant.
Renze de Waal
@Pax: To cite the OP: "I want it to cycle through columns" (the outer loop), this answer: "your loop can then loop" (the inner loop). Okay, it is less than O(n²). But still it's neither elegant nor necessary.
Tomalak
@Tomalak: No, there is no inner loop. There is only one loop, which loops over the members of the array. By writing "your loop can loop over ..." I intended to describe what the loop does, not an inner loop. Did I mention that there is only one loop?
Renze de Waal
A: 

This approach uses Excel's range object functionality.

Define your range using Excel's UNION method to be a range of non-contiguous of columns.

To speed up the loop, you can reduce the range to include only those cells which contain formulas using the SpecialCells method of the range object.

Function LoopColumns() Dim Target As Range Dim Cell As Range

' Create a range object based on the Union of the columns you want Set Target = Union(Range("A:D"), Range("F:G"), Range("J:L"), Range("P:Q"))

' If you only want to process formulas, reduce the range again but having ' Excel define the range as only those cells which contain a formula Set Target = Target.SpecialCells(xlCellTypeFormulas, 1)

For Each Cell In Target.Cells
    'process cells here...
Next Cell

End Function