How to find if one cell is not equal to the cell to the left? - excel

I am trying to create a VBA function that loops through each cell in a range, checking if it is equal or not to the cell to the left of it, and if it is a certain color. If it's not equal to the left cell and is that certain color, it adds a number in the same row but a different column to a running sum.
For whatever reason, the condition of the left cell being equal to the current cell is not working: it will still include cells that are the same value as the cell to the left. How do I fix this?
Sub TestFormulas()
Dim x As Long
x = SumRenewed(Range("E2:E9000"))
MsgBox (x)
End Sub
' This function checks cell color and adds it to a sum if it is a certain color.
' It also checks to see if the cell is the same as what's to the left of it. If it is the same, it gets omitted.
' This prevents unnecessary older irrelevant month from being included.
Function SumRenewed(rRng As Range)
Dim lngSum As Long
Dim intIndex As Integer
Dim lngSomething As Variant
For Each cl In rRng
intIndex = cl.Interior.ColorIndex
If cl <> Left(cl, 1) And cl.Interior.ColorIndex = 43 Then '43 is the color index for light green
lngSomething = CLng(Cells(cl.Row, 2))
MsgBox (lngSomething)
lngSum = WorksheetFunction.Sum(lngSomething, lngSum)
lngSomething = CVar(lngSomething)
End If
Next cl
SumRenewed = lngSum
End Function
I have tried numerous workarounds for offsets, assigning Left(cl, 1) to a variable and changing the data type, and Googled every which way I can think for 2.5 days.

Sum Up Column If Matching Criteria (Incl. ColorIndex)
In VBA
Sub TestFormulas()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range
Set rg = ws.Range("E2", ws.Cells(ws.Rows.Count, "E").End(xlUp))
Dim MySum As Double
MySum = SumRenewed(rg, "D", "B", 43)
MsgBox MySum
End Sub
The Function
Function SumRenewed( _
ByVal SingleColumnRange As Range, _
ByVal CompareColumnID As Variant, _
ByVal SumColumnID As Variant, _
ByVal SingleColumnColorIndex As Long) _
As Double
Application.Volatile
Dim lrg As Range: Set lrg = SingleColumnRange.Columns(1)
Dim crg As Range: Set crg = lrg.EntireRow.Columns(CompareColumnID)
Dim srg As Range: Set srg = lrg.EntireRow.Columns(SumColumnID)
'Debug.Print lrg.Address, crg.Address, srg.Address
Dim lCell As Range ' Lookup cell
Dim r As Long ' Range Row
Dim lString As String ' Lookup String
Dim cString As String ' Compare String
Dim sValue As Variant ' Sum Value
Dim Total As Double ' Total Sum
For Each lCell In lrg.Cells
r = r + 1
lString = CStr(lCell.Value)
cString = CStr(crg.Cells(r).Value)
If StrComp(lString, cString, vbTextCompare) <> 0 Then ' not equal
If lCell.Interior.ColorIndex = SingleColumnColorIndex Then
sValue = srg.Cells(r).Value
'Debug.Print r, lString, cString, sValue
If VarType(sValue) = vbDouble Then ' is a number
Total = Total + sValue
End If
End If
End If
Next lCell
SumRenewed = Total
End Function
In Excel (not recommended)
Note that it will update on each calculation due to Application.Volatile. It will never update if the color has changed. Hence it is practically useless in Excel.
=SumRenewed(E2:E21,"D","B",43)

Related

Function to check for specific value in a range of cells and output 'TRUE' in a helper column

