Copy data cell down or up - vba

I am a noob in vba. However, I would like to implement the following use case to make my cooperate life a lot of easier.
I have the following data:
I would like to copy the first row down until I hit a filled field, the second row also down until I hit a filled field, the third row up and down and the 4th row up.
This is how I would like to have my result sheet should look like.
Any recommendation how to implement this use case in vba?
I appreciate your replies!

can you please try this one? This routine is assuming column G as main start point and checks if column A,B,C,D is empty or not in the same row and fill it up accordingly.
Sub ASD()
Dim lastRow As Long
lastRow = Range("G" & Rows.Count).End(xlUp).Row
For Each c In Range("G:G")
If c.Value <> "" Then
If c.Offset(0, -3).Value = "" Then
c.Offset(0, -3).Value = c.Offset(0, -3).End(xlDown).Value
End If
If c.Offset(0, -4).Value = "" Then
c.Offset(0, -4).Value = c.Offset(0, -4).End(xlUp).Value
End If
If c.Offset(0, -5).Value = "" Then
c.Offset(0, -5).Value = c.Offset(0, -5).End(xlUp).Value
End If
If c.Offset(0, -6).Value = "" Then
c.Offset(0, -6).Value = c.Offset(0, -6).End(xlUp).Value
End If
End If
Next c
End Sub
Only problem is it do not do anything if the reference cell is empty in the column G. I think you don't need that anyway.

Hope this help.
It will run on your current selection
Copy the whole code into a module, run the fill_down() to fill down, fill_up() to fill up.
'======================
'******Filling*********
'======================
Sub fill_up()
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.FormulaR1C1 = "=R[1]C"
End Sub
Sub fill_down()
Call copy_last(Selection)
Call filling
End Sub
Function copy_last(r As range)
Dim arr() As Variant
Dim x As Double
Dim arr_size As Double
arr = r
arr_size = UBound(arr, 1)
For x = arr_size To 1 Step -1
If Not isempty(arr(x, 1)) Then
Exit For
End If
Next x
r(r.Row, 1) = arr(x, 1)
End Function
Function filling()
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.FormulaR1C1 = "=R[-1]C"
End Function
'=======================
'******End filling******
'=======================

Related

How to select cells NOT containing a certain word

I have an Excel table, in which some of the cells of the C column contain the word "Rinse" (the other cells have various other contents).
Using VBA code, here's how I would select all rows containing the word "Rinse" in the C column - this code works fine.
For i = 3 To 300
If Cells(i, 3).Value = "Rinse" Then
Rows(i).Select
Selection.FormatConditions.Delete
End If
Next
However, I want to do exactly the opposite, namely to select all rows NOT containing the word "Rinse" in the C column. I've tried the following, but it doesn't work.
For i = 3 To 300
If Cells(i, 3).Value = Not "Rinse" Then
Rows(i).Select
Selection.FormatConditions.Delete
End If
Next
How do I get this done?
Use the Instr function, like this:
If Instr(Cells(i, 3).Value, "Rinse") = 0 Then
Change this line of your code ( <> not equals to)
If Cells(i, 3).Value <> "Rinse" Then
The Like operator can be useful here:
If Not Cells(i, 3).Value Like "*Rinse*" Then
If "Rinse" can be found anywhere in your cell value
You could filter out the Rinse values and then select the visible cells.
Could be quicker than looking at each individual cell.
Public Sub Test()
Dim lRow As Long
With ThisWorkbook.Worksheets("Sheet1")
lRow = .Cells(.Rows.Count, 3).End(xlUp).Row
With .Range(.Cells(1, 3), .Cells(lRow, 3))
.AutoFilter Field:=1, Criteria1:="<>*Rinse*"
'Can replace Select in next row with .FormatConditions.Delete
.SpecialCells(xlCellTypeVisible).Select
End With
.ShowAllData
End With
End Sub
The advantage of this code is in its speed. Acceleration is achieved by referencing the sheet only once for every row and only once for the result, and by formatting only the used range columns instead of the entire rows.
Private Sub SelectNonContiguousRange()
Dim RngAddress() As String
Dim i As Long
Dim R As Long
ReDim RngAddress(300) ' this number should be
With ActiveSheet
For R = 3 To 300 ' equal to this number
' use = (equal) or <> (unequal) as required:
If .Cells(R, "C").Value <> "Rinse" Then
' If .Cells(R, "C").Value = "Rinse" Then
RngAddress(i) = .Range(.Cells(R, "A"), _
.Cells(R, .UsedRange.Columns.Count)).Address
i = i + 1
End If
Next R
ReDim Preserve RngAddress(i - 1)
.Range(Join(RngAddress, ",")).FormatConditions.Delete
End With
End Sub
BTW, you can use a variation of this code to select multiple rows simultaneously (like you can do with Ctl+Click), for example all rows containing the word "Rinse".
#Renee - Change the if condition line as shown below.
For i = 3 To 300
If Cells(i, 3).Value <> "Rinse" Then
Rows(i).Select
Selection.FormatConditions.Delete
End If
Next

