Excel VBA Find value in column, and do math - vba

I have
Column A - with Names
Column B - with Quantities
Column C - Where I want returned Value of Quantity x Cost
Column E - with Names but located in different cells
Column F - with Prices
What I'm trying to achieve is: Take value from A1 and find it in E:E (Lets say we found it in E10), when found take value of B1 and multiply it by Respective value of F10 - and put all this in Column C
And so on for all values in column A
I was trying to do it with Do while and two variables x and y, but for some reason it doesn't find all values only for some rows.
Thank you in Advance.
Sub update_button()
'calculates money value for amazon sku
Dim x, y, z As Integer 'x, y, and z variables function as loop counters
'Loop through added SKU/Prices
For x = 4 To 25000
If Worksheets("Sheet1").Range("H" & x) = "" Then
'Blank row found, exit the loop
Exit For
End If
'Loop through Column E to find the first blank row to add the value from H into
For y = 4 To 25000
If Worksheets("Sheet1").Range("E" & y) = "" Then
'Blank row found, Add SKU and Price
Worksheets("Sheet1").Range("E" & y) = Worksheets("Sheet1").Range("H" & x)
Worksheets("Sheet1").Range("F" & y) = Worksheets("Sheet1").Range("I" & x)
'Blank out Columns H and I to prevent need to do it manually
Worksheets("Sheet1").Range("H" & x) = ""
Worksheets("Sheet1").Range("I" & x) = ""
Exit For
End If
Next y
Next x
'---NOW THIS IS WHERE I HAVE THE PROBLEM
'Get Values
Dim intCumulativePrice As Integer
'Loop through report tab and get SKU
x = 4 'initialize x to the first row of data on the Sheet1 tab
Do While Worksheets("Sheet1").Range("A" & x) <> "" 'Loop through valid SKU's to find price of item
y = 4 'initialize y to the first row of SKUs on the Sheet1 tab
Do While Worksheets("Sheet1").Range("E" & y) <> ""
If Worksheets("Sheet1").Range("E" & x) = Worksheets("Sheet1").Range("A" & y) Then 'Check if current SKU on Sheet1 tab matches the current SKU from SKU list
'Calculates the total
intCumulativePrice = intCumulativePrice + (Worksheets("Sheet1").Range("B" & y) * Worksheets("Sheet1").Range("F" & x))
' Puts Quantity X Price in Column B agains every Cell
Worksheets("Sheet1").Range("C" & y) = (Worksheets("Sheet1").Range("B" & y) * Worksheets("Sheet1").Range("F" & x))
Exit Do
End If
y = y + 1
Loop
x = x + 1
Loop
'Puts Grand total in Column L Cell 4
Worksheets("Sheet1").Range("L4") = intCumulativePrice
'Show messagebox to show that report processing has completed
MsgBox "Report processing has been completed successfully", vbInformation, "Processing Complete!"
End Sub

You can do this with a simple VLOOKUP formula in column C.
=VLOOKUP(A1,E1:F65000,2,FALSE)*B1
You can also use a named range for the data in columns E and F, so you don't have to rely on a fixed address like E1:F65000.

To do this with VBA you should copy the source data to Variant arrays and loop over those. Much faster and IMO easier to read and debug.
Something like this
Sub Demo()
Dim Dat As Variant
Dim PriceList As Range
Dim PriceListNames As Variant
Dim PriceListPrices As Variant
Dim Res As Variant
Dim sh As Worksheet
Dim i As Long
Dim nm As String
Dim nmIdx As Variant
Dim FirstDataRow As Long
FirstDataRow = 4
Set sh = ActiveSheet
With sh
Dat = Range(.Cells(FirstDataRow, "B"), .Cells(.Rows.Count, "A").End(xlUp))
Set PriceList = Range(.Cells(FirstDataRow, "E"), .Cells(.Rows.Count, "F").End(xlUp))
PriceListNames = Application.Transpose(PriceList.Columns(1)) ' Need a 1D array for Match
PriceListPrices = PriceList.Columns(2)
ReDim Res(1 To UBound(Dat, 1), 1 To 1)
For i = 1 To UBound(Dat, 1)
nm = Dat(i, 1)
nmIdx = Application.Match(nm, PriceListNames, 0)
If Not IsError(nmIdx) Then
Res(i, 1) = Dat(i, 2) * PriceListPrices(nmIdx, 1)
End If
Next
Range(.Cells(FirstDataRow, 3), .Cells(UBound(Dat, 1) + FirstDataRow - 1, 3)) = Res
End With
End Sub

