sum specific cells with a condition - vba - vba

I want to sum the numbers from E column for each name and put the result in column F. I mean:
(Sheet1)
colA-Name: colE-Number:
row1: Michael 16
row2: Michael 20
row3: Andrew 15
row4: Edward 19
row5: Edward 13
row6: Edward 24
row7: Helen 17
And I want something like this:
colA-Name: colF-SUM:
row1: Michael 36
row2: Michael
row3: Andrew 15
row4: Edward 56
row5: Edward
row6: Edward
row7: Helen 17
Here is my code so far but doesn't work.
Sub sum()
Dim Rng As Range
Dim cel As Range
Dim lastRowA As Long
Dim myRange As Range
Dim myString As String
lastRowA = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
Set Rng = Sheets("Sheet1").Range("A2:A" & lastRowA)
Set cel = Sheets("Sheet1").Range("A:A")
'find duplicates name in column A
Set myRange = Sheets("Sheet1").Range("A2:A" & lastRowA).Find(What:=myString, LookAt:=xlPart)
For Each cel In Rng
'if founded, sum their numbers and put it in F column
If Not myRange Is Nothing Then
cel.Offset(0, 5) = cel.Offset(0, 4).Value + cel.Offset(1, 4).Value
'Range("F:F").Value = cel.Offset(0, 4).Value + cel.Offset(1, 4).Value
End If
Next
End Sub
Is anyone able to help? Thanks!

You could use a simple excel formula for the job if that may suit
=IF(ISERROR(VLOOKUP(ACurRow;A$1:A(CurRow-1);1;FALSE));SUMIF(A$1:A$LastRow;ACurRow;E$1:E$LastRow);"")
Enter the formula in your expected results column on any row - except the first one. This is the row I am referring to as "CurRow"
replace the "CurRow" and "LastRow" references to your actual values in the formula
paste the formula in all other rows

I solved the problem. I have changed the way but is what I want. The code below:
find duplicates name from column A
copy once each name in column F
calculate the sum of the numbers matched to each name.
I get the results in column G.
The code:
Sub sum()
Dim cell As Range
Dim ws As Worksheet
Dim rngA As Range
Dim myString As String
Dim i As Integer
Dim Found As Boolean
Set ws = Sheets("Sheet1")
ws.Select
lastrRowA = ws.Range("A" & Rows.Count).End(xlUp).Row
ws.Range("F2:G" & lastrRowA).Clear
Set rngA = ws.Range("A2:A" & lastrRowA)
'find duplicates in column A and copy once in column F
For Each cell In rngA
myString = cell.Value
i = 2
Found = False
While ws.Cells(i, 6).Value <> "" And Not Found
If ws.Cells(i, 6).Value = myString Then
Found = True
End If
i = i + 1
Wend
If Not Found Then
ws.Cells(i, 6).Value = myString
End If
Next
'ascending column F
ws.Select
ws.Range("F2:F" & lastrRowA).Select
Selection.sort Key1:=Range("F1"), Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortTextAsNumbers
ws.Range("A1").Select
'sum the numbers(from column E) for each Name (i.e. Michael=16+20=36 etc.) and put the results in col G
For RRF = 1 To ws.Range("F" & Rows.Count).End(xlUp).Row
NT = ws.Range("F" & RRF).Value
For RR1 = 1 To lastrRowA
If NT = ws.Range("A" & RR1).Value Then ws.Range("G" & RRF).Value = ws.Range("G" & RRF).Value + ws.Range("E" & RR1).Value
Next RR1
Next RRF
End Sub

Related

check for value , compare and copy to another column

I have a sheet with two columns. Column (E) contains ID and names from the data source and column (K) contains ID,which are extracted from comment section.
Column E contains sometime ID, starting with B2C, and sometime names and Id starting with 5. column K contains always ID starting with B2C. the length of the ID B2C is usually 11 to 13 Digit Long. the length of ID starting with 5 is 8 Digit Long.
I would like to have a VBA that checks both the columns, if there is an id starting with 5 or some Name in column E, then it should look into column K, if an ID starting with B2C is present, then it should copy to Column L, else copy the same value(from column E) to column L.
I researched through find and replace. I saw examples where exact Name of find was given and replaced with given Name. I am able to form an algorithm, but struck how to start with code in my case. the code below, has an runn time error
object varaible or with block variable not set .
Sub compare()
Dim i As Long
Dim ws As Worksheet
ws = Sheets("Sheet1")
For i = 1 To Rows.Count
If ws.Cells(i, 11).Value = "" Then
ws.Cells(i, 12).Value = ws.Cells(i, 5).Value
Else
ws.Cells(i, 12).Value = ws.Cells(i, 11).Value
End If
Next i
End Sub
I have an Image below, which Shows the end result.
Any lead would be appreciated.
The issue causing the error message is that you are missing a Set statement for your worksheet object. You must use Set when assigning an object to a variable, that is anything with its own methods. A simple data type without methods (String, Integer, Long, Boolean, ...) doesn't need the Set statement, and can just be directly assigned like i = 0.
Your code should be updated to:
Dim i As Long
Dim ws As Worksheet
Set ws = Sheets("Sheet1")
' RED FLAG! Rows.Count is going to cause you to loop through the entire column,
' see the below example for how to use the UsedRange property.
For i = 1 To Rows.Count
If ws.Cells(i, 11).Value = "" Then
ws.Cells(i, 12).Value = ws.Cells(i, 5).Value
Else
ws.Cells(i, 12).Value = ws.Cells(i, 11).Value
End If
Next I
An alternative which avoids even using a worksheet variable would be to use a With block:
Dim r As Long
With ThisWorkbook.Sheets("Sheet1")
For r = 2 To .UsedRange.Rows.Count
.Range("L" & r).Value = .Range("E" & r).Value
If .Range("K" & r).Value = "" Then .Range("L" & r).Value = .Range("K" & r).Value
Next r
End With
Edit:
There are various ways to find the last used row, each with their drawbacks. A drawback of both UsedRange and xlCellTypeLastCell is they are only reset when you save/close/re-open the workbook. A better solution can be found in this answer.
Sub compare()
Dim r As Long, lastrow As Long, ws As WorkSheet
Set ws = ThisWorkbook.Sheets("Sheet1")
lastrow = LastRowNum(ws)
With ws
For r = 2 To lastrow
.Range("L" & r).Value = .Range("E" & r).Value
If .Range("K" & r).Value = "" Then .Range("L" & r).Value = .Range("K" & r).Value
Next r
End With
End Sub
' Function from linked question
Public Function LastRowNum(Sheet As Worksheet) As Long
LastRowNum = 1
If Application.WorksheetFunction.CountA(Sheet.Cells) <> 0 Then
LastRowNum = Sheet.Cells.Find(What:="*", LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
End If
End Function
This is my solution:
Option Explicit
Sub Compare()
Dim i As Long
Dim lngLastRow As Long
Dim ws As Worksheet
lngLastRow = Range("A1").SpecialCells(xlCellTypeLastCell).Row
Set ws = Worksheets(1)
With ws
.Columns(12).Clear
.Cells(1, 12) = "Extract from Comment"
For i = 1 To lngLastRow
If .Cells(i, 11).Value = "" Then
.Cells(i, 12).Value = ws.Cells(i, 5).Value
Else
.Cells(i, 12).Value = ws.Cells(i, 11).Value
End If
Next i
End With
End Sub
It clears column(12) and writes Extract from comment in the first cell of the row, to make sure that everything is clean.
lngLastRow is the last row of the sheet.

Excel fill rows with data from columns in separate sheets

I have an excel file with lots of sheets named "xxA" and "xxB" with xx being consecutive numbers.
Each sheet has the following format:
header1 header2 header3 header 4 header5
ingredient1 description xx 20 g
ingredient2 description xx 34 ml
ingredient3 description xx 56 g
and some other rows at the end.
Basically I want to create a new sheet in which rows 2-27 from column D are copied to a column named "value" and create two new columns with the number in the sheet name and another one with the letter like so:
subject condition ingredient value
21 A ingredient1 20
21 A ingredient2 34
21 A ingredient3 56
21 B ingredient1 34
21 B ingredient2 23
21 B ingredient3 47
...
I tried messing with pivot tables but that doesn't really work. I don't know how to create a VBA, so any direction on that would be great if that is the only way to go.
I think this is what you are looking for. It copies data from worksheets and gets the sheet names split as asked. I have it hard coded to only work for double digit numbers and single letters. Do you have sheets that do not fit that form? If so, I can rework my code!
ORIGINAL:
Sub SheetSummary()
'Make new worksheet with required headers
ActiveWorkbook.Sheets.Add
ActiveSheet.name = "Summary"
range("A1").Value = "subject"
range("B1").Value = "condition"
range("C1").Value = "ingredient"
range("D1").Value = "value"
Dim ws As Worksheet
Dim wsNum As String
Dim wsLetter As String
Dim wsLastRow As Long
Dim sumLastRow As Long
Dim myCell As range
Dim nextOpenRow As Long
'If a worksheet is not the summary, then get the data
For Each ws In Worksheets
If ws.name <> "Summary" Then
wsNum = Left(ws.name, 2)
wsLetter = Right(ws.name, 1)
wsLastRow = ws.Cells(Rows.count, "A").End(xlUp).Row
nextOpenRow = Cells(Rows.count, "A").End(xlUp).Row + 1
ws.range("A2", ws.Cells(wsLastRow, "A")).Copy
range("C" & nextOpenRow).PasteSpecial xlPasteAll
lastRow = Cells(Rows.count, "C").End(xlUp).Row
ws.range("C2", ws.Cells(wsLastRow, "C")).Copy
range("D" & nextOpenRow).PasteSpecial xlPasteAll
Application.CutCopyMode = False
For Each myCell In range("A2", Cells(lastRow, "A"))
If myCell.Value = "" Then
myCell.Value = wsNum
End If
Next myCell
For Each myCell In range("B2", Cells(lastRow, "B"))
If myCell.Value = "" Then
myCell.Value = wsLetter
End If
Next myCell
End If
Next ws
End Sub
EDIT:
Sub SheetSummary()
'Make new worksheet with required headers
ActiveWorkbook.Sheets.Add
ActiveSheet.name = "Summary"
range("A1").Value = "subject"
range("B1").Value = "condition"
range("C1").Value = "ingredient"
range("D1").Value = "value"
Dim ws As Worksheet
Dim wsNum As String
Dim wsLetter As String
Dim wsLastRow As Long
Dim sumLastRow As Long
Dim myCell As range
Dim nextOpenRow As Long
'If a worksheet is not the summary, then get the data
For Each ws In Worksheets
If ws.name <> "Summary" Then
wsNum = Left(ws.name, 2)
wsLetter = Right(ws.name, 1)
wsLastRow = ws.Cells(Rows.count, "A").End(xlUp).Row
nextOpenRow = Cells(Rows.count, "A").End(xlUp).Row + 1
ws.range("A2:A27").Copy
range("C" & nextOpenRow).PasteSpecial xlPasteAll
lastRow = Cells(Rows.count, "C").End(xlUp).Row
ws.range("D2:D27").Copy
range("D" & nextOpenRow).PasteSpecial xlPasteAll
Application.CutCopyMode = False
For Each myCell In range("A2", Cells(lastRow, "A"))
If myCell.Value = "" Then
myCell.Value = wsNum
End If
Next myCell
For Each myCell In range("B2", Cells(lastRow, "B"))
If myCell.Value = "" Then
myCell.Value = wsLetter
End If
Next myCell
End If
Next ws
End Sub
Since you don't know VBA I wouldn't recommend taking that route. You can acheive everything you want using Excel formulas.
To get the name of a sheet use:
=MID(CELL("filename",A1),FIND("]",CELL("filename",A1))+1,255)
Replace "A1" with a reference to the cell on the worksheet you want the name of.
Then use the Left() function to split out the "xx" from the name and then use the Right() function to split out "A"
Hope this helps.

Color non-adjacent cells that match criteria

I use the below code to color the cells in column K and Z that match the criteria; but it colors all cells between K and Z. To fix, I use the last line of code to remove the color in columns L thru Y. Is there a way to modify the line of code that starts with "Range" to only color cells K and Z that match the criteria?
Sub ColrCls()
Dim ws As Worksheet
Dim lRow As Long, i As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("K" & .Rows.Count).End(xlUp).Row
For i = 2 To lRow
If .Cells(i, 11).Value = "Non Sen" And .Cells(i, 26).Value = "N/A" Then
Range(.Cells(i, 11), .Cells(i, 26)).Interior.ColorIndex = 6
End If
Next i
Columns("L:Y").Interior.ColorIndex = xlNone
End With
End Sub
You are specifying the Range.Parent property in your With ... End With statement but ignoring it when it is most important¹.
Sub ColrCls()
Dim ws As Worksheet
Dim lRow As Long, i As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lRow = .Range("K" & .Rows.Count).End(xlUp).Row
For i = 2 To lRow
If .Cells(i, 11).Value = "Non Sen" And .Cells(i, 26).Value = "N/A" Then
.Range("K" & i & ", Z" & i).Interior.ColorIndex = 6
Else
.Range("K" & i & ", Z" & i).Interior.Pattern = xlNone
End If
Next i
End With
End Sub
A Range object to Union discontiguous cells could be one of the following.
.Range("K5, Z5")
Union(.Cells(5, "K"), .Cells(5, "Z"))
In the example above, I've concatenated together a string like the first of these two examples.
¹ See Is the . in .Range necessary when defined by .Cells? for an earnest discussion on this subject.
You could replace
Range(.Cells(i, 11), .Cells(i, 26)).Interior.ColorIndex = 6
with
.Cells(i, 11).Interior.ColorIndex = 6
.Cells(i, 26).Interior.ColorIndex = 6

Compare between sheets in excel vba

I'm new to VBA and currently busting my head to maintain item list in excel which is connected to database.
sheet1 columns (starts from column B)
UID | itemno. | itemweight | processed
sheet2 same headers(starts from column A)
UID | itemno. | itemweight | deleted
I framed this theoretical target and coded it, tried several times.
It is not looping :( and not achieving my target.
Any suggestions will be helpful! Thanks in advance
Following are my steps framed with the code:
Private Sub CommandButton3_Click()
' Loop until no new UID found
' 1. GoTo Sheet2 to First/Next Cell with UID
' 2. Read UID value
' 3. GoTo Sheet1
' 4. Search in UID column for read UID
' 5. if UID found
' 5.1 get data from Sheet1
' 5.2 GoTo Sheet 2
' 5.3 Past data in right cells
' 5.4 GoTo Sheet 1
' 5.5 Put check flag in proccessed field
' 6. if UID NOT found
' 6.1 GoTo Sheet 2
' 6.2 Put delete flag in delete field
' Loop End
'
' GoTo Sheet1
' Search for all parts with no checked process flag
' Copy datasets
' GoTo Sheet2
' Add data to the end of table
---------------------------------------------------
Dim dict As Object
Dim proc As Range
Dim del As Range
Dim chk, myrange As Range
Set dict = CreateObject("Scripting.dictionary")
Dim sheet1 As Worksheet, Sheet2 As Worksheet
Set sheet1 = ThisWorkbook.Worksheets("MetadataSheet")
Set Sheet2 = ThisWorkbook.Worksheets("PlanningData")
' Read values from sheet2 to dictionary
Dim lastRow As Long
lastRow = Sheet2.Cells(Sheet2.Rows.Count, 1).End(xlUp).Row
Dim i As Long
For i = 1 To lastRow
' Store value to dictionary
dict(Sheet2.Cells(i, 1).Value) = 1
Next
' Read from sheet1 and check if each value exists
lastRow = sheet1.Cells(sheet1.Rows.Count, 2).End(xlUp).Row
For i = 1 To lastRow
' Check if value exists in dictionary
If dict.exists(sheet1.Cells(i, 2).Value) Then
' found
sheet1.Range("B2:D2").Select
Selection.Copy
Sheet2.Select
Sheet2.Range("A2:C2").Select
ActiveSheet.Paste
sheet1.Select
sheet1.Range("E2").Select
Set proc = sheet1.Range("E2")
proc.Value = "X"
Else
' not found
Sheet2.Select
Set del = Sheet2.Range("D2")
del.Value = "X"
End If
Next
'for initial load
sheet1.Select
Set chk = sheet1.Range("E2", "E" & lastRow)
For Each chk In myrange
If chk.Value = "" Then
chk.Range("B2:D2").Select
Selection.Copy Destination:= _
Sheets(2).Range("A65536").End(xlUp)(2, 1)
End If
Next chk
End Sub
This code is almost working good. For the first run it loads the data from sheet1 to sheet2.
During second run I get an type mismatch error!
Dim x As Long
Dim y As Long
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim lastRow1, lastRow2, lastrow3 As Long
Dim proc As Range
Dim del As Range
Set ws1 = Worksheets("Metadatasheet")
Set ws2 = Worksheets("PlanningData")
y = 2 'this is the first row where your data will output
x = 2 'this is the first row where you want to check for data
lastRow1 = ws1.Range("B:B").Find("*", SearchDirection:=xlPrevious).Row
lastRow2 = ws2.Range("A:A").Find("*", SearchDirection:=xlPrevious).Row
If Not IsEmpty(ws2.Range("B2").Value) Then
Do Until ws1.Range("B2") = ""
For x = 2 To lastRow2
If ws1.Range("B2", "B" & lastRow1).Value = ws2.Range("A2", "A" & lastRow2).Value Then
ws2.Range("A" & y).Value = ws1.Range("B" & x).Value
ws2.Range("B" & y).Value = ws1.Range("C" & x).Value
ws2.Range("C" & y).Value = ws1.Range("D" & x).Value
Set proc = ws1.Range("E" & y)
proc.Value = "X"
y = y + 1
Else
Set del = ws2.Range("D" & y)
del.Value = "X"
End If
Next x
Loop
Else
'lastrow3 = ws1.Range("E:E").Find("*", SearchDirection:=xlPrevious).Row
For x = 2 To lastRow1
If Not ws1.Range("E" & y).Value = "X" Then
ws2.Range("A" & y).Value = ws1.Range("B" & x).Value
ws2.Range("B" & y).Value = ws1.Range("C" & x).Value
ws2.Range("C" & y).Value = ws1.Range("D" & x).Value
y = y + 1
End If
Next x
End If
End Sub

VBA: Skip cells in range

I'm trying to fill formulas in column C like this:
LastRow = Range("A65536").End(xlUp).Row
Range(Cells(1, 3), Cells(LastRow, 3)).Formula = "=A1*B1"
And it works fine. But is it possible to skip those cells in column C, which value is for example > 0 ?
A B C -> A B C
3 2 0 3 2 6
3 4 0 3 4 12
5 6 5 5 6 5 <- this value is not updated
For this you will have to do a slight change to how your data looks like. For example, you need to add "Header" to the first row. The reason we would do that is because we would be using AutoFilter
Let's say your data looks like this
Now use this code
Sub Sample()
Dim ws As Worksheet
Dim copyFrom As Range
Dim lRow As Long
'~~> Change this to the relevant sheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
With ws
'~~> Remove any filters
.AutoFilterMode = False
'~~> Get lastrow in column c
lRow = .Range("C" & .Rows.Count).End(xlUp).Row
'~~> Filter the col C to get values which have 0
With .Range("C1:C" & lRow)
.AutoFilter Field:=1, Criteria1:="=0"
Set copyFrom = .Offset(1, 0).SpecialCells(xlCellTypeVisible)
'~~> Assign the formula to all the cells in one go
If Not copyFrom Is Nothing Then _
copyFrom.Formula = "=A" & copyFrom.Row & "*B" & copyFrom.Row
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
End Sub
Output
One way to do it through loop :
Sub test()
Dim rngTest As Range
Dim rngCell As Range
LastRow = Range("A65536").End(xlUp).Row
Set rngTest = Range(Cells(1, 3), Cells(LastRow, 3))
Application.Calculation = xlCalculationManual
For Each rngCell In rngTest.Cells
If Not rngCell <> "" Then
rngCell.Formula = "=" & rngCell.Offset(, -2).Address & "*" & rngCell.Offset(, -1).Address
End If
Next
Application.Calculation = xlCalculationAutomatic
End Sub