userform using variable of a commandbutton

I have a problem that I can not solve ... my problem is this:
I need to create a User Form that performs data search within 4 sheets, this data is the same only changing from year to year in the spreadsheets, but when I try to link my CommandBotton and use the variable in the error combo box ...
The business rule is as follows:
The User enters the employee's enrollment in this automatically pulls the data to the fields of the User Form, in case he wanted to change the worksheet he used the combo box to change between those worksheets and executing the same search but in different worksheets.
Public plan As Worksheet
Sub ComboBox1_Change()
Sheets(ComboBox1.ListIndex + 1).Activate
End Sub
Sub UserForm_Initialize()
For Each plan In ActiveWorkbook.Worksheets
ComboBox1.AddItem plan.Name
Next plan
End Sub
Sub bnt1_Click()
With ThisWorkbook.Sheets(plan).Range("A:A")
Set c = .Find(textCp.Value, LookIn:=xlValues, lookat:=xlPart)
If Not c Is Nothing Then
c.Activate
textCp.Value = c.Value
textName.Value = c.Offset(0, 1).Value
textAd.Value = c.Offset(0, 2).Value
text60.Value = c.Offset(0, 65).Value
text60_20.Value = c.Offset(0, 66).Value
text100.Value = c.Offset(0, 67).Value
text100_20.Value = c.Offset(0, 68).Value
textAdc.Value = c.Offset(0, 69).Value
textAdcT.Value = c.Offset(0, 70).Value
End If
End With
End Sub
Sub btnSair_Click()
Unload FormPes
End Sub
Simply use With plan.Range("A:A").
Here is how I would write the code. I fell like With c.EntireRow would make it easier to identify which columns that you are referring to, as opposed to c.Offset(0, 65).Value.
Sub bnt1_Click()
With plan.Range("A:A")
Set c = .Find(textCp.Value, LookIn:=xlValues, lookat:=xlPart)
If Not c Is Nothing Then
With c.EntireRow
textCp.Value = .Value
textName.Value = .Range("B1").Value
textAd.Value = .Range("C1").Value
text60.Value = .Range("BN1").Value
text60_20.Value = .Range("BO1").Value
text100.Value = .Range("BP1").Value
text100_20.Value = .Range("BQ1").Value
textAdc.Value = .Range("BR1").Value
textAdcT.Value = .Range("BS1").Value
End With
End If
End With
End Sub

VBA macro run-time too long