Try this:
Sub HTH()
With Worksheets("Sheet1")
With .Range("A4", .Range("A" & Rows.Count).End(xlUp)).Offset(, 2)
.Formula = "=VLOOKUP(A4,E:F,2,FALSE)*$B$1"
.Value = .Value
End With
End With
End Sub

Related

Auto scheduling

I am trying to make an auto scheduling program with an excel.
For example, each number is certain job assigned to the person given day.
1/2 1/3 1/4 1/5
Tom 1 2 2 ?
Justin 2 3 1 ?
Mary 3 3 ?
Sam 1 ?
Check O O X ? ## check is like =if(b2=c2,"O","X")
The things I want to make sure is every person is given a different job from yesterday.
My idea
while
randomly distribute jobs for 1/5
wend CheckCell = "O"
But I found that checking cell in the vba script doesn't work - the cell is not updated in each while loop.
Could you give me a little pointer for these kinds of program? Because I am new to vbaScript, any kinds of help would be appreciated.
Using VBA, I'm sure there are better ways to do this, but this will check the values from the penultimate column against values from last column and if they match it will write "O" to under the last column, else it will write "X":
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get the last row with data on Column A
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
counter = 0 'set counter
For i = 2 To LastRow 'loop through penultimate column and add values to array
If ws.Cells(i, LastCol - 1).Value <> "" Then
Values = Values & ws.Cells(i, LastCol - 1) & ","
End If
Next i
Values = Left(Values, Len(Values) - 1)
Values = Split(Values, ",") 'split values into array
For i = 2 To LastRow 'loop through last column and add values to array
If ws.Cells(i, LastCol).Value <> "" Then
ValuesCheck = ValuesCheck & ws.Cells(i, LastCol) & ","
End If
Next i
ValuesCheck = Left(ValuesCheck, Len(ValuesCheck) - 1)
ValuesCheck = Split(ValuesCheck, ",")
For y = LBound(Values) To UBound(Values) 'loop through both arrays to find all values match
For x = LBound(ValuesCheck) To UBound(ValuesCheck)
If Values(y) = ValuesCheck(x) Then counter = counter + 1
Next x
Next y
If counter = UBound(Values) + 1 Then 'if values match
ws.Cells(LastRow + 1, LastCol).Value = "O"
Else 'else write X
ws.Cells(LastRow + 1, LastCol).Value = "X"
End If
End Sub
just to clarify are you looking to implement the random number in the vba or the check.
To do the check the best way would be to set the area as a range and then check each using the cells(r,c) code, like below
Sub checker()
Dim rng As Range
Dim r As Integer, c As Integer
Set rng = Selection
For r = 1 To rng.Rows.Count
For c = 1 To rng.Columns.Count
If rng.Cells(r, c) = rng.Cells(r, c + 1) Then
rng.Cells(r, c).Interior.Color = RGB(255, 0, 0)
End If
Next c
Next r
End Sub
this macro with check the text you have selected for the issue and change the cell red if it matches the value to the right.
To make it work for you change set rng = selection to your range and change the rng.Cells(r, c).Interior.Color = RGB(255, 0, 0) to the action you want
A sligthly different approach than the other answers.
Add this function:
Function PickJob(AvailableJobs As String, AvoidJob As String)
Dim MaxTries As Integer
Dim RandomJob As String
Dim Jobs() As String
Jobs = Split(AvailableJobs, ",")
MaxTries = 100
Do
MaxTries = MaxTries - 1
If MaxTries = 0 Then
MsgBox "Could find fitting job"
End
End If
RandomJob = Jobs(Int((1 + UBound(Jobs)) * Rnd()))
Loop Until RandomJob <> AvoidJob
PickJob = RandomJob
End Function
And put this formula in your sheet
=PickJob("1,2,3",D2)
where D2 points to is the previous job

