简体   繁体   English

如何将公式应用于列 vba 范围内的每个单元格?

[英]How to apply formula to each cell in column vba range?

I have an excel file with a column full of numbers (occasionally there will be some text or a blank cell) for example例如,我有一个 excel 文件,其中有一列充满数字(偶尔会有一些文本或空白单元格)

4
5
10
13
5

4
not applicable

9
2
1
6

I want to apply a function to that cell that does the following.我想将 function 应用于执行以下操作的单元格。 If the cell value is blank or text do nothing.如果单元格值为空白或文本不执行任何操作。 If the value is less that 8 then make it 8, if it is greater than or equal to 8 do nothing.如果该值小于 8,则将其设为 8,如果大于或等于 8,则什么都不做。 So my example column would become:所以我的示例列将变为:

8
8
10
13
8

8
not applicable

9
8
8
8

I wish to overwrite the values in the cell with the new values as opposed to preserving the original values that were in the cell.我希望用新值覆盖单元格中的值,而不是保留单元格中的原始值。

Of course I could do =max(cell, 8) and then just copy the output down the column and the copy/paste values over the top of the column but I need to do this for multiple columns on a sheet (100's of times) so I need a good way to do this.当然,我可以做=max(cell, 8)然后只需将 output 复制到列下方并将值复制/粘贴到列顶部,但我需要对工作表上的多个列执行此操作(100 次)所以我需要一个好方法来做到这一点。

So far I have managed to write a macro that iterates through my sheet and selects the columns I need to update but I don't know how to actually update them.到目前为止,我已经设法编写了一个宏来遍历我的工作表并选择我需要更新的列,但我不知道如何实际更新它们。

Can I define a function and then apply it to each cell in a column?我可以定义一个 function 然后将其应用于列中的每个单元格吗? Is there a faster/more efficient way to do this.有没有更快/更有效的方法来做到这一点。

EDIT编辑

Code so far:到目前为止的代码:

Sub updatemin()

Dim i, updatecol As Integer
updatecol = 14


For i = 1 To 100
    Columns(updatecol).Select
    'need to figure out how to make any values less than 8 into 8 for the cells in the given range before moving on to the next column to do the same.
    updatecol = updatecol + 22
Next i

End Sub

Update Columns更新列

Option Explicit

Sub UpdateMin()

    Const FirstCellAddress As String = "N2"
    Const ColumnOffset As Long = 22
    Const ColumnsCount As Long = 100
    Const MinCriteria As Double = 8
    
    Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
    
    Dim fCell As Range: Set fCell = ws.Range(FirstCellAddress)
    
    Dim rg As Range
    Dim lCell As Range
    Dim rCount As Long
    
    With fCell.Resize(ws.Rows.Count - fCell.Row + 1, _
            (ColumnsCount - 1) * ColumnOffset + 1)
        Set lCell = .Find("*", , xlFormulas, , , xlPrevious)
        If lCell Is Nothing Then Exit Sub
        rCount = lCell.Row - .Row + 1
        Set rg = .Resize(rCount, 1)
    End With
    
    Dim Data As Variant
    Dim cValue As Variant
    Dim r As Long
    Dim c As Long
    
    For c = 1 To ColumnsCount
        With rg.Offset(, (c - 1) * ColumnOffset)
            'Debug.Print .Address
            Data = .Value
            For r = 1 To rCount
                cValue = Data(r, 1)
                If VarType(Data(r, 1)) = vbDouble Then
                    If cValue < MinCriteria Then
                        Data(r, 1) = MinCriteria
                    End If
                End If
            Next r
            .Value = Data
            '.Interior.Color = vbYellow
        End With
    Next c
    
    MsgBox "Columns updated.", vbInformation

End Sub

I propose to separate obtaining and processing of data.我建议将数据的获取和处理分开。 As for the latter, why not to apply a formula =IF(Data < Minimum, Minimum, Data) to numbers in a data range?至于后者,为什么不将公式=IF(Data < Minimum, Minimum, Data)应用于数据范围内的数字? To select only numbers, we can use SpecialCells .对于 select 只有数字,我们可以使用SpecialCells

Sub UpdateMin(Data As Range, Optional MinCriteria As Double)
Dim Numbers As Range
Dim Area As Range
Dim Formula As String
    On Error Resume Next
    Set Numbers = Data.SpecialCells(xlCellTypeConstants, xlNumbers)
    If Numbers Is Nothing Then Exit Sub
    On Error GoTo 0
    For Each Area In Numbers.Areas
        ' =IF(Area < MinCriteria, MinCriteria, Area)
        Formula = "IF(" & Area.Address & "<" & MinCriteria & "," & MinCriteria & "," & Area.Address & ")"
        Area.Value2 = Evaluate(Formula)
    Next Area
End Sub

We need to iterate over continuous areas here to calculate IF(...) as an array formula.我们需要在此处迭代连续区域以计算IF(...)作为数组公式。 To get the range of interest in your case I'd use this code:为了获得您的案例的兴趣范围,我将使用以下代码:

Function getData() As Range
Dim Result As Range
Const DataSheet = "Sheet1"
Const first = 14
Const delta = 22
Const last = first + 99 * delta
Dim i&
    ' rebuild to your needs
    With ThisWorkbook.Worksheets(DataSheet)
        Set Result = .Columns(first)
        For i = first + delta To last Step delta
            Set Result = Union(Result, .Columns(i))
        Next i
        Set getData = Intersect(Result, .UsedRange)
    End With
End Function
 

The final part:最后一部分:

Sub main_macro()
    UpdateMin getData, 8
End Sub

I'm not sure if this is a good approach, because we are iterating over data twice - to select numbers and then to update them.我不确定这是否是一个好方法,因为我们对数据进行了两次迭代 - 到 select 个数字,然后更新它们。 But both parts are addressed to Excel itself.但这两个部分都是针对 Excel 本身的。 So the job, I hope, is gonna be done quickly at least in case of big chunks of numbers.所以我希望这项工作至少在大量数字的情况下能够快速完成。 The worst scenario, I think, is a regular alternation of numbers and words.我认为最糟糕的情况是数字和单词的规律交替。 Let me know about your choice and how it worked in the end.让我知道您的选择以及最终效果如何。

Imagine the following data in column A想象一下 A 列中的以下数据

在此处输入图像描述

Use the following code to loop through all data and if it is smaller then 8 turn it into 8, omit the cells if they're not numeric or if they are empty.使用以下代码循环遍历所有数据,如果它小于 8,则将其变为 8,如果单元格不是数字或为空,则忽略这些单元格。

Option Explicit

Public Sub Example()
    Dim ws As Worksheet  ' define your worksheet
    Set ws = ThisWorkbook.Worksheets("Sheet1")

    Dim LastRow As Long  ' find last used row in column A
    LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    ' update data to MinValue in the given DataRange
    UpdateValuesToMinimum MinValue:=8, DataRange:=ws.Range("A1", "A" & LastRow)
End Sub


Public Sub UpdateValuesToMinimum(ByVal MinValue As Long, ByVal DataRange As Range)
    Dim DataValues() As Variant  ' read all data into an array for faster processing
    DataValues = DataRange.Value2
    
    Dim iRow As Long  ' loop through all rows
    For iRow = LBound(DataValues, 1) To UBound(DataValues, 1)
        Dim iCol As Long  ' loop through all columns
        For iCol = LBound(DataValues, 2) To UBound(DataValues, 2)
            ' check if it is numeric and not empty
            If IsNumeric(DataValues(iRow, iCol)) And DataValues(iRow, iCol) <> vbNullString Then
                ' if data is <MinValue set it to MinValue 
                If DataValues(iRow, iCol) < MinValue Then
                    DataValues(iRow, iCol) = MinValue
                End If
            End If
        Next iCol
    Next iRow
    
    ' write array data back to the cells
    DataRange.Value2 = DataValues
End Sub

And you will get as result:你会得到这样的结果:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM