Adding numbers in a column - excel

I have written this code to add numbers in a column. It is not adding the last cell.
For example if there are three numbers 1, 2 and 3 it will sum up 1 and 2 and ignore value in third cell. When there is a fourth number 1, 2, 3 and 4 it adds 1, 2 and 3.
Sub add()
Dim Rng As Range, a As Integer
Set Rng = Range("b2", Range("b2").End(xlDown))
Counter = Rng.Count
a = 0
For i = 2 To Counter
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub

Let's say the Counter = Rng.Count gives 4, and you start your i = 2 (possibly to keep title of your column). Your code will not print 4 digits, because you start loop from 2.
The counter should look like this:
Counter = Rng.Count + 1
and it will work

It is because .End(xlDown) - it works like CTRL + downarrow, and ends on the last not empty, or first not empty cell in the column. That's why when you delete value in specific row it will "break" the range. If you want to scan all rows, no matter if it's empty or not use the loop from the first row, and you will get the sum of the whole column range (starting from row 2 of course):
Sub SumWholeColumn()
'give the sum of all numbers in column B, starting from B2
Dim i, a As Long
Dim column, addr As String
a = 0
column = "b"
For i = 2 To Rows.Count
addr = column & i
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub
If your range is fixed, you can speed up the calculation process by setting the range manually. Otherwise it will scan ALL rows. For example if you know, that your random numbers will not exceed row 1000, then you can use something like this:
Sub SumWholeColumn()
'give the sum of all numbers in column B, starting from B2
Dim i, a As Long
Dim maxRows As Integer
Dim column, addr As String
a = 0
column = "b"
maxRows = 1000
For i = 2 To maxRows
addr = column & i
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub

Well I think that at the first time I did not understood your point then, I thought you would like to paste numbers in column B from 0 to the last row, starting from the B2 address. If so - this will work:
Sub add()
Dim i, a As Long
Dim column As String
Dim addr As String
a = 0
column = "b"
For i = 2 To Rows.Count
addr = column & i
ActiveSheet.Range(addr).Value = a
a = a + 1
Next i
End Sub
but today I realised that your title "Adding numbers in a column via Excel VBA
" is wrong and probably you are trying to achieve something else (because you are trying to give some value in ActiveCell?) and if so, please correct me:
you have actually some numbers in column B, and you would like to give in the ActiveCell the sum of all those numbers? The answer for this will be:
Sub SumAll()
'give the sum of all numbers in column B, starting from B2
Dim Rng As Range
Dim a, i As Long
Set Rng = Range("b2", Range("b2").End(xlDown))
Counter = Rng.Count + 1
a = 0
For i = 2 To Counter
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub
You need to use "a" as Long, because Integer is up to 2147483647 and if you fill all rows in the column, starting from 0 and iterate the number by 1 to the last row, and sum the values it will give you 2147319811 - out of the Integer scope.
i value can be Integer (not Long as in my example), because "i" max value will not exceed the scope (Workbook rows are limited to 1048576). You can safely change i to Integer and save some KB's of memory :)

Related

Split array and compare values to separate columns