I'm trying to check a range of cells for the value "X" and when the column name where the "X" was found is among an array I have previously specified, I want to have a helper column that would say TRUE otherwise say FALSE.
To illustrate, here's a sample table:
In my sample, I have this array that contains 3 values ( Math, English and History). If there is an X in any of the rows whose header name is in the array, I want the helper column to say TRUE otherwise FALSE. It doesn't have to be all of the values in the array, it can be at least only one.
Here is my code (my original file has more columns than my sample, so my code is liek this)
Sub add_helper()
' Adding helper column
Dim checking As Variant
checking = check_issue() -- this is another function, basically checking will contain the values I want to check in this case Math, English and History, i have confirmed this gets it successfully
Dim wks As Worksheet
Set wks = ActiveSheet
Dim rowRange As Range
Dim colRange As Range
Dim LastCol As Long
Dim LastRow As Long
LastRow = wks.Cells(wks.Rows.Count, "I").End(xlUp).row
Set rowRange = wks.Range("I2:AD" & LastRow)
Set colRange = wks.Range("I1:AD1")
'Loop through each row
For Each rrow In rowRange
Do
For Each cell In colRange
'Do something to each cell
If InStr(checking, cell.value) > 0 Then
If Cells(rrow.row, rrow.Column).value <> "" Then
wks.Range("AI" & rrow.row).value = "TRUE"
Exit For
Else
wks.Range("AI" & rrow.row).value = "FALSE"
End If
End If
Next cell
Loop Until wks.Range("AI" & rrow.row).value <> "TRUE"
Next rrow
End Sub
My code results to just having an input of true whenever there is an X without actually checking if the header column is in my array.
Did you try normal formulas in Excel? You could create a table (a ListObject) with the courses as your array values and the combine SUMPRODUCT with COUNTIF to output True/False in your helper column. Easy to update and adapt:
Notice the table at most right named T_COURSES. The formula in helper column is:
=SUMPRODUCT(--(COUNTIF(T_COURSES,$B$1:$E$1)>0)*--(B2:E2="x"))>0
It works perfectly and it autoupdates changing values:
Match Headers of Matches Against Values in Array
Option Explicit
Sub AddHelper()
Dim checking As Variant: checking = check_issue()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim hrg As Range: Set hrg = ws.Range("I1:AD1") ' Header Range
Dim drg As Range ' Data Range
Set drg = ws.Range("I2:AD" & ws.Cells(ws.Rows.Count, "I").End(xlUp).Row)
Dim crg As Range: Set crg = drg.EntireRow.Columns("AI") ' (Helper) Column Range
crg.Value = False
Dim rrg As Range, rCell As Range, r As Long, c As Long, IsFound As Boolean
For Each rrg In drg.Rows
r = r + 1 ' for the (helper) column range
c = 0 ' for the header range
For Each rCell In rrg.Cells
c = c + 1
If StrComp(CStr(rCell.Value), "x", vbTextCompare) = 0 Then
If IsNumeric(Application.Match(CStr(hrg.Cells(c)), checking, 0)) _
Then IsFound = True: Exit For
End If
Next rCell
If IsFound Then crg.Cells(r).Value = True: IsFound = False
Next rrg
End Sub

Incompatibility type when using range

I'm trying to to run a command if these arguments checks , but it's giving me incompatibily type on that block, what am I doing wrong?
Dim rn as range
For Each rg In Columns("X")
If rg.Value Like "?*#?*.?*" And _
LCase(Cells(rg.Row, "U").Value) = "Demande de création d'intervention" _
And LCase(Cells(rg.Row, "V").Value) <> "envoyé" Then
Comparing Strings
If you loop through the cells of the whole column, it will take forever. Calculate the last row, the row of the last non-empty cell in the column, instead.
LCase(Something) will never be equal to Demande.... Use demande... instead.
If you use CStr to convert a value to a string, you won't have to worry about the value being incompatible when comparing it to another string.
The Code
Option Explicit
Sub Test()
Const FirstRow As Long = 2
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, "X").End(xlUp).Row
' If LastRow < FirstRow Then
' MsgBox "No data in column.", vbCritical
' Exit Sub
' End If
' Reference the column range ('rg').
Dim rg As Range
Set rg = ws.Range(ws.Cells(FirstRow, "X"), ws.Cells(LastRow, "X"))
' Reference the other column ranges ('rg2', 'rg3')
Dim rg2 As Range: Set rg2 = rg.EntireRow.Columns("U")
Dim rg3 As Range: Set rg3 = rg.EntireRow.Columns("V")
' Note that all three ranges are of the same size.
' Declare additional variables to be use in the loop.
Dim Cell As Range ' Current cell in the first range
Dim cString As String ' String Representation of the Current Cell's Value
Dim r As Long ' Index of the Current Cell
' Use 'CStr' to convert the values to strings to avoid an error occurring
' if the cell contains an error value.
For Each Cell In rg.Cells ' note '.Cells'
r = r + 1 ' count the cells (in this case rows)
cString = CStr(Cell.Value)
If cString Like "?*#?*.?*" _
And LCase(CStr(rg2.Cells(r).Value)) _
= "demande de création d'intervention" _
And LCase(CStr(rg3.Cells(r).Value)) <> "envoyé" Then
' Do your thing, e.g.
Debug.Print r, cString
End If
Next Cell
End Sub
Results in the Immediate window (Ctrl+G).
8 FY#I.NV
11 MF#X.UT
14 EU#X.IF

Excel - Test if two consecutive digits in the range's address are the same

