VBA- Copying Values from one cell to another offset cell - excel

I am trying to go through row 6 and from column 1 to 26 and search for the sentence Earned Cumulative Hours. Once that is done then I am trying to go from row 8 to the last row(30 in this case) for the column that has Earned Cumulative Hours in row 6.
Then I am trying to paste the values of the cells from this column to 2 cells left in the same row. But I keep getting errors and the code doesn't work.
Can someone please point me in the right direction ? Thanks
Sub project()
Dim lastrow As Long
Dim i As Long
Dim j As Long
lastrow = Sheets("Progress").Cells(Rows.Count, 26).End(xlUp).Row
For j = 1 To 26
If Cells(6, j) = "Earned Cumulative Hours" Then
For i = 8 To lastrow
Cells(i, j).Copy
Cells(i, j).Offset(0, -2).Select
Selection.PasteSpeical Paste:=xlPasteValues
Next i
End If
Next j
End Sub

There are a few problems I can see straight away with your code. Firstly if you are offsetting back two columns .Cells(i, j).Offset(0, -2) then you will be overwriting existing values. If this is what you intend to do then weird but ok.
The next issue is that you have a problem if 'Earned Cumulative Hours' is in Column A. If this is your case Excel will be most unhappy trying to offset two columns to the left and will give an error.
In this case instead of copying and pasting it will be more efficient to set values in one column to the other which you can see in my code. Finally, your Cell references will be valid for the active sheet only. You need to qualify what worksheet you interest in as shown in my code. I normally put this at the start of the code if it is a self contained block.
You could also eliminate the i loop and set ranges of values at a time but we'll save that for next time!
I haven't test this code but it should be fine.
Sub projectawesome()
Dim lastrow as Long, i as Long, j as Long
'Qualify the sheet (assuming its in the activeworkbook)
With Sheets("Progress")
lastrow = .Cells(.Rows.Count, 26).End(xlUp).Row
'I've changed this to column three to prevent offset errors.
For j = 3 to 26
If .Cells(6, j) = "Earned Cumulative Hours" Then
For i = 8 to lastrow
'Assuming overwriting data is ok.
'No need to copy and paste
.Cells(i, j - 2).Value = .Cells(i, j).Value
Next i
End If
Next
End With
End Sub

Try this and we can get rid of those selects
Sub project()
Dim lastrow As Long
Dim i As Long
Dim j As Long
lastrow = Sheets("Progress").Cells(Rows.Count, 26).End(xlUp).Row
For j = 1 To 26
If Cells(6, j) = "Earned Cumulative Hours" Then
For i = 8 To lastrow
Cells(i, j).Copy
With Cells(i, j)
.Offset(0, -2).PasteSpecial xlPasteValues
End With
Next i ' next row
End If
Next j ' next col
End Sub

Related

Check if consecutive rows have identical value, if not insert new row between them

i am looking for a solution to my problem. The task is to compare two consecutive rows with each other,in the range from column D1 to the last written cell in Column D. If the value of a consecutive cell is equal to the value of the previous cell , i.e. D2=D1, the macro can go next, else it shall insert a new row between the two values. Since i am fairly new to vba and mostly use information based on online research, i could not find a fitting solution til now. Below you can see a part of what tried.
Sub Macro()
'check rows
Dim a As Long
Dim b As Long, c As Long
a = Cells(Rows.Count, "D").End(xlUp).Row
For b = a To 2 Step -1
c = b - 1
If Cells(b, 4).Value = Cells(c, 4).Value Then
If Cells(b, 4).Value <> Cells(c, 4).Value Then
Rows("c").Select
Selection.Insert Shift:=xlDown
End If
End If
Next b
End Sub
Sub InsertRows()
Dim cl As Range
With ActiveSheet
Set cl = .Cells(.Rows.Count, "D").End(xlUp)
Do Until cl.Row < 2
Set cl = cl.Offset(-1)
If cl.Value <> cl.Offset(1).Value Then cl.Offset(1).EntireRow.Insert
Loop
End With
End Sub
Side note. You can benefit from reading How to avoid using Select in Excel VBA

How to copy data from a cell in sheet1 to sheet2, looping through each cell?