Im trying to create a function which scans a column (job-trav-seq) and splits the values in each cell within a given range. It then compares these values to comparable cells in separate columns (so for instance job-trav-seq would have a cell 58546-05-10; this function would remove the dashes and compare the 58546 to job number, 05 to traveller ID and 07 to sequence No.
Basically, the function needs to first takes the A column (JobTravSeq) and breaks it apart into individual variables. (variable 1 should be compared with values in column B, values in variable 2 should be compared with column C and values in variable 3 should be compared with column D)
A loop should go up through the column cells as long as variable 1 = values in column B and variable 2 = values in column C (this is rowStart); this should be stored as a variable
A second loop should occur (rowEnd); which should loop down though the column cells as long as variable 1 = values in column B and variable 2 = values in column C; this should be stored as a variable
The code should then traverse between rowStart and rowEnd and check if variable 3 = values in column D, if it does then place an asterisk (or something similar) in front of the value to mark it as a current task
What im starting with: Sample Doc
What im trying to achieve: SampleDocOutput
any help would be most appreciated
heres my code for reference:
Sub SampleDocOrganise()
Dim i As Integer
Dim LastRow, rowCompare As Long
Dim variArr, rowStart, rowEnd, rangeID As Variant
Dim JobTravSeqRng As Range, jobNoRng As Range, TravellerRng As Range,
opSeqRng As Range, _
rng_JobTravSeq As Range, rng_JobNo As Range, rng_Traveller As Range,
rng_opSeq As Range
Set JobTravSeqRng = Range("A:A")
Set jobNoRng = Range("B:B")
Set TravellerRng = Range("C:C")
Set opSeqRng = Range("D:D")
For Each JobTravSeq In Selection
Str_Array = Split(JobTravSeq, "-")
For h = 0 To UBound(Str_Array)
Range("A:A").Find (Str_Array)
Range.Offset(, h + 1) = Str_Array(h)
For rowStart = 4 To Rows.Count
If Worksheets("Sheet1").Cells(Str_Array, 1).Value = jobNoRng.Value Then
If Cells(Str_Array, 2).Value = jobNoRng.Value Then
Cells.Value = rowStart
End If
End If
Next rowStart
For rowEnd = LastRow To 4 Step -1
If Cells(Str_Array, 1).Value = Range("B:B").Value Then
If Cells(Str_Array, 2).Value = Range("C:C").Value Then
Cells.Value = rowEnd
End If
End If
Next rowEnd
For rowCompare = rowStart To rowEnd
For Each opSeqArr In Str_Array
If Cells(Str_Array, 3).Value = Range("D:D").Value Then
If Cells(Str_Array, 1).Value = Range("B:B") Then
ActiveCell.Characters(0, 0).Insert (" P ")
With ActiveCell.Characters(0, Len(" P ")).Font
.Name = "OpSeq_Equals"
.Bold = True
.Color = -16776961
End With
MsgBox cell.Value = "*" & ""
' if cell changes then go to next loop
Else
' if cell changes then go to next loop
End If
End If
Next
Next
Next h
Next
End Sub
Sub MsgboxTasks() 'should display all rows that contain an asterisk in opSeq (current tasks)
End Sub

Assign a variable to cells to compare mutliple numbers

I have a data set where I need to compare the first number in each transect against each other.
For example, in the below data set I need to compare cells D2, D7, D12 and D17 and assign a value based on which one is the smallest number, the next smallest and so on. This will be used to assign the transect numbers in column A.
My issue is that the number of sections (in this example 4) and the number of transects (also 4 in this example) will vary. So the cells I need to compare will change.
I have written the code that calculates the number of transects, which is:
Dim tlength As Worksheet
Dim tb As Long *'tb=transect break*
Sub tlength_start_stop_coords()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("sheet1")
Set tlength = ThisWorkbook.Worksheets("transect lengths") *' assigns the sheet to a variable
for efficient coding*
tb = 0 *'counter to calculate the number of transects*
j = 2 *'counter for row*
Lastrow = Lastrow + 1 *'add a row to last row so that the last row includes a blank line.*
*'the following for loop is used to calculate the number of transects*
For i = 2 To Lastrow
If tlength.Range("D" & i) = vbNullString Then
If tlength.Range("D" & i - 1) <> vbNullString Then
tb = tb + 1 *'updates the counter*
End If
End If
Next i
tbtotal = tb *'stores the total counter in variable tbtotal for later use*
I think I may need to use a loop. But I am stuck trying to figure out how to manage the unknown number of comparisons in changing cell locations.
The desired result is in the below screenshot of the expected outcome, with results in column A. To begin with, I only need to get the number for the first row of each transect. Once I have that, I can copy using xldown.
Expected outcome:
Another possible data set and outcome expected might be:
enter image description here
with an expected outcome of:
enter image description here
Worked for me using your second set of sample data:
Sub Tester()
Dim tlength As Worksheet, i As Long, tNum As Long, v, vPrev, arr
Dim col As New Collection, e, c As Range, rng As Range
Set tlength = ThisWorkbook.Worksheets("transect lengths")
'collect all the Section 1 Latitudes and row numbers
For i = 2 To tlength.Cells(Rows.Count, "B").End(xlUp).Row
If tlength.Cells(i, "B") = 1 Then
col.Add Array(i, tlength.Cells(i, "D").Value) 'store start row and first Latitude
End If
Next i
SortCollection col, 2 'sort collection by second element in each array
tNum = 0
'loop over the sorted collection and assign the order of the transects
For Each e In col
tNum = tNum + 1
Set c = tlength.Cells(e(0), "B")
'following assumes all transects have at least 2 sections...
tlength.Range(c, c.End(xlDown)).Offset(0, -1).Value = tNum
Next e
End Sub
'Sort a collection of 0-based arrays in ascending order by the n'th element of each array
'Adapted from https://stackoverflow.com/a/3588073/478884
Sub SortCollection(col As Collection, n As Long)
Dim i As Long, j As Long, vTemp As Variant
For i = 1 To col.Count - 1 'Two loops to bubble sort
For j = i + 1 To col.Count
If col(i)(n - 1) < col(j)(n - 1) Then 'change to > for ascending sort
vTemp = col(j) 'store the lesser item
col.Remove j 'remove the lesser item
col.Add Item:=vTemp, before:=i 're-add the lesser item before the greater Item
End If
Next j
Next i
End Sub

How to count a pair of texts in one column that are few rows apart using vba?

In workbook A, I'm trying to count when a text, "Dr" occurs then within 5 rows after it, how many cells are blank or the cell is either a text, "Nr" or "Cr".
In another word, I'm trying to count the numbers of pairs of "DR-blank(within 5 rows after DR)", "DR-NF(within 5 rows after DR)", and "DR-CR(within 5 rows after DR)". The data set looks like this:
Column A 0 1 2 3 4 5 6 7 8
Column B Dr Cr Dr Nr
And then I want to copy the result to workbook B.
I've been tried to use offset:
If Range("B2:B901").Value = "D" Then
'V3 = Application.WorksheetFunction.CountBlank(.Range("B2:B901").Offset(5, 0))
Wb.Worksheets("Sheet1").Cells(Rows.Count, "M").End(xlUp).Offset(1, 0).Value = V3
But I always got a "0" in return, meaning the logic wasn't quite right to capture what I intended to do.
Could someone help with the codes? Really appreciated!
This code will iterate through every cell in the range you provide (in this case B1:B901 in sheet1) and if it contains the vale Dr it will then iterate through the subsequent 5 cells to check if they contain the values you are looking for.
It will output the contents of column A and column B to a new workbook, together with your count of nr, cr and blank in columns c, d and e respectively.
Option Compare Text 'this tells VBA that you want you string comparisons to NOT be
'case sesitive. If you want case to be taken into account, then leave
'this line out.
Sub test()
Dim cll As Range
Dim vCellValue As Variant
Dim iterator As Integer
Dim vCountBlank As Integer
Dim vCountCr As Integer
Dim vCountNr As Integer
Dim wb2 As Workbook
Set wb2 = Workbooks.Add
For Each cll In Sheet1.Range("B2:B901")
vCountBlank = 0
vCountCr = 0
vCountNr = 0
If cll.Value = "Dr" Then
For iterator = 1 To 5
vCellValue = cll.Offset(iterator, 0).Value
If vCellValue = "Nr" Then vCountNr = vCountNr + 1
If vCellValue = "Cr" Then vCountCr = vCountCr + 1
If vCellValue = "" Then vCountBlank = vCountBlank + 1
Next iterator
End If
wb2.Sheets(1).Cells(cll.Row, 1).Value = cll.Offset(0, -1).Value
wb2.Sheets(1).Cells(cll.Row, 2).Value = cll.Value
wb2.Sheets(1).Cells(cll.Row, 3).Value = vCountNr
wb2.Sheets(1).Cells(cll.Row, 4).Value = vCountCr
wb2.Sheets(1).Cells(cll.Row, 5).Value = vCountBlank
Next cll
Set wb2 = Nothing
End Sub

How to convert many columns to one column in excel but keep the 1st cell of each column and repeat it in new rows?

I have and excel document that looks like this:
and i want it to be like:
*comma (,) means that data are in different cells horizontally.
is there any vb macro or an expression to do it?
If all of the Rows have the same number of columns, then you can use INDEX, INT, COUNTA and MOD to break this down.
Column A:
=INDEX(Sheet1!$A$1:$D$2,1+INT((ROW()-1)/(COUNTA(Sheet1!$1:$1)-1)),1)
Column B:
=INDEX(Sheet1!$A$1:$D$2,1+INT((ROW()-1)/(COUNTA(Sheet1!$1:$1)-1)),2+MOD(ROW()-1,COUNTA(Sheet1!$1:$1)-1))
Where Sheet1!$A$1:$D$2 is the 'Input' range, and Sheet1!$1:$1 is any row in that range with a full row of data.
INDEX lets you get a specific row/column of a range. Our Range is Sheet1!$A$1:$D$2, and the Row is the same for both formulae:
1+INT((ROW()-1)/(COUNTA(Sheet1!$1:$1)-1)),
This will be 1 for n rows, 2 for the next n, etc, where n is the number of cells in a row minus the starter column (i.e. how many names per gender)
(INT removes the decimal part of a number, so INT(3/4) is INT(0.75), which is 0. COUNTA just counts the non-blank cells)
The difference between the two is the Column. In column A, we just want the first column, so Column is 1. In column B, we want the xth item after the first column, where x A) counts up by 1 each row and B) resets to 1 when we go from Male to Female (or beyond)
Now, the MOD function lets us do that fairly simply: MOD(0, 3) is 0, MOD(1, 3) is 1, MOD(2, 3) is 2, and MOD(3, 3) is back to 0. We just need to start out row count at 0 (subtract 1 from Row, and add it back outside the MOD) and remove the first column from the items-per-row (subtract 1 from the COUNTA, add 1 outside the MOD)
A straightforward solution would be to use Split
Sub TransferIt()
Const SEP = ","
Dim rg As Range
Dim vdat As Variant
Dim lDat As Variant
Dim i As Long, j As Long
Dim col As Collection
' Assumption data is in column A, adjust accordingly
Set rg = Range("A1:A4")
vdat = WorksheetFunction.Transpose(rg)
Set col = New Collection
For i = LBound(vdat) To UBound(vdat)
lDat = Split(vdat(i), SEP)
For j = LBound(lDat) + 1 To UBound(lDat)
' first field always contains female or male
col.Add lDat(LBound(lDat)) & SEP & lDat(j)
Next j
Next i
vdat = collectionToArray(col)
' Write data into column B
Range("B1").Resize(UBound(vdat) + 1) = WorksheetFunction.Transpose(vdat)
End Sub
' Source: http://www.iwebthereforeiam.com/iwebthereforeiam/2004/06/excel-vba-code-to-convert-coll.html
Function collectionToArray(c As Collection) As Variant()
Dim a() As Variant: ReDim a(0 To c.Count - 1)
Dim i As Integer
For i = 1 To c.Count
a(i - 1) = c.Item(i)
Next
collectionToArray = a
End Function
Before:
After:
Code:
Sub settupp()
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
s1.Activate
n = Cells(Rows.Count, 1).End(xlUp).Row
k = 1
For i = 1 To n
namee = Cells(i, 1).Value
For j = 2 To 4
numberr = Cells(i, j).Value
s2.Cells(k, 1) = namee
s2.Cells(k, 2) = numberr
k = k + 1
Next
Next
End Sub