I want to offset a range if the numerical part of a range's address can be divided by 11.
(A11, A22, A33, and so forth).
Take a range in a given sheet, for example Range("A2").
Could I do ...
Dim isRng as Range
Dim rngAddress as String
Dim tstAddress as Integer, nsnAddress as Integer
isRng = Range("A2")
isRng.Select
rngAddress = isRng.Address
Currently, rngAddress = $A$2 (I think). So then, could I ...
tstAddress = Right(rngAddress, 2)
nsnAddress = Right(tstAddress, 1)
If tstAddress / nsnAddress = 11 Then
'whatever code
Set isRng = ActiveCell.Offset(4,0).Select
Else
End If
I want it to skip down 4 rows after hitting any range like A11 or A22.
Would this work? Is there a better way of doing this? I really appreciate the help.
This should do the trick...
Sub sully_was_here()
Dim r As Range
Set r = [a22]
With r
.Select
If .Row Mod 11 = 0 Then
'whatever code here
.Offset(4).Select
End If
End With
End Sub
Divisible: Using Mod
If cCell.Row Mod 11 = 0 Then
Option Explicit
Sub Divisible()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range: Set rg = ws.Range("A2:A33")
Dim cCell As Range
Dim r As Long
For Each cCell In rg.Cells
r = r + 1
If cCell.Row Mod 11 = 0 Then
Debug.Print r, cCell.Offset(4).Address, "***"
Else
Debug.Print r, cCell.Address
End If
Next cCell
End Sub

Adding/removing rows from a table depending on true or false cell

I am trying to add/remove rows to a table in excel automatically dependent on the value of a cell in another table on a different sheet.
For example;
On a seperate sheet I have one table with the following. This table contains all projects regardless of project outcome.
And then on another sheet I have a table containing all the entries that have been deemed a success.
I am trying to collate all projects that have been identified as a success to the above table. However, If I was to change Project1 to a Fail on the first table Project1 must be removed from the bottom table.
I have tried if statements but I can't seem to get the logic right. Would this have to be achieved through the use of a macro?
Any help would be greatly appreciated.
A VBA Solution
The code runs automatically, you don't have to run anything. The code
will run when you change the criteria values (Success, Fail). Keep in mind
that the criteria is case sensitive.
Copy the following code into the sheet code of the source sheet
e.g. Sheet1 and carefully adjust the 5 constants to fit your needs.
Sheet Code
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const FirstRow As Long = 2 ' Source/Target First Row Number
Const Cols As String = "A:G" ' Source/Target Columns Range Address
Const CritCol As Long = 4 ' Criteria Column
' Note: If CritCol = n then it presents the n-th column of Columns Range,
' and not the n-th column of the worksheet.
Const Criteria = "Success" ' Criteria
Const TargetName = "Sheet2" ' Target Worksheet Name
Dim SourceColumns As Range
Set SourceColumns = Me.Columns(Cols)
Dim CriteriaColumn As Long
CriteriaColumn = getNthColumn(Me, SourceColumns.Address, CritCol)
If CriteriaColumn = 0 Then Exit Sub
If Intersect(Me.Columns(CriteriaColumn), Target) Is Nothing Then Exit Sub
Dim CriteriaRange As Range
Set CriteriaRange = getColumnRange(Me, CriteriaColumn, FirstRow)
If Not Intersect(CriteriaRange, Target) Is Nothing Then
Dim TargetSheet As Worksheet
Set TargetSheet = ThisWorkbook.Worksheets(TargetName)
transferData SourceColumns, CriteriaRange, CritCol, Criteria, _
FirstRow, TargetSheet
End If
End Sub
Copy the following code into a standard module e.g. Module1.
Nothing to change here.
Module Code
Option Explicit
Function getColumnRange(Sheet As Worksheet, _
ByVal ColumnNumberOrLetter As Variant, _
Optional ByVal FirstRow As Long = 1) As Range
Dim rng As Range
Set rng = Sheet.Columns(ColumnNumberOrLetter) _
.Find("*", , xlFormulas, , , xlPrevious)
If rng Is Nothing Then Exit Function ' No data in whole column.
If rng.Row < FirstRow Then Exit Function ' No data in and below first cell.
Set getColumnRange = Sheet.Range(Sheet.Cells(FirstRow, rng.Column), rng)
End Function
Function getNthColumn(Sheet As Worksheet, ByVal RangeAddress As String, _
Optional ByVal NthColumn As Long = 1) As Long
Dim rng As Range
Set rng = Sheet.Columns(RangeAddress)
If rng Is Nothing Then Exit Function
If rng.Columns.Count < NthColumn Then Exit Function
getNthColumn = rng.Column + NthColumn - 1
End Function
Sub transferData(SourceColumns As Range, CriteriaColumnRange As Range, _
CriteriaColumn As Long, Criteria As Variant, FirstRow As Long, _
TargetSheet As Worksheet)
Dim NoR As Long
NoR = Application.WorksheetFunction.CountIf(CriteriaColumnRange, Criteria)
Dim Source As Variant
Source = Intersect(SourceColumns, CriteriaColumnRange.Rows.EntireRow)
Dim Target As Variant
Dim i As Long, j As Long, k As Long
ReDim Target(1 To NoR, 1 To UBound(Source, 2))
For i = 1 To UBound(Source)
If Source(i, CriteriaColumn) = Criteria Then
k = k + 1
For j = 1 To UBound(Source, 2)
Target(k, j) = Source(i, j)
Next j
End If
Next i
Erase Source
With TargetSheet
.Range(SourceColumns.Rows(FirstRow).Address).Resize( _
.Rows.Count - FirstRow + 1).ClearContents
.Range(SourceColumns.Rows(FirstRow).Address).Resize(k) = Target
End With
End Sub

How to transpose different sized rows into one column

I'm pretty new to Excel VBA and I am currently trying to take data from multiple rows and transpose it into a single column. I know where the first cell of the data will begin, but that's all I know. Each row of data is a different sized row, and there can be a varying number of columns also.
So my current method is using a sort of transpose where I just select a very large range (in hopes that it captures all my data) and then transposing it. It does work, albeit pretty slow, and it also includes all the blanks in my range also.
Sub transpose()
Dim InputRange As Range
Dim OutputCell As Range
Set InputRange = Sheets("Sheet1").Range("P1:AC100")
'output will begin at this cell and continue down.
Set OutputCell = Sheets("Sheet1").Range("A1")
For Each cll In InputRange
OutputCell.Value = cll.Value
Set OutputCell = OutputCell.Offset(1, 0)
Next
End Sub
The current method isn't the worst, but I'm sure there are better methods that are quicker and ignore blanks. I'm not sure if an actual transpose is the best way, or perhaps using some sort of loop method. The data is usually contained within 200 rows, and 10 columns if that helps in deciding a method (maybe looping might be quick enough). Any help would be appreciated!
Edit
I have found a method of ignoring the blanks:
For Each cll In InputRange
If Not IsEmpty(cll.Value) Then
OutputCell.Value = cll.Value
Set OutputCell = OutputCell.Offset(1, 0)
End If
Next
This 'snake' method works fine for me.
Sub Snake()
Dim N As Long, i As Long, K As Long, j As Long
Dim sh1 As Worksheet, sh2 As Worksheet
K = 1
Set sh1 = Sheets("Sheet1")
Set sh2 = Sheets("Sheet2")
N = sh1.Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To N
For j = 1 To Columns.Count
If sh1.Cells(i, j) <> "" Then
sh2.Cells(K, 1).Value = sh1.Cells(i, j).Value
K = K + 1
Else
Exit For
End If
Next j
Next i
End Sub
Before:
After:
One thing you could do is instead of looping the entire range just loop the SpecialCells.
Depending on what the content is of your inputRange then you can choose which XlCellType to use.
If it is just hardcoded values then xlCellTypeConstants would work fine for you.
Alternatively, you might be looking at formulas, in which case you would want to use xlCellTypeFormulas.You can also do a Union if you need both.
Here is an example using just xlCellTypeConstants
Sub transposes()
' Example just for hardcoded data
Dim inputRange As Range
Set inputRange = Sheets("Sheet1").Range("P1:AC100").SpecialCells(xlCellTypeConstants)
Dim outputCell As Range
Set outputCell = Sheets("Sheet1").Range("A1")
Dim cell As Range
For Each cell In inputRange
Dim offset As Long
outputCell.offset(offset).Value = cell.Value
offset = offset + 1
Next cell
End Sub
Option Explicit
Public Sub Range_2_Column_Skip_VbNUllString()
' Test Covered
'
Range_2_Column Cells(1, 1).CurrentRegion, _
Cells(1, 5), vbNullString
End Sub
Public Function Range_2_Column( _
ByVal r_Sour As Range, _
cell_Dest As Range, _
ByVal sKip As String)
' Test Covered
A2_2_Range A2_From_Coll( _
Coll_From_A2_Skip( _
A2_From_Range(r_Sour), sKip)), cell_Dest
End Function
Public Sub A2_2_Range( _
a2() As Variant, _
cell As Range)
' Test Covered
cell.Resize( _
UBound(a2), UBound(a2, 2)).Value = _
a2
End Sub
Public Function A2_From_Range( _
ByVal r As Range) _
As Variant()
' Test Covered
'
A2_From_Range = r.Value
End Function
Public Function Coll_From_A2_Skip( _
a2() As Variant, _
ByVal sKip As String) _
As Collection
' Test Covered
'
Dim coll As New Collection
Dim v As Variant
For Each v In a2
If v <> sKip Then
coll.Add v
End If
Next
Set Coll_From_A2_Skip = coll
End Function
Public Function A2_From_Coll( _
ByVal coll As Collection) _
As Variant()
' Test Covered
'
ReDim a2(1 To coll.Count, 1 To 1) As Variant
Dim v As Variant
Dim iCount As Long
iCount = 1
For Each v In coll
a2(iCount, 1) = v
iCount = iCount + 1
Next
A2_From_Coll = a2
End Function

Resources