this is my first attempt to create a macro, so sorry in advance for my lack of knowledge on the subject. I've attempted to follow tutorials and examples online, but I'm not having a lot of luck.
I want to create a macro that can move an entire row above the previous row if certain values in the row are less than the respective values in the previous row.
I tried posting an image of the excel sheet I'm working with, but I do not have enough reputation.
The logic would be something like this:
IF--- Column2(row_i) < Column2(row_i-1)
AND--- Column3(row_i) < Column4(row_i-1)
THEN
Insert a blank row above row_i-1
Copy row_i and paste it in the blank row
Delete the original row_i
Return to top of list and begin search again
ELSE--- Move to row_i+1}
Here is what I currently have:
Sub PrioritySort()
Dim i As Integer
For i = 11 To 17
If Cells(i, 2) < Cells((i - 1), 2) Then
If Cells(i, 3) < Cells((i - 1), 4) Then
//insert row_i above row_i-1
Else
Next i
End Sub
If anyone would be willing to help, it'd be greatly appreciated!
//insert row_i above row_i-1 is something like:
Rows(i).Select
Selection.Cut
Rows(i-1).Select
Selection.Insert Shift:=xlDown
...Also remember an "End If" to close out your multiline If statements.
Let's take a sample:
column1 column2 column3 column4
4 4 4 4
3 3 3 3
2 2 2 2
1 1 1 1
We want to reordering this. Our end result should look like this
column1 column2 column3 column4
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
Macro
Sub Macro3()
Dim NoOfTimesChanged As Integer
' attempt to reorder rows and find out if reordering
' was done or not
NoOfTimesChanged = ReOrderRows()
' keep on reording until there is nothing else to reorder
Do While NoOfTimesChanged > 0
NoOfTimesChanged = ReOrderRows()
Loop
End Sub
Function
' Reorder all rows based on certain condition
' Returns: 0 or 1 to the caller
' 0 is returned when no reording was necessary
' 1 is returned when reordering was necessary
Function ReOrderRows() As Integer
Dim ReOrdered As Integer
ReOrdered = 0
' Lets start from row #3 and compare with row #2
' Remember that row #1 has headers
For i = 3 To 5
' IF--- Column2(row_i) < Column2(row_i-1)
' AND--- Column3(row_i) < Column4(row_i-1)
If Cells(i, 2) < Cells(i - 1, 2) And _
Cells(i, 3) < Cells(i - 1, 4) Then
' select the current row and cut it
Rows(i & ":" & i).Select
Selection.Cut
' select the above row insert the cut-rows
' making sure the current selection is moved down
Rows(i - 1 & ":" & i - 1).Select
Selection.Insert shift:=xlDown
' mark this flag to 1 so as to inform
' the caller function that reordering
' was performed
ReOrdered = 1
End If
Next i
ReOrderRows = ReOrdered
End Function
Try this out. Note that I have used only 4 rows + 1 header row and therefore the for loop goes from 3 to 5. You can change this code as you desire.
Related
I am trying to simplify a code in a Macro for numbering rows of specific columns of excel. Currently I am using
With Sheet1.Range("V6")
.Value = 1
.AutoFill .Resize(V6 + C, 1), xlFillSeries
End With
The macro has already set "C" as a variable that can change each time it is run. I want to simplify the code because I don't know how to loop this to repeat in every third column. I have tried For Loops but i am new to VBA and cannot get the program to run. Looping this would help me becasue I currently have this same code altered 85 differnt times to fill 85 different columns. For example, the next set is
With Sheet1.Range("Y6")
.Value = 1
.AutoFill .Resize(Y6 + C, 1), xlFillSeries
End With
Is there a more simple way this can be accomplished?
An alternate approach using Offset to fill every third column, starting in V6.
Sub MyNumbering()
Dim c As Long, i As Long
c = 100
For i = 0 To 84
With Sheet1.Range("V6").Offset(, i * 3)
.Value = 1
.AutoFill .Resize(c), xlFillSeries
End With
Next i
End Sub
This will iterate every 3rd column starting with column V and post 85 columns of row numbers starting in row 6 and ending at C + 6
Sub mynum()
Dim c As Long: c = 100
Dim j As Long
For j = 22 To 22 + 85 * 3 Step 3
With Sheet1.Range(Sheet1.Cells(6, j), Sheet1.Cells(c + 6, j))
.Formula = "=ROW(1:1)"
.Value = .Value
End With
Next j
End Sub
I just started to learn Excel VBA so bear with me here, I have a column of ID numbers and I already figured out how to generate numbers from 1 to the end of a list as follow 1,2,3,4 etc.
The problem is how can I generate a list of ID numbers like this 1,1,2,2,3,3 etc (row 1 and row 2 should have the same incremented number)
Here is what I did to increment numbers by 1 :
Sub AddingNbr()
Columns("A").Insert
Range("A1").Value = "ID"
For i = 1 To Range("B2", Range("B2").End(xlDown)).Count
Cells(i + 1, 1).Value = i
Next
End Sub
Can you try this?
Sub AddingNbr()
Columns("A").Insert copyorigin:=xlFormatFromRightOrBelow
Range("A1").Value = "ID"
For i = 1 To Range("B2", Range("B2").End(xlDown)).Count
Cells(i + 1, 1).Value = WorksheetFunction.Ceiling(CDec(i) / CDec(2), 1)
Next
End Sub
well all this is fine, but I have a nice way to do incrementation of values in excel with the one format we want. This formula really helps espacially when you want to use forms. That's the formula:
="LIVR"&TEXT(IF(A1=$A$1;0;MID(A1;5;3)+1);"000") it makes that
LIVR000
LIVR001
LIVR002
LIVR003
...
LIVR999
I would like to take values from column A and cut and paste them into column B, with each value exactly one cell to the left of their corresponding matching values from column C. Here is a before and after of what I would like to accomplish. Basically, each value from column A finds its match in column C and is copied, then pasted directly to the left of its match in column B.
Column A Column C
10 1
9 2
8 3
7 4
6 5
5 6
4 7
3 8
2 9
1 10
Column B Column C
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
Here it what I have tried:
Sub arrange()
Cells(1, 1).Activate
Do
If IsEmpty(ActiveCell) Then Exit Do
If ActiveCell.Offset(0, 2).Value = ActiveCell.Value Then
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Select
ActiveSheet.Paste
ActiveCell.Offset(1, -1).Activate
Else
ActiveCell.Offset(1, 0).Activate
End If
Loop
End Sub
The problem with this approach is that it only finds matching values in the same row. I want it to be able to search the entire column and place the value next to a match, whether the match is in the same row or not.
What you need to do is loop through column A and check to see if each value can be found in column C. If it is copy to column B.
Sub arrange()
Dim Wbk As Workbook
Set Wbk = ActiveWorkbook
Dim row As Integer
Dim col As Integer
Dim currentA As Integer
Dim numRows As Integer
Worksheets("chicoexcel").Activate
numRows = Wbk.Worksheets("chicoexcel").Range("A2", Range("A2").End(xlDown)).Rows.Count
For a = 1 To numRows + 1 'start at 1 because it is the first row. This will loop through Column A
currentA = Sheets("chicoexcel").Cells(a, "A").Value 'Save the current value in column A
For c = 1 To numRows + 1 'start at 1 because it is the first row. This will loop through Column C
If (Sheets("chicoexcel").Cells(c, "C").Value = currentA) Then 'Check if the col C value is equal to the current col A value.
Sheets("chicoexcel").Cells(c, "B") = Sheets("chicoexcel").Cells(c, "C").Value 'If so copy to column B
Sheets("chicoexcel").Cells(a, "A") = Null 'Remove the value from col A
End If
Next c
Next a
End Sub
I assumed all the values were integers, and that the data starts in row 1.
This is not the most efficient solution, but you'll be able to see what is happening using the debugger. You could include the FIND function to speed things up. I'll let you figure that out yourself.
I have tried, and been unable to find any sample VBA code that fits my needs. What I'm trying to do is find duplicate matches between two columns and consolidate them with respect to a third column, then in a fourth column show how many instances of the duplicate existed originally.
The original data:
The ideal output after removing duplicates:
As you can see, in the output I have 1 instance of 1 in Column A, a in Column B, retained the first value the duplicates started at in Column C and express 2 occurences of the duplicates in Column D. Can anyone point me in the right direction? Any help would be greatly appreciated.
the below code will find the number of occurrences in fourth column and remove the duplicates
Sub foo()
row_count = 20
For i = 2 To row_count
Count = 1
For j = 2 To row_count
If i <> j And Cells(i, 1).Value <> "" Then
If Cells(i, 1).Value = Cells(j, 1).Value And Cells(i, 2).Value = Cells(j, 2).Value Then
Rows(j & ":" & j).Delete Shift:=xlUp
Count = Count + 1
j = j - 1
End If
End If
Next j
If Count > 1 Then
Cells(i, 4).Value = Count
End If
Next i
End Sub
I want to copy the cells "A2:A" & patientprofiles + 1 and paste them in the first unused row in column D (i.e., there should be no blank cells between what's already in column D and what I want to paste there, but I also don't want to paste over what's already there). I then want to repeat this process a user-defined number of times (this variable will be called g1_observations). I then want to copy the cells "A" & patientprofiles + 2 & ":A" & 2 * patientprofiles + 1 to the new last used row in column D (i.e., taking into account that I've just pasted patientprofiles number of cells g1_observations number of times at the bottom of column D. I want to continue repeating this process a user-defined number of times (this number of times is defined by the variable numberofgrids).
For example: imagine that the user has defined that there will be three grids. Grid 1 will have 2 observations, Grid 2 will have 3 observations, and Grid 3 will have 4 observations. Also imagine that patientprofiles has been set to 40.
If this is the case, there will already be values in cells D1:D121, so I want to begin pasting in D122. I want to paste the cells A2:A41 (40 cells because patientprofiles = 40) to cells D122:D161; I want to paste the cells A42:A81 to cells D162:D201 and again to D:202:D241; and I want to paste cells A82:A121 to cells D242:D281, again to cells D282:D321, and again to cells D322:D361. I'm pasting each "grid" one less time than the number of observations for that grid, because the first group of observations for all grids is what's contained in cells D2:D121. End example
I'm pretty sure I need to use a nested For...Next loop in order to do this, but I'm having trouble with both the inner and outer loop. I think the outer loop should go something like this:
Dim i as long
For i = 0 to numberofgrids - 1
[insert inner loop here]
Next
As far as the inner loop goes, I'm not really sure what I'm doing because it keeps pasting over itself when I am pasting from two grids. The current code I have uses repeated For...Next loops and doesn't work:
Dim myLastRow as Integer
myLastRow = Worksheets("Work").UsedRange.Rows.Count
Dim j as Long
For j = 1 To g1_observations - 1
If j = 1 Then
Range(Cells(2, 1), Cells((patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells(j * myLastRow + 1, 4)
ElseIf j > 1 Then
Range(Cells(2, 1), Cells((patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells((j + 1) * (myLastRow / 2) + 1, 4)
Else: Range("A1").Select
End If
Next
For j = 1 To g2_observations - 1
If j = 1 Then
Range(Cells(patientprofiles + 2, 1), Cells((2 * patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells(j * myLastRow + 1, 4)
ElseIf b > 1 Then
Range(Cells(patientprofiles + 2, 1), Cells((2 * patientprofiles + 1), 1)).Copy _
Destination:=Worksheets("Work").Cells((b + 1) * (myLastRow / 2) + 1, 4)
Else: Range("A1").Select
End If
Next
It pastes over itself, and sometimes it skips lines. I can't really figure out how to reconcile myLastRow with a loop.
I think the inner loop should probably start off something like this:
Dim j as Long
For j = 0 to gj_observations - 1
Range(Cells(j * XXX + 2, 1), Cells((j + 1) * patientprofiles + 1).Copy _
Destination:=Worksheets("Work").Cells(myLastRow * j + 1) , 4
but I'm having difficulty because the variables are called g1_observations, g2_observations, g3_observations, etc., all the way up to g10_observations, and obviously gj_observations won't work. I want to loop on the number between "g" and "_", but I don't know how to get VBA to read variables that way, or if that's possible at all.
Can anyone help me out here? My mind is spinning from trying to understand the concept of loops, especially with different variables at each level.
Also, side question, how do you tell VBA to do nothing in an If statement? I currently have it selecting A1 by writing Else: Range("A1").Select, but I'm sure there's a better way of doing it.
When you're writing macros, it's a better practice to work with ranges and avoid manipulating cells one at a time in a loop. Your macro will run much faster and the code will be clearer.
If you want to create a set of variables that you can access by number, you would use something called an array. This is a pretty fundamental concept that exists in almost every programming language, so I'll refer you to MSDN or your favorite VBA language reference guide for more details.
Dim ws As Worksheet
Dim lr As Long ' Last Row
Dim szpp As Long ' Size (rows) patient profiles
Dim szgobsrv(2) As Long ' Size (rows) observation groups
Dim i As Long
Dim j As Long
Dim SourceCells As Range
Dim TargetCell As Range
Set ws = Sheets("Work")
szpp = 40
szgobsrv(0) = 1
szgobsrv(1) = 2
szgobsrv(2) = 3
For i = 0 To UBound(szgobsrv)
lr = ws.UsedRange.Row + ws.UsedRange.Rows.Count - 1
' copy the patient profile cells multiple times depending on group size
For j = 0 To szgobsrv(i) - 1
Set SourceCells = ws.[A2].Offset(i * szpp).Resize(szpp)
Set TargetCell = ws.[D1].Offset(lr + j * szpp)
SourceCells.Copy TargetCell
Next
Next
Note the usage of the Resize and Offset methods. These are helpful Range methods that can change the size and position of a range by a fixed amount.
The main problem you are having with values being over written is that youre not using Offset.
Another important thing to remember about nested loops is that the nested loop runs i times per loop of the upper level loop. I am thinking that nested loops here might not be good for you. You could probably just make them all independent loops?
If you want to loop to the number contained within the variable you might want to set that variable equal to a number.
example:
g2_observations =2
For j = 1 To g2_observations - 1
Aside from this I am actuall yhaving difficulty understanding what you need, but hopefully this helps?
numberofgrids = input
i = 1 to numberofgrids
gridCount = gridCount + 1
'Loop Stuff
Case Select gridCount
Case is = 1
'logic
Case is = 2
'logic
Etc etc
End Select
If numberofgrids = gridCount Then
Exit For
End If
Next i