Create Sub array variable name sheet

I have one array with Sheet Names called SheetNames and I want to generate a sub array of it that only returns True at the condition (IF). I try to have a loop into a cell value onto different sheets, evaluating condition cell.value = "S". When checks that for the first D column (z = 4) I want to make the same check (IF condition) for columns D to DR at the same row.
I need to get similar result if I use formula at
Diary!C7
= IF (element!D6 = "S",CONCATENATE (element!B1, ", "), ""),
IF (element1!D6 = "S",CONCATENATE (element1!B1, ", "), ""), ....
IF (element!E6 = "S",CONCATENATE (element!B1, ", "), ""),
IF (element1!E6 = "S",CONCATENATE (element1!B1, ", "), "") .... )
Where element is a sheet name taken from an array with the sheet names who get the condition (Code S or another code).
SheetNames is one array with all the book sheets and FSheet (Filtered Sheet with condition) an array with only the filtered (with condition IF). When I can populate FSheet array for each sheet I test the condition then I must concatenate it's values at another sheet/cell and began the test condition again to the next cell (E6) ... But I'm trapped at the step to create FSheet.
Sub Test()
Dim ws As Worksheet
Dim SheetNames() As String, FSheets() As String, q As String
Dim element As Variant
Dim lastSheet As Integer, r As Integer, incrSheet As Integer, i As Integer
Dim Rgn As Range
' Enter the sheet names into an array. Redim array's size to the number of sheets (lastSheet)
For Each ws In ActiveWorkbook.Worksheets
ReDim Preserve SheetNames(lastSheet)
SheetNames(lastSheet) = ws.name
lastSheet = lastSheet + 1
Next ws
MsgBox lastSheet
' Test condition for each sheet/cell
For z = 4 To 11
For Each element In SheetNames()
incrSheet = 1
If ActiveWorkbook.Sheets(element).Cells(6, z).Value = "S" Then
ReDim Preserve FSheets(incrSheet)
FSheets(incrSheet) = element
incrSheet = incrSheet + 1
End If
Next element
Next z
i = 3
' Define the sheet to work (total project will have more than one, one for code we need test, S, C, etc)
With Worksheets("Diary")
.Activate
.Range("C7").Select
' Concatenate values at Summary page
Do
Cells(7, i).Select
For r = 1 To UBound(FSheets)
'Concatenate with &:
varConctnt = varConctnt & ", " & FSheets(r)
Next r
'remove the "&" before the first element:
varConctnt = Mid(varConctnt, 2)
q = varConctnt
varConctnt = ""
i = i + 1
ActiveCell.Value = q
Loop While i < 11
' Drag the formula for the rest of the rows
Range("C7:J7").Select
Selection.AutoFill Destination:=Range("C7:J12"), Type:=xlFillDefault
End With
End Sub
Where you are going wrong, is your attempt to dynamically set the range. Assuming you are testing the value of a single cell, it is much easier to use Cells, rather than Range, since you can use R1C1 notation. Try something like this:
incrSheet = 1
For z = 4 To 11
For Each element In SheetNames()
If ActiveWorkbook.Sheets(element).Cells(6, z).Value = "S" Then
ReDim Preserve FSheets(incrSheet)
FSheets(incrSheet) = element
MsgBox incrSheet
incrSheet = incrSheet + 1
End If
Next element
Next z

Concatenate Strings with Do Loop with dynamic column and files