I have written several subs which are then called from a main sub. Individual subs run very quickly, most are instantaneous (the DoFind sub takes a few seconds to run due to the large amounts of data in the table) however when I run the main sub it takes up to a minute to execute. Any ideas/tips on why this is the case?
Note, I haven't had much experience with VBA (all has been learnt in the past week). There are other macros used, but they are not shown since even the test sub takes approximately 1 minute
Sub DoFind()
Dim i As Long
i = 1
Do While Sheets("Temp").Cells(i, "A").Value <> Empty
Dim BearingArray(6) As String
BearingArray(0) = Sheets("Temp").Cells(i, "A").Value
BearingArray(1) = Sheets("Temp").Cells(i, "B").Value
BearingArray(2) = Sheets("Temp").Cells(i, "C").Value
BearingArray(3) = Sheets("Temp").Cells(i, "D").Value
BearingArray(4) = Sheets("Temp").Cells(i, "E").Value
BearingArray(5) = Sheets("Temp").Cells(i, "F").Value
BearingArray(6) = Sheets("Temp").Cells(i, "G").Value
With Sheets("Calculations")
.Cells(17, "K").Value = BearingArray(0)
.Cells(19, "O").Value = BearingArray(1)
.Cells(20, "O").Value = BearingArray(2)
.Cells(23, "O").Value = BearingArray(3)
.Cells(22, "O").Value = BearingArray(4)
.Cells(26, "O").Value = BearingArray(5)
.Cells(17, "L").Value = BearingArray(6)
End With
i = i + 1
If Sheets("Calculations").Cells(17, "M").Value = "PASS" Then
Exit Do
Else
End If
Loop
If Sheets("Temp").Cells(i, "A").Value = Empty Then
MsgBox "No available bearing."
End If
End Sub
Sub Create_Sheet_Temp()
ThisWorkbook.Sheets.Add
ActiveSheet.Name = "Temp"
' This creates a new worksheet called "Temp"
End Sub
Sub Copy_Paste()
Dim NewTable As ListObject
Sheets("Calculations").Activate
Set NewTable = Sheets("Calculations").ListObjects("Full_Bearings_List")
NewTable.Range.SpecialCells(xlCellTypeVisible).Select
NewTable.DataBodyRange.SpecialCells(xlCellTypeVisible).Copy
Sheets("Temp").Range("A1").PasteSpecial xlPasteAll
Application.CutCopyMode = False
'This sub copies all visible cells from a filtered table and pastes them to the new sheet called "Temp"
End Sub
Sub test()
Create_Sheet_Temp
Copy_Paste
DoFind
End Sub
You can speed up your code by storing the worksheets in variables (ahead of the loop).
Dim TempWS as Worksheet
Dim CalcWS as Worksheet
set tempws= Sheets("Temp")
set CalcWS=Sheets("Calculations")
Also declare the array outside of the loop. Also Id recommend to use numeric column index.
Sheets("Temp").Cells(i, "G").Value
to
TempWS.Cells(i, 7).Value
Comparing against Empty is not always the best choice, try
... <> ""
EDIT:
For the Copy try t use the destination parameter of the Copy method. Example from the help:
Worksheets("Sheet1").Range("A1:D4").Copy _
destination:=Worksheets("Sheet2").Range("E5")

Compare 4 columns in one excel sheet using vba