How can I cycle through sheet1 to see if there is data in that cell?
If there is no data then go to the next cell.
If there is data in the next cell paste it into sheet2.
The criteria are:
I cannot use a set range it will change as the data changes in sheet1.
I can keep sheet names a constant such as sheet1 and sheet2.
I found a way using columns and or rows yet that code has a major issue. If there is no starting data in the first cell it will not copy anything in the entire row and or column.
I am posting the code I worked with to check the data in columns but if there is no starting data it will skip the whole row.
Sub CopytoImport()
Dim wb As Workbook
Dim iCol As Long
Dim ws As Worksheet
Sheets("sheet2").Cells.ClearContents
' Loop through the column
For iCol = 1 To 22 ' Call out columns I cannot set this every time it should look threw all cells
With Worksheets("sheet1").Columns(iCol)
' Check tht column is not empty
If .Cells(1, 1).Value = "" Then
'Nothing in this column
'Do nothing
Else
' Copy the coumn to te destination
Range(.Cells(1, 1), .End(xlDown)).Copy _
Destination:=Worksheets("sheet2").Columns(iCol).Cells(1, 1)
End If
End With
Next iCol
ActiveWorkbook.Save
End Sub
Function runcode()
Call CopytoImport
End Function
Cells(1, 1) is just RANGE.("A1") you are only operating on this cell in your code. You would need Cells(1, iCol) to account for what column you are on during your loop.
You might also need a nested loop since you are looping through rows as well. The basic outline of a nested loop is as follows. Note the Cells(1,1) is replaced with the i and j representing what row and what column we are on. This might not be the fastest way to achieve the results you want but it sounds like this is what you are asking for help with. You will also need to define a lastrow (with a + 1 at the end to get the next blank cell) in your Sheet2 for when you paste the data. You would put this right under where the loop starts going through rows. This is so the lastrow of your sheet2 is recalculated each time data is being moved to that sheet. I am not going to re-write your code since you stated it is not complete but here is an example that should help you.
For j = 5 To lastcolumn
For i = 5 To lastrow
Dim lastrow2 As Long
lastrow2 = Worksheets(1).Range("A" & Rows.Count).End(xlUp).Row + 1
If Worksheets(2).Cells(i, j).Value <> 0 Then
Worksheets(1).Range("C" & lastrow2).Value = Worksheets(2).Cells(i, j).Value
Worksheets(1).Range("B" & lastrow2).Value = Worksheets(2).Cells(2, j).Value
End If
Next i
Next j
To find your lastrow:
dim lastrow as long
lastrow = Range("A" & rows.count).End(xlup).Row ' or whatever column contains the data
To find your last column
Dim lastcolumn As Long
lastcolumn = Worksheets(2).Cells(2, Columns.Count).End(xlToLeft).Column

How to limit the value of one cell's value by another cell's value?

