2

Looking for an easy way to add a certain number to each row in a column. Something like range("b1:b9")=range("a1:a9")+1

From this:

enter image description here

to this:

enter image description here

JackeyOL
  • 313
  • 2
  • 16
  • Just a note on performance, using the Built-In functions such as `Selection.PasteSpecial` are fast for small datasets, if you go over say 100k rows then reconsider the performance of a For-Loop. – Jeremy Thompson Mar 22 '21 at 04:08
  • Do you mean that for loops are fast for large dataset? That's actually the real reason I have this problem. Looking for solution that is most time-efficient. I feel that if i can do something like `range("b1:b9")=range("a1:a9")+1`, it will be the fastest. – JackeyOL Mar 22 '21 at 04:37
  • See the answers here to understand what I really meant by the above comment: *Using Excels inbuilt C++ is the fastest way with smaller datasets, using the dictionary is faster for larger datasets* https://stackoverflow.com/questions/36044556/quicker-way-to-get-all-unique-values-of-a-column-in-vba – Jeremy Thompson Mar 22 '21 at 05:13

5 Answers5

2

You could use Evaluate, seems quite quick.

Sub Add1()

    With Range("A1:A10000")
        .Value = Evaluate(.Address & "+1")
    End With

End Sub
norie
  • 9,609
  • 2
  • 11
  • 18
1

Looking for a "time-efficient" solution and avoiding loops are not the same thing.

If you were to loop over the range itself, then yes, it would be slow. Copying the range data to a Variant array, looping that, then copying the result back to the range is fast.

Here is a demo

Sub Demo()
    Dim rng As Range
    Dim dat As Variant
    Dim i As Long
    Dim t1 As Single
    
    t1 = Timer() '  just for reportingh the run time
    
' Get a reference to your range by whatever means you choose.  
' Here I'm specifying 1,000,000 rows as a demo
    Set rng = Range("A1:A1000000")
    dat = rng.Value2
    For i = 1 To UBound(dat, 1)
        dat(i, 1) = dat(i, 1) + 1
    Next
    rng.Value2 = dat
    
    Debug.Print "Added 1 to " & UBound(dat, 1) & " rows in " & Timer() - t1; " seconds"
End Sub

On my hardware, this runs in about 1.3 seconds

FYI, the PasteSpecial, Add technique is faster still

chris neilsen
  • 52,446
  • 10
  • 84
  • 123
  • Thank you for the brilliant answer. I ran it and it was indeed fast. I'm not so familiar with VBA arrays could you please explain what is this code `dat = rng.Value2` doing? I'm confused that why we can get the value of a range – JackeyOL Mar 23 '21 at 01:36
  • `rng.Value2` returns the `Value2` property of the `rng` object, which is a 2D array, of the same size as the range, containing the values of the cells in the range. The reason it's faster is because each operation that accesses a sheet has a time overhead. Referencing `rng.Values2` is one operation, looping a range is many operations. – chris neilsen Mar 23 '21 at 11:03
  • is the datatype of `dat` created using range.value/value2 is the same as `Dim Array(1 To row, 1 To col)`? – JackeyOL Mar 23 '21 at 14:13
  • Data type of `dat` is `Variant`. If `rng` is more than 1 cell, `Value2` returns a Variant containing a 2D array the same size as rng, where each element is a Variant. – chris neilsen Mar 23 '21 at 19:56
  • is there a difference if I just create the array using `dat=Range("A1:A1000000").value2` – JackeyOL Mar 24 '21 at 03:50
  • That will get the data into `dat` just fine. But you won't have a range variable to easily return the result to (`rng.Value2 = dat` in my example) – chris neilsen Mar 24 '21 at 09:31
  • oh yes, you are exactly right. I forgot that we will output the value from the array back to the range. – JackeyOL Mar 25 '21 at 22:41
0

Start the macro recorder.

  • type a 1 into an empty cell
  • copy that cell
  • select the cells that you want to add that value to
  • open the Paste Special dialog
  • select "Add" and OK

