Decrementing a value in VBA - vba

I'm new here and to VBA, and joined because a friend told me this community is helpful.
I'm currently having a problema in VBA that is as follow:
-The user chooses a code from a list in the Worksheet "Book Rent";
-This code is kept in Cell D10 and is saved by a Macro in another Sheet "Renting" where several data is saved form the form;
-Then in another Worksheet called "Books" there is the book code in column B, the title in column C and the quantity of books available in D;
-What I wanted is when the user rents the book, the quantity decrements by 1(I don't need a validation if the number is zero or not right now, so just the decrement is enough);
I have tried doing Sheets("Book Rent").Cells(10, "D") = Sheets("Books").Range("D5") - 1 but it gives me an error.
I am right now, trying to use a for loop but it also gives me na error.
If someone could give me some help or if you need more information I would be glad to help.
Here's my for loop:
Dim Looper As Integer
For Looper = 5 To Sheets("Livros").Cells(Looper, 2) = Sheets("Form Aluguer").Cells(10, "D")
Sheets("Livros").Cells(Looper, "D").Select
Cells(Looper, "D") = Cells(Looper, "D") - 1
Next Looper
Thank you by the way.

If you want to loop from X to Y (where X > Y) just use this:
For X = 5 to 0 Step - 1
Next
The Step command tells it how to increment X, and 0 is how long we are incrementing X until. Keep in mind that the For loop needs some number as the second argument, so you could do:
For X = 5 to Sheets("Form Aluguer").Cells(10, "D")
But you can only do this if Sheets("Form Aluguer").Cells(10, "D") contains a number.
If you want to loop while Sheets("Livros").Cells(Looper, 2) = Sheets("Form Aluguer").Cells(10, "D") you would need to do something like this:
Dim looper as Long
Do While Sheets("Livros").Cells(Looper, 2) = Sheets("Form Aluguer").Cells(10, "D")
looper = looper -1
Loop

EDIT:
Here is what I think you are looking for, I've left my original answer at the bottom of this post. But this code is more of a working version of what I think you need. It just may need to be adapted slightly depending on the layout of your workbook.
I have added more comments than I usually would due to you being new to VBA.
Hope this helps
Sub Decrement()
'Declare a BookCount variable to hold the value of the cell containing the number of books
Dim BookCount As Integer
'Declare a range to hold the range of the Books Quantity column
Dim QuantityRng As Range
'Dim two ranges to use during the loops
Dim RentCell As Range, BookCell As Range
'Declare and set a variable named RentRng to the Rental list and BookRng to the Book list's Code column
Dim RentRng As Range, BookRng As Range
Set RentRng = Worksheets("Renting").Range("A:A")
Set BookRng = Worksheets("Books").Range("B:B")
'For every cell in the Renting list
For Each RentCell In RentRng
'Stop the subroutine when the loop encounters a blank cell
If RentCell.Value = "" Then
Exit Sub
End If
'Check every cell in the Book code list
For Each BookCell In BookRng
'Exit the loop when encounters a blank cell so can look for the next book in the outer loop
If BookCell.Value = "" Then
Exit For
End If
'Check if the Rental worksheet Code Matches the Books worksheet code, and if so then decrements the field by one
If RentCell.Value = BookCell.Value Then
Set QuantityRng = BookCell.Offset(0, 2)
BookCount = QuantityRng.Value
BookCount = BookCount - 1
QuantityRng.Value = BookCount
End If
Next BookCell
Next RentCell
End Sub
I'm not sure whether I understood correctly, but I would start by declaring a variable named BookCount to hold the value of the cell in question. Then you can use BookCount = BookCount - 1
The problem you have there, is that you're trying to take 1 away from a range, when it's actually the value of the range that you are wanting to amend.
Then you can set the value of the range, to BookCount
Here's a small example
Sub Decrement()
Dim BookCount As Integer
BookCount = Range("A1").Value
BookCount = BookCount - 1
Range("A1").Value = BookCount
End Sub

Related

Excel range subtraction, overlooking errors in some cells possible?

I am having trouble figuring out how to subtract two ranges from each other, some cells in range H:H have "#N/A" while in range D:D there are no errors. I know in Excel it's a simple "=H2-D2" and drag that down but I'm in the process of recording a Macro and wanted to automate the subtraction as well. So far this is what I have:
Dim quantity1, quantity2, rIntersect, Qdiff, x As Range
Set quantity1 = Range("D:D")
Set quantity2 = Range("H:H")
Set rIntersect = Intersect(quantity1, quantity2)
For Each x In quantity1
If Intersect(rIntersect, x) Is Nothing Then
If Qdiff Is Nothing Then
Set Qdiff = x
Else
Set Qdiff = Application.Union(Qdiff, x)
End If
End If
Next x
Range("J2").Select
Dim lastRowJ As Long
lastRowJ = Range("A" & Rows.Count).End(xlUp).Row
Range("J2").AutoFill Destination:=Range("J2:J" & lastRowJ)
Place this procedure in a standard code module:
Public Sub Subtract()
[j2:j99] = [h2:h99-d2:d99]
End Sub
If you like how that works, I'm happy to embellish it so that it is not hard-coded for 98 rows only. Let me know.
UPDATE
Here is a version that will deal with any number of rows. It keys off of column D. So if there are 567 numbers in column D, then you will get 567 corresponding (subtracted) results in column J.
This assumes that the data start in row 2, and that there are no blank cells until the numbers in column D end.
If you are going to call this from the Macro Dialog then you should keep it Public. If on the other hand you are going to call it from another procedure in the same module, then you can make it Private.
Here is the enhanced solution:
Public Sub Subtract()
Dim k&
Const F = "iferror(h2:h[]-d2:d[],0)"
k = [count(d:d)]
[j2].Resize(k) = Evaluate(Replace(F, "[]", k + 1))
End Sub
Note that the routine now handles the errors and places a ZERO value in column J when the corresponding value in column H is an error. If you would prefer to have something other than a ZERO (like a blank for instance) when there are errors in column H, just let me know and I'll update to whatever you want.
UPDATE 2
Here is how to handle displaying blanks instead of zeroes:
Public Sub Subtract()
Dim k&
Const F = "iferror(if(h2:h[]-d2:d[]=0,"""",h2:h[]-d2:d[]),0)"
k = [count(d:d)]
[k2].Resize(k) = Evaluate(Replace(F, "[]", k + 1))
End Sub

Delete rows based on range possible mistake

I'm trying to delete rows on one worksheet based on a range in another worksheet. I think the problem here is probably something simple based on my limited VBA experience. Here is the code I've written:
Sub LimitedElements()
imax = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
For i = 2 To imax
If Sheets("test").Cells(j, 1).Value = Sheets("Limited Elements").Range("A1:A10") Then
Rows(i).EntireRow.Delete
End If
Next i
End Sub
I get a message saying "Application-defined or object-defined error".
Can anyone tell me what I'm doing wrong? Or if this is just a dumb way to do this and I should be doing it differently?
Please see if below works for you:
Sub LimitedElements()
Dim imax As Integer
Dim a As Variant
Dim b As Range
Dim c As Object
Dim d As Integer
imax = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
For i = 2 To imax
a = Sheets("test").Cells(i, 1).Value
Set b = Sheets("Limited Elements").Range("A1:A10")
Set c = b.Find(What:=a, LookIn:=xlValues)
If Not c Is Nothing Then
Sheets("test").Rows(i).EntireRow.Delete
i = i - 1
imax = imax - 1
End If
Next i
End Sub
Noted that it is not fine tuned and is intended to give you an understanding on how to approach the solution.
I added code to decrement i. I think I understand that the code can't tell which worksheet I'm specifying for deleting the row but I'm not sure what to do about it. I tried changing "Rows(i).EntireRow.Delete" to "Sheets("test").Rows(i).EntireRow.Delete" but I'm not sure if that's the right thing to do or not.
Some extra details to make things clearer:
Sheet "test" has about 1000 rows with unique numbers in column A. Sheet "Limited Elements" has about 100 rows with unique numbers column A. I want it it delete the rows in "test" that have values in column A that match the column A values in "Limited Elements".
Sub LimitedElements()
imax = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
For i = 2 To imax
If Sheets("test").Cells(i, 1).Value = Sheets("Limited Elements").Range("A1:A10") Then
Sheets("test").Rows(i).EntireRow.Delete
i = i - 1
imax = imax - 1
End If
Next i
End Sub
I think the original problem was that I had Cells(j,1) instead of cells(i,1). Now I've fixed that but it gives me a type mismatch error which I think is due to comparing a single cell to a range.
At this point I think I'm just lost. I can't figure out how to change it so it works and does what I want it to do.

Create new worksheet based on text in coloured cells, and copy data into new worksheet

I have a large data set which I need to manipulate and create individual worksheets. Within column B all cells which are coloured Green I would like to make a new worksheet for. Please see screen shot.
For example I would like to create worksheets titled "Shopping" & "Retail". Once the worksheet is created, I would then like to copy all the data between the "worksheet title" (Green Cells) from columns ("B:C") & ("AI:BH") Please see screen shot below for expected output;
The code I have so far is below as you can see it is not complete as I do not know how I would go about extracting data between the "Green Cells".
Sub wrksheetadd()
Dim r As Range
Dim i As Long
Dim LR As Long
Worksheets("RING Phased").Select
LR = Range("B65536").End(xlUp).Row
Set r = Range("B12:B" & (LR))
For i = r.Rows.Count To 1 Step -1
With r.Cells(i, 1)
If .DisplayFormat.Interior.ColorIndex = 35 Then
MsgBox i
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = Cells (i,1).Value
Worksheets("RING Phased").Select
End If
End With
Next i
End Sub
Any help around this would be much appreciated.
Sorry for taking a while to get back to this, I've been somewhat busy the last few days, so I haven't had much time to be on StackOverflow.
Anyway, the way I'd go about this would be to store all the found values in an array, and then loop through that array in order to find the distance between them.
The following code works for me, using some very simplified data, but I think the principle is sound:
Option Explicit
Option Base 0
Sub wrksheetadd()
Dim r As Range, c As Range
Dim i As Long: i = 0
Dim cells_with_color() As Range: ReDim cells_with_color(1)
With Worksheets("RING Phased")
' Since it doesn't seem like the first cell you want to copy from is colored, hardcode that location here.
' This also saves us from having to test if the array is empty later.
Set cells_with_color(i) = .Range("B12")
i = i + 1
Set r = Range(.Range("B13"), .Range("B" & .Cells.Rows.Count).End(xlUp))
' Put all the cells with color in the defined range into the array
For Each c In r
If c.DisplayFormat.Interior.ColorIndex = 35 Then
If i > UBound(cells_with_color) Then
ReDim Preserve cells_with_color(UBound(cells_with_color) + 1)
End If
Set cells_with_color(i) = c
i = i + 1
End If
Next
' Loop through the array, and copy from the previous range value to the current one into a new worksheet
' Reset counter first, we start at 1, since the first range-value (0 in the array) is just the start of where we started checking from
' (Hmm, reusing variables may be bad practice >_>)
i = 1
While i <= UBound(cells_with_color)
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = cells_with_color(i).Value
' Set the range to copy - we could just do this in the copy-statement, but hopefully this makes it slightly easier to read
Set r = .Rows(CStr(cells_with_color(i - 1).Row) + 1 & ":" & CStr(cells_with_color(i).Row))
' Change the destination to whereever you want it on the new sheet. I think it has to be in column one, though, since we copy entire rows.
' If you want to refine it a bit, just change whatever you set r to in the previous statement.
r.Copy Destination:=Worksheets(CStr(cells_with_color(i).Value)).Range("A1")
i = i + 1
Wend
End With
End Sub
It probably lacks some error-checking which ought to be in there, but I'll leave that as an exercise to you to figure out. I believe it is functional. Good luck!

Delete entire row when a value exist (With sheets) [duplicate]

I have 2 sheets: sheet1 and sheet2. I have a value in cell A3 (sheet1) which is not constant. And many files in sheets2.
What I would like to do, is when the value in cell A3 (Sheet1) is the same as the value in the column A (Sheet2), it will delete the entire row where is find this value (Sheet2).
This is my attempt. It doesn't work: no rows are deleted.
If Worksheets("Sheet1").Range("A3").Text = Worksheets("Sheet2").Range("A:A").Text Then
Dim f As String
f = Worksheets("Sheet1").Range("A3")
Set c = Worksheets("Sheet2").Range("A:A").Find(f)
Worksheets("Sheet2").Range(c.Address()).EntireRow.Delete
End If
My guess is that you're not finding anything with the .Find(). Since you're not checking it for is Nothing you don't know. Also, .Find() retains all the search parameters set from the last time you did a search - either via code or by hand in your spreadsheet. While only the What parameter is required, it's always worth setting the most critical parameters (noted below) for it, you may want to set them all to ensure you know exactly how you're searching.
Dim f As String
If Worksheets("Sheet1").Range("A3").Text = Worksheets("Sheet2").Range("A:A").Text Then
f = Worksheets("Sheet1").Range("A3")
Set c = Worksheets("Sheet2").Range("A:A").Find(What:=f, Match:=[Part|Whole], _
LookIn:=[Formula|value])
if not c is Nothing then
Worksheets("Sheet2").Range(c.Address()).EntireRow.Delete
else
MsgBox("Nothing found")
End If
End If
Go look at the MS docs to see what all the parameters and their enumerations are.
Sub Test()
Dim ws As Worksheet
For x = 1 To Rows.Count
If ThisWorkbook.Sheets("Sheet2").Cells(x, 1).Value = ThisWorkbook.Sheets("Sheet1").Cells(3, 1).Value Then ThisWorkbook.Sheets("Sheet2").Cells(x, 1).EntireRow.Delete
Next x
End Sub

Removing rows based on matching criteria

I have a dated CS degree so I understand the basics of VB but I don't write macros very often and need help solving a particular condition. (...but I understand functions and object oriented programming)
Assume the following:
- Column A contains reference ID's in alphanumeric form, sorted alphabetically.
- Column B contains strings of text, or blanks.
I'm trying to write a macro that automatically removes any extra rows for each unique reference number based on the contents of the "Notes" in column B. The problem is that if column A has multiple instances of a unique ref number, I need to identify which row contains something in column B. There is one catch: it is possible that the reference number has nothing in column B and should be retained.
To explain further, in the following screenshot I would need to:
Keep the yellow highlighted rows
Delete the remaining rows
I tried to show various configurations of how the report might show the data using the brackets on the right and marked in red. Its difficult to explain what I'm trying to do so I figured a picture would show what I need more clearly.
This task is making the report very manual and time consuming.
it's pretty simple
you just go throug the rows and check whether this row needs to be deleted, an earlier row with this id needs to be deleted or nothing should happen.
in my example i mark these rows and delete them in the end.
Sub foo()
Dim rngSelection As Range
Dim startingRow As Integer
Dim endRow As Integer
Dim idColumn As Integer
Dim noteColumn As Integer
Dim idValuableRow As New Dictionary
Dim deleteRows As New Collection
Set rngSelection = Selection
startingRow = rngSelection.Row
endRow = rngSelection.Rows.Count + startingRow - 1
idColumn = rngSelection.Column
noteColumn = idColumn + 1
For i = startingRow To endRow
currentID = Cells(i, idColumn)
If idValuableRow.Exists(currentID) Then
If Trim(idValuableRow(currentID)("note")) <> "" And Trim(Cells(i, noteColumn)) = "" Then
deleteRows.Add i
ElseIf idValuableRow(currentID)("note") = "" And Trim(Cells(i, noteColumn)) <> "" Then
deleteRows.Add idValuableRow(currentID)("row")
idValuableRow(currentID)("row") = i
idValuableRow(currentID)("note") = Cells(i, noteColumn)
End If
Else
Dim arr(2) As Variant
idValuableRow.Add currentID, New Dictionary
idValuableRow(currentID).Add "row", i
idValuableRow(currentID).Add "note", Cells(i, noteColumn)
End If
Next i
deletedRows = 0
For Each element In deleteRows
If element <> "" Then
Rows(element - deletedRows & ":" & element - deletedRows).Select
Selection.Delete Shift:=xlUp
deletedRows = deletedRows + 1
End If
Next element
End Sub
it could look something like this. the only thing you need is to add Microsoft Scripting Runtime in Tools/References