I have a table full of numbers. I need a macro script, which will compare the target value with the score value. In case of the score value is higher than the target value, then change the target value to the score value.
Can somebody help me?
(https://imgur.com/a/xmej0fi)
I have tried to apply the following code on one cell, but it didn't work.
Sub check_value()
If Cells(8, 23).Value < Cells(8, 24).Value Then
Cells(8, 23).Value = Cells(8, 24).Value
End If
End Sub
I think this may help you:
Option Explicit
Sub test()
Dim LastRow As Long, Row As Long, Target As Long, Score As Long, LastColumn As Long, Column As Long
With ThisWorkbook.Worksheets("Sheet1")
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
For Column = 2 To LastColumn
For Row = 2 To LastRow Step 2
Target = .Cells(Row, Column).Value
Score = .Cells(Row + 1, Column).Value
If Score > Target Then
.Cells(Row, Column).Value = Score
End If
Next Row
Next Column
End With
End Sub
F2 and F3 have a scenario where the Target < Score, so we can compare the values (similar to what you've got)
if cells(2,6).value < cells(3,6) then cells(2,6).value = cells(3,6).value
My only correction from your tested code is using rows to compare, rather than columns, which appears to be how each Skill is being assessed (e.g., Target for Skill 5 is 5 (F2), while the Score is 7 (F3)).
Note that you can use a loop to get through the whole data set by finding last column (lc) and last row (lr), such that:
For j = 2 to lc 'columns
for i = 2 to lr step 2 'rows with a step of 2 so you can do sets of score/target
'do stuff
next i
next j

Improve performance in Excel using VBA?

We have a single formula which we are coping to over to a defined range of over 250'000 cells. The performance of Excel clearly takes a hit. Is there a way to improve the performance by using VBA?
The formula returns either 0 or 1 as a value to the cell depending on 4 criteria. The Excel formula is:
=IF(NOT(ISTEXT($B9)),"",IF((L$5=""),"",IF(AND(M$5>MIN($G9,$H9),L$5<MAX($G9,$H9)),1,0)))
Thanks for your help !
Something like this could be an alternative to 250,000 rows of formulas. As stated in the comments, this still would take some time given the size of the data set. I ran a test with a sheet that just had the necessary cells populated with 249,488 rows and the code took 12 seconds to run. With more data in the sheet I anticipate it taking longer than that.
That said this will reduce the memory of your file significantly since there won't be any formulas:
Sub Run()
Dim i As Long 'Row number for loop
Dim lRow As Long 'Last row of data set
Dim ms As Worksheet
Set ms = Sheets("Sheet1") 'Change to whatever sheet you need this in
With ms
If .Cells(5, 12).Value = "" Then
MsgBox "Please enter a value in Cell L5 before proceeding."
Else
lRow = .Cells(.Rows.Count, 2).End(xlUp).Row 'This is assuming Column B is populated in full to the bottom of the data set
For i = 9 To lRow 'This is assuming you will be starting the calculation in row 9
If IsNumeric(.Cells(i, 2).Value) = False And .Cells(i, 2).Value <> "" Then 'Ensuring Column B is text and not blank
If .Cells(5, 12).Value < WorksheetFunction.Max(.Cells(i, 7).Value, .Cells(i, 8).Value) And .Cells(5, 13).Value > WorksheetFunction.Min(.Cells(i, 7).Value, .Cells(i, 8).Value) Then
.Cells(i, 1).Value = 1 'Assuming you want the 0 or 1 in Column A
Else
.Cells(i, 1).Value = 0 'Assuming you want the 0 or 1 in Column A
End If
End If
Next i
End If
End With
End Sub
EDIT
Per Cornintern's awesome suggestion, I've rewritten this to use arrays instead of looping through the entire range. This now takes less than 2 seconds:
Sub Run()
Dim i As Long 'Row number for loop
Dim lRow As Long 'Last row of data set
Dim ms As Worksheet
Dim mVar1() As Variant
Dim mVar2() As Variant
Dim mVar3() As Variant
Dim rVar() As Variant
Dim num1 As Long
Dim num2 As Long
Set ms = Sheets("Sheet1") 'Change to whatever sheet you need this in
With ms
If .Cells(5, 12).Value = "" Then
MsgBox "Please enter a value in Cell L5 before proceeding."
Else
lRow = .Cells(.Rows.Count, 2).End(xlUp).Row 'This is assuming Column B is populated in full to the bottom of the data set
ReDim rVar(1 To lRow - 8) As Variant
mVar1 = .Range("G9:G" & lRow)
mVar2 = .Range("H9:H" & lRow)
mVar3 = .Range("B9:B" & lRow)
num1 = .Cells(5, 12).Value
num2 = .Cells(5, 13).Value
For i = 1 To UBound(mVar1) 'This is assuming you will be starting the calculation in row 9
If IsNumeric(mVar3(i, 1)) = False And mVar3(i, 1) <> "" Then 'Ensuring Column B is text and not blank
If num1 < WorksheetFunction.Max(mVar1(i, 1), mVar2(i, 1)) And num2 > WorksheetFunction.Min(mVar1(i, 1), mVar2(i, 1)) Then
rVar(i) = 1
Else
rVar(i) = 0
End If
End If
Next i
End If
End With
Range("A9:A" & lRow) = WorksheetFunction.Transpose(rVar)
End Sub
Given that your formula is simple I would expect that the formula approach would calculate faster/better than VBA:
Excel calculates using multiple cores: VBA only uses 1
The overhead of transferring data to VBA and back to Excel is
substantial
Excel can calculate over a million simple formulas per second
Excel can automatically recalculate efficiently if any of the data
changes, but you would have to rerun the entire VBA sub.
I would recommend seeing how long the formula approach takes in practice: I would be surprised if it calculates in more than a second.

Delete rows which are not formatted as dates

In my column A I have cells which consists of formatted dates and general formatting. I want to delete rows which are not dates, and I've made this code, but I have to run it multiple times to get it to delete all the rows which aren't dates.
Code:
Sub del_row_not_date()
Dim rng_A_last As Long
Dim i As Integer
rng_A_last = Sheet1.Cells(Sheet1.Rows.Count, "A").End(xlUp).Row
Debug.Print rng_A_last
For i = 1 To rng_A_last Step 1
If IsDate(Sheet1.Cells(i, 1)) = False Then
Sheet1.Cells(i, 1).EntireRow.Delete
End If
Next
End Sub
Thanks in advance!
Since rows will
Based on this LINK I found this:
a tip about deleting rows based on a condition If you start at the top and work down, every time you delete a row your counter will effectively move to the cell two rows below the row you deleted because the row immediately below the deleted row moves up (i.e. it is not tested at all).
This works :)
Sub del_row_not_date()
Dim rng_A_last As Long
Dim i As Integer
rng_A_last = Sheet1.Cells(Sheet1.Rows.Count, "A").End(xlUp).Row
Debug.Print rng_A_last
For i = rng_A_last To 1 Step -1
'Debug.Print Cells(i, 1).Value
If IsDate(Sheet1.Cells(i, 1)) = False Then
Sheet1.Cells(i, 1).EntireRow.Delete
End If
Next
End Sub
you could try
Sub del_row_not_date()
With Sheet1
.Range("A1", .Cells(.Rows.Count, "A").End(xlUp)).SpecialCells(xlCellTypeConstants, xlTextValues).EntireRow.Delete
End With
End Sub
what above deletes all column "A" not-numbers cells entire row
if you have cells with numbers that are not dates then use:
Option Explicit
Sub del_row_not_date()
Dim i As Integer
With Sheet1
For i = .Cells(.Rows.Count, "A").End(xlUp).Row To 1 Step -1 '<--| loop backwards not to loose next row index after deleting the current one
If Not IsDate(.Cells(i, 1)) Then .Cells(i, 1).EntireRow.Delete
Next i
End With
End Sub

Resources