Stop the macro recorder. Use that code as is or work it into your other code.

Range("C1").Value = 1
Range("C1").Select
Application.CutCopyMode = False
Selection.Copy
Range("A1:A5").Select
Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlAdd, SkipBlanks:= False, Transpose:=False
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
teylyn
  • 34,374
  • 4
  • 53
  • 73
0

Increase Range Values

  • In the following examples, the Add solution took 2.4s while the Array solution took 8.7s (on my machine) to process 5 columns.
  • In the Array solution, the selection is never changed, it just writes the result to the range.
  • The Add solution is kind of mimicking this behavior, by setting all selections as they initially were. Hence the complications.
Option Explicit

' Add Solution

Sub increaseRangeValuesTEST()
    increaseRangeValues Sheet1.Range("A:E"), 1 ' 2.4s
End Sub

Sub increaseRangeValues( _
        ByVal rg As Range, _
        ByVal Addend As Double)
    
    Application.ScreenUpdating = False
    
    Dim isNotAW As Boolean: isNotAW = Not rg.Worksheet.Parent Is ActiveWorkbook
    Dim iwb As Workbook
    If isNotAW Then Set iwb = ActiveWorkbook: rg.Worksheet.Parent.Activate
    
    Dim isNotAS As Boolean: isNotAS = Not rg.Worksheet Is ActiveSheet
    Dim iws As Worksheet
    If isNotAS Then Set iws = ActiveSheet: rg.Worksheet.Activate
    
    Dim cSel As Variant: Set cSel = Selection
    Dim aCell As Range: Set aCell = ActiveCell
    Dim sCell As Range: Set sCell = rg.Cells(rg.Rows.Count, rg.Columns.Count)
    Dim sValue As Double: sValue = sCell.Value + Addend
    
    sCell.Value = Addend
    sCell.Copy
    
    rg.PasteSpecial xlPasteAll, xlPasteSpecialOperationAdd ' 95%
    Application.CutCopyMode = False
    sCell.Value = sValue
    aCell.Activate
    cSel.Select
    
    If isNotAS Then iws.Activate
    If isNotAW Then iwb.Activate
    
    Application.ScreenUpdating = True

End Sub


' Array Solution    

Sub increaseRangeValuesArrayTEST()
    increaseRangeValuesArray Sheet1.Range("A:E"), 1 ' 8.7s
End Sub

Sub increaseRangeValuesArray( _
        ByVal rg As Range, _
        ByVal Addend As Double)
    
    With rg
        Dim rCount As Long: rCount = .Rows.Count
        Dim cCount As Long: cCount = .Columns.Count
        Dim Data As Variant
        If rCount > 1 Or cCount > 1 Then
            Data = .Value
        Else
            ReDim Data(1 To 1, 1 To 1): Data = .Value
        End If

        Dim r As Long, c As Long
        For r = 1 To rCount
            For c = 1 To cCount
                Data(r, c) = Data(r, c) + Addend
            Next c
        Next r
        .Value = Data ' 80%
    End With

End Sub
VBasic2008
  • 44,888
  • 5
  • 17
  • 28
0

#1. Disable your autocalculations

Application.Calculation = xlCalculationManual

#2. Disable your screenupdating

Application.ScreenUpdating = False

#3. As long as your row entries aren't more than ~56000, but your dataset is substantial then its quicker to read into an array, do the manipulations in an array, then output that array in one go.

array1 = Range(cells(3,2), cells(12,2)).value

for i = 1 to ubound(array1, 1)
    array1(i, 1) = array(i, 1) + 1
next i

range(cells(3,10), cells(12,10)) = array1

Note that array1 will be 2D, and you'll be addressing (1,1) through to (10,1) in the example above

Then after pasting back in, reenable your autocalcs, THEN your screenupdate

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Amiga500
  • 1,258
  • 1
  • 6
  • 11