Coloring Excel rows

So i found this script on this site to color rows with the same cell-data and change the color when the celldata changes and it seems to work just fine, but i have two minor issues
It seems to only apply to the first 900 rows (I have an excel list with 8000+ rows)
It colors the entire row, is there a way to make it only color a certain part of the row?
Thanks in advance! here's the script:
Public Sub HighLightRows()
Dim i As Integer
i = 2 'start at 2, cause there's nothing to compare the first row with
Dim c As Integer
c = 2 'Color 1. Check http://dmcritchie.mvps.org/excel/colors.htm for color indexes
Do While (Cells(i, 1) <> "")
If (Cells(i, 1) <> Cells(i - 1, 1)) Then 'check for different value in cell A (index=1)
If c = 2 Then
c = 37 'color 2
Else
c = 2 'color 1
End If
End If
Rows(Trim(Str(i)) + ":" + Trim(Str(i))).Interior.ColorIndex = c
i = i + 1
Loop
End Sub
Try this:
Public Sub HighLightRows()
Const START_ROW As Long = 2 '<< use a Constant for fixed values
Const VALUE_COL As Long = 1
Dim rw As Range, emptyCells As Long, i As Long, currentValue, tmp
Dim arrColors
arrColors = Array(37, 2)
Set rw = ActiveSheet.Rows(START_ROW)
currentValue = Chr(0) 'dummy "current value"
Do While emptyCells < 10 'quit after 10 consecutive empty cells
tmp = rw.Cells(VALUE_COL).Value
If Len(tmp) > 0 Then
If tmp <> currentValue Then
i = i + 1
currentValue = tmp 'save the new value
End If
'assign the color to a specific set of cells in the row
' starting at cell 1 and 5 columns wide
rw.Cells(1).Resize(1, 5).Interior.ColorIndex = arrColors(i Mod 2)
emptyCells = 0 'reset empty row counter
Else
emptyCells = emptyCells + 1 'increment empty row counter
End If
Set rw = rw.Offset(1, 0) 'next row
Loop
End Sub
It looks like the code only evaluates if the cell is the same as the cell above it. Conditional formatting, as John Coleman said, would be more effective. With it values in the whole column can be evaluated instead of just adjacent ones. And, if I'm not mistaken, there's a setting to look for dup values since Excel 2007, so there doesn't have to be some kind of formula kung-fu to do it.
Unless I'm missing something, it's as simple as Conditional Formatting -> Highlight Cell Rules -> Duplicate Values.

Resources