I need your help please, I have 4 columns in an excel sheet and I need to compare them 2 by 2 i will explain to you :
In column A i have users(user1,user2,user3 ...)
In column B i have functionalities ( fonc1, fonc2, fonc3.....)
In column C i have users(user1,user2,user3 ...)
In column D i have functionalities ( fonc1, fonc2, fonc3.....)
The columns C and D are a new version of columns A and B in the columns C and D the users may change order or change functionalities .
When i execute my code i put the result in other new columns:
column F where i have the users
column G where i put the Deleted_functionalities
column H where i put the New_functionalities
The first problem is that the code doesn't get the users it get only the new and deleted functionalities. The second problem is that when the column A is more than column C where the users are stocked the code doesn't work. Can you please help me to find a solution? Thank you in advance .
Here is my code and the file I am working on :
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("B2:B2000")
If WorksheetFunction.CountIf(Range("D2:D2000"), rngCell) = 0 Then
Range("G" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
For Each rngCell In Range("D2:D2000")
If WorksheetFunction.CountIf(Range("B2:B2000"), rngCell) = 0 Then
Range("H" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
End Sub
and this is the excel file
http://www.cjoint.com/c/FCxnwjp22rv
try this
Private Sub CommandButton2_Click()
Dim ws As Worksheet
Dim cell As Range, funcCell As Range
Dim oldUserRng As Range, newUserRng As Range, reportRng As Range
Dim iReport As Long
Dim oldFunc As String, newFunc As String
Set ws = ThisWorkbook.Worksheets("users") '<== adapt it to your needs
With ws
Set oldUserRng = .Columns(1).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set newUserRng = .Columns(3).Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeConstants, xlTextValues)
Set reportRng = .Range("F1:I1") '<== added one report column to account for unchanged functions
End With
reportRng.Value = Array("user", "deleted", "new", "same")
iReport = 1
For Each cell In oldUserRng
With cell
oldFunc = .Offset(, 1).Value
Set funcCell = FindAndOffset(newUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", "", oldFunc)
Else
newFunc = funcCell.Value
If newFunc = oldFunc Then
reportRng.Offset(iReport) = Array(.Value, "", "", newFunc)
Else
reportRng.Offset(iReport) = Array(.Value, oldFunc, newFunc, "")
End If
End If
iReport = iReport + 1
End With
Next cell
For Each cell In newUserRng
With cell
Set funcCell = FindAndOffset(oldUserRng, .Value, 1)
If funcCell Is Nothing Then
reportRng.Offset(iReport) = Array(.Value, "", .Offset(, 1).Value, "")
iReport = iReport + 1
End If
End With
Next cell
End Sub
Not so sure it does what you need.
you'd better provide screenshots of "before" and "after" scenarios.
BTW, is it safe to assume that both old and new user columns cannot hold duplicates (i.e.: two or more "userX" in column A and/or column C?)
But it does speed up thing considerably since it iterates only through non empty cells.
I hope I get what you want to achieve. Does the following solve your problem?
Private Sub CommandButton2_Click()
Dim rngCell As Range
For Each rngCell In Range("A2:A20000")
If WorksheetFunction.CountIf(Range("C2:C20000"), rngCell) > 0 Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = Application.WorksheetFunction.VLookup(rngCell.Value, Range("C2:D20000"), 2, 0)
ElseIf (rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 1) = rngCell.Offset(0, 1).Value
End If
Next
For Each rngCell In Range("C2:C20000")
If (WorksheetFunction.CountIf(Range("A2:A20000"), rngCell) = 0 And rngCell <> "") Then
Range("F" & Rows.Count).End(xlUp).Offset(1) = rngCell
Range("F" & Rows.Count).End(xlUp).Offset(0, 2) = rngCell.Offset(0, 1).Value
End If
Next
End Sub
A user is only included in column F when he appears both in columns A and C.In case you want to include every user that is either in column A or C the code has to be altered.

Concatenate Columns Of Data

*Edited To Add: Current error I'm receiving. See bottom of this post for screenshot.
I have text in column D. The macro should find blank cells, and then concatenate the text from all cells below it.
Example
Text starting in D2, displaying like this...
Blank Cell
SampleText1
SampleText2
SampleText3
Blank Cell
SampleText4
SampleText5
SampleText6
The macro should display the text in D2...
SampleText1, SampleText2, SampleText3
and then in D6, like this...
SampleText4, SampleText5, SampleText6
..and so on.
This only needs to work in column D, so I'm guessing I can write it to that range.
The closest answer I've come across is here:
Excel Macro to concatenate
Here is the code I'm currently working with...
Sub ConcatColumns()
Do While ActiveCell <> "" 'Loops until the active cell is blank.
'The "&" must have a space on both sides or it will be
'treated as a variable type of long integer.
ActiveCell.Offset(0, 1).FormulaR1C1 = _
ActiveCell.Offset(0, -1) & " " & ActiveCell.Offset(0, 0)
ActiveCell.Offset(1, 0).Select
Loop
End Sub
Edit: Now using great code from #jeeped but receiving an error, seen in the below screenshot
Start from the bottom and work up, building an array of the strings. When you reach a blank cell, Join the strings using your preferred deliminator.
Sub build_StringLists()
Dim rw As Long, v As Long, vTMP As Variant, vSTRs() As Variant
Dim bReversedOrder As Boolean, dDeleteSourceRows As Boolean
ReDim vSTRs(0)
bReversedOrder = False
dDeleteSourceRows = True
With Worksheets("Sheet4")
For rw = .Cells(Rows.Count, 1).End(xlUp).Row To 1 Step -1
If IsEmpty(.Cells(rw, 1)) Then
ReDim Preserve vSTRs(0 To UBound(vSTRs) - 1)
If Not bReversedOrder Then
For v = LBound(vSTRs) To UBound(vSTRs) / 2
vTMP = vSTRs(UBound(vSTRs) - v)
vSTRs(UBound(vSTRs) - v) = vSTRs(v)
vSTRs(v) = vTMP
Next v
End If
.Cells(rw, 1) = Join(vSTRs, ", ")
.Cells(rw, 1).Font.Color = vbBlue
If dDeleteSourceRows Then _
.Cells(rw, 1).Offset(1, 0).Resize(UBound(vSTRs) + 1, 1).EntireRow.Delete
ReDim vSTRs(0)
Else
vSTRs(UBound(vSTRs)) = .Cells(rw, 1).Value2
ReDim Preserve vSTRs(0 To UBound(vSTRs) + 1)
End If
Next rw
End With
End Sub
I've left options for reversing the string list as well as removing the original rows of strings.
                  Before build_StringLists procedure
                  After build_StringLists procedure