So at Assign sheet I indicate the sheets to take data for each group (each for column and the first row is the explanation of the group). I dynamically can add res at the file or delete
After I use predefined codes to assign which type of discounts / day are applied. At the example I only put two codes (C and S) and one week. For example the raw sheet data for designations Red and Black.
Data product worksheet
Then at Diary I want to show the result of concatenate the B1 value (name of product) each time code fromm price are indicated into the rows. Also I use two loop because at raw product data I have one column for price but at the Diary I have two
Summary page
This is what I finally want to get and doing like that because my boss don't know anything for code and he wan't edite it so I try to do ll dynamic at the sheets :) [I only put two images because i don't have reputations point enough to put more]
With the formula I only get FALSE as answer :(, and I need to get what you can see at summary page
Sub Diary()
Dim I As Integer, x As Integer, y As Integer, z As Integer, n As Integer
Dim p As Integer, d As Integer, f As Integer
Dim a As String, b As String
Dim element As Variant
' Initialize variables I and y at 3 and 4 to begin to show the data at the column I desire. Also x and z were intended to pass the one column mode data sheet to the two column mode at the summary page.
I = 3
x = 1
y = 4
z = 0
With Worksheets("Asign")
.Activate
.Range("B2").Select
End With
' Set the size of Data with sheet names it get form the page assign. It can dynamically changed as size as names of sheets
With ActiveSheet
r = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
Dim Data() As String
ReDim Data(r)
For p = 1 To r - 1
Data(p) = ActiveSheet.Cells(p + 1, "B").Value
Next p
With Worksheets("Diary")
.Activate
.Range("C7").Select
End With
' At Diary concatenate the same cell for all the sheets I have his name stored at Data() and then pass to the next cell with data at raw data sheets (in the images (Red, Black ,... pages). In this case search for code S
Do
Cells(7, I).Select
ActiveCell.Value = ActiveCell.FormulaR1C1 = "=CONCATENATE(" & a & ")"
For Each element In Data
b = ActiveCell.FormulaR1C1 = "IF(Data& !R[-2]C[" & x & "]=""S"",CONCATENATE(Data&!R1C2,"", ""),"""")"
a = b & ";" & b
Next element
x = x - 1
I = I + 2
Loop While I < 4
' The same for the second column of summary sheet called Diary. In this case search for code C
Do
Cells(7, y).Select
ActiveCell.Value = ActiveCell.FormulaR1C1 = _
"=CONCATENATE(" & a & ")"
For Each element In Data
b = ActiveCell.FormulaR1C1 = "IF(Data& !R[-2]C[" & z & "]=""S"",CONCATENATE(Data&!R1C2,"", ""),"""")"
a = b & ";" & b
Next element
z = z - 1
y = y + 2
Loop While I < 4
' Drag and Drop the formula to all the sheet's cells you need
Range("C8:E8").Select
Selection.AutoFill Destination:=Range("C8:E10"), Type:=xlFillDefault
'
End Sub
try this. .... could be simplified by looping through "colors" .... black, red, etc
Sub Diary()
Dim red As Variant
red = Sheets("red").Range("d6:g12") ' put range data into an array for processing
Dim black As Variant
black = Sheets("black").Range("d6:g12")
Dim i As Integer
Dim j As Integer
Dim strC As String
Dim strS As String
For i = 1 To 7
For j = 1 To 4
strC = ""
strS = ""
If LCase(black(i, j)) = "c" Then strC = "Black"
If LCase(black(i, j)) = "s" Then strS = "Black"
If LCase(red(i, j)) = "c" Then
If Len(strC) > 1 Then strC = strC & ";"
strC = strC & "Red"
End If
If LCase(red(i, j)) = "s" Then
If Len(strS) > 1 Then strS = strS & ";"
strS = strS & "Red"
End If
Sheets("diary").Range("c7").Cells(i, j * 2 - 1) = strS
Sheets("diary").Range("c7").Cells(i, j * 2) = strC
Next j
Next i
End Sub

Find Copy and Paste in VBA macro

I am trying to write a macro which search data from one sheet and copy's to another.
But now I have a problem because I want to copy data between two searches and paste the whole data from multiple cells into one single cell.
For example in the above picture my macro:
SEARCH for "--------------" and "*****END OF RECORD"
COPIES everything in between , here example data in row 29 and 30 and from column A,B,C
PASTE all the data from multiple cells A29,B29,C29 and then A30,B30,C30 to single cell in sheet 2 say cell E2.
This pattern is reoccurring in the column A so I want to search for the next occurrence and do all the steps 1,2,3 and this time I will paste it in Sheet2 , cell E3.
Below is the code:
I am able to search my pattern but hard time in giving references to the cells in between those searched patterns and then copying all the data to ONE cell.
x = 2: y = 2: Z = 7000: m = 0: n = 0
Do
x = x + 1
If ThisWorkbook.Sheets("lic").Range("A" & x) = "---------------------" Then m = x
If ThisWorkbook.Sheets("lic").Range("A" & x) = "****** END OF RECORD" Then n = x
If (n > 0) Then
Do
For i = m To n
ThisWorkbook.Sheets("lic").Range("A" & i + 1).Copy
ThisWorkbook.Sheets("lic").Range("B" & i + 1).Copy
ThisWorkbook.Sheets("lic").Range("C" & i + 1).Copy
'If (n > 0) Then ThisWorkbook.Sheets("Sheet1").Range("E" & y) = ThisWorkbook.Sheets("lic").Range("A" & m + 1, "C" & n - 1): y = y + 1
'If (n > 0) Then ThisWorkbook.Sheets("Sheet1").Range("E" & y).Resize(CopyFrom.Rows.Count).Value = CopyFrom.Value: y = y + 1
Loop While Not x > Z
'Driver's Licence #:Driver's Licence #:Driver's Licence #:
x = 2: y = 2: Z = 7000: counter = 1
Do
x = x + 1
If ThisWorkbook.Sheets("lic").Range("A" & x) = "Driver's Licence #:" Then counter = counter + 1
If (counter = 2) Then ThisWorkbook.Sheets("Sheet1").Range("B" & y) = ThisWorkbook.Sheets("lic").Range("C" & x): y = y + 1: counter = 0
If x = Z Then Exit Sub
Loop
End Sub
Considering that the search is working correctly, about the copy thing you just need to do:
Sheet2.Range("E2").value = ThisWorkbook.Sheets("lic").Range("A" & i + 1).value & ";" & ThisWorkbook.Sheets("lic").Range("B" & i + 1).value & ";" & ThisWorkbook.Sheets("lic").Range("C" & i + 1).value
The result will be something like: AIR COO; L DAT; A
--------UPDATE---------
It was hard to understand your code, so I'm write a new one. Basically it's copy what it found on sheet1 to sheet2.
Sub Copy()
Dim count As Integer 'Counter of loops to the for
Dim Z As Integer 'Limit of (?)
Dim h As Integer 'Count the filled cells on sheet2
Dim y As Integer 'Counter the columns to be copied
Z = 7000
h = 1
'Assuming that the "----" will always be on the top, the code will start searching on the second row
'if it's not true, will be needed to validate this to.
For count = 2 To Z
If Sheet1.Cells(count, 1).Value <> "****** END OF RECORD" Then
If Sheet1.Cells(count, 1).Value <> "" Then
For y = 1 To 3 'In case you need to copy more columns just adjust this for.
Sheet2.Cells(h, 1).Value = Sheet2.Cells(h, 1).Value & Sheet1.Cells(count, y).Value
Next y
h = h + 1
End If
Else
MsgBox "END OF RECORD REACHED"
Exit Sub
End If
Next count
End Sub
Maybe I don't get the full idea but this might work for you.
I'm not at all sure what you want to see in the final output, so this is an educated guess:
Sub DenseCopyPasteFill ()
Dim wsFrom, wsTo As Worksheet
Dim ur As Range
Dim row, newRow As Integer
Dim dataOn As Boolean
Dim currentVal As String
dataOn = False
newRow = 3
Set wsFrom = Sheets("Sheet1")
Set wsTo = Sheets("Sheet2")
Set ur = wsFrom.UsedRange
For row = 1 To ur.Rows.Count
If wsFrom.Cells(row, 1).Value2 = "--------------" Then
dataOn = True
ElseIf wsFrom.Cells(row, 1).Value2 = "***** END OF RECORD" Then
newRow = newRow + 1
dataOn = False
ElseIf dataOn Then
currentVal = wsTo.Cells(newRow, 5).Value2
wsTo.Cells(newRow, 5).Value2 = currentVal & _
wsFrom.Cells(row, 1) & wsFrom.Cells(row, 2) & _
wsFrom.Cells(row, 3)
End If
Next row
End Sub
If you can get away without using the Windows clipboard, I would. Instead of copy/paste, here I demonstrated how you can simply add or append a value.
Add this sub:
Sub copy_range(rng As Range)
Dim str As String
str = rng.Cells(1).Value & rng.Cells(2).Value & rng.Cells(3).Value
Range("E" & Range("E" & Rows.Count).End(xlUp).Row + 1).Value = str
End Sub
Then your for loop should look like this:
For i = m To n
copy_range ThisWorkbook.Sheets("lic").Range("A" & i + 1 & ":C" & i + 1)
Next i

Copy value & offset from workbook X to workbook Y only when range value > 0, then loop for remaining rows

Any and all help or suggestions are certainly appreciated... I'm working with two separate workbooks.
Workbook X is my "Order Form", this contains relevant columns:
column A: item num
column B: qty
I want to pull both the item number and the qty value ONLY IF qty > 0 or <> NULL.
This data will then be sent to Workbook Y, which contains:
column B: item num
column C: qty
Below is one variation of the code I've attempted, I'm about 5 hours into this and running low on ideas.
Thanks!
Dim x As Workbook
Dim y As Workbook
'Open both workbooks
Set x = Workbooks.Open(Me.txtSelect)
Set y = Workbooks.Open(Me.txtOutput)
Dim i As Integer
i = 1
While i < 200
'Extract data from sheet x into sheet y
If x.Sheets("Order Form").Cells(i, 2).Value > 0 Then
x.Sheets("Order Form").Cells(i, 1).Value = y.Sheets("UploadTemplate").Cells(B2 + i).Value
x.Sheets("Order Form").Cells(i, 2).Value = y.Sheets("UploadTemplate").Cells(C2 + i).Value
Else
End If
i = i + 1
Wend
Here was the original code I had in place of the "While", but was getting ALL values:
If x.Sheets("Order Form").Range("B9").Value > 0 Then
x.Sheets("Order Form").Range("A9:B200").AdvancedFilter Action:=xlFilterCopy, _
CriteriaRange:=x.Sheets("Order Form").Range("A11:B11"), CopyToRange:=y.Sheets("UploadTemplate").Range("B2:C2") _
, Unique:=False
End If
Try this:
Dim x As Workbook
Dim y As Workbook
dim yi as integer
'Open both workbooks
Set x = Workbooks.Open(Me.txtSelect)
Set y = Workbooks.Open(Me.txtOutput)
Dim i As Integer
i = 1
yi=2
While i < 200
'Extract data from sheet x into sheet y
If x.Sheets("Order Form").Cells(i, 2).Value > 0 Then
y.Sheets("UploadTemplate").Cells(yi,2).Value=x.Sheets("Order Form").Cells(i, 1).Value
y.Sheets("UploadTemplate").Cells(yi,3).Value=x.Sheets("Order Form").Cells(i, 2).Value
yi = yi + 1
End If
i = i + 1
Wend
You need a second counter on the second workbook, as if you're skipping rows in the first workbook because they're blank, you don't want to do the same in the second workbook(I assume)
You also need to ensure that you taking the values from the first sheet, and assigning them to the second sheet. eg. Y gets the value from X - Y=X