Hi I am trying to update cell values on all rows until the row number changes. Here is my code:
Sub MyLoop()
Dim i As Integer
Dim var As String
Dim LastRow As Long
LastRow = Range("A" & Rows.Count).End(xlUp).Row
i = 1
var = Cells(i, 4).Value
For i = 1 To LastRow
If Range("A" & i).Value = "1" Then
Cells(i, 2).Value = var
End If
var = Cells(i, 4).Value
Next i
End Sub
I have attached before and after images of how it should look once routine has been ran. Basically Loop through all rows and in column A is the number changes store the value in column D and paste it into column B until the row number changes.
Before:
After:
Kind Regards
Is it really when the number changes or when the word in Column D changes?
Columns("D:D").Cut Destination:=Columns("B:B")
Range("B1:B" & Cells(Rows.Count, "A").End(xlUp).Row).SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
Range("B1:B" & Cells(Rows.Count, "A").End(xlUp).Row).Value = Range("B1:B" & Cells(Rows.Count, "A").End(xlUp).Row).Value
Sub MyLoop()
Dim i As Integer
Dim var As String
Dim LastRow As Long
LastRow = Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To LastRow
IF Cells(i, 4).Value<>"" Then 'Get new value from column 4
var = Cells(i, 4).Value
End If
Cells(i, 2).Value = var 'Assign value to column 2
Next i
End Sub
Fill Column
A Quick Fix
Sub MyLoop()
Dim LastRow As Long
Dim i As Long
Dim A As Variant
Dim D As Variant
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To LastRow
If Cells(i, 1).Value <> A Then
A = Cells(i, 1).Value
D = Cells(i, 4).Value
End If
Cells(i, 2).Value = D
Next i
End Sub
A More Flexible Solution
Adjust the values in the constants section.
Option Explicit
Sub fillColumn()
' Define constants.
Const wsName As String = "Sheet1"
Const ColumnsAddress As String = "A:D"
Const LookupCol As Long = 1
Const CriteriaCol As Long = 4
Const ResultCol As Long = 2
Const FirstRow As Long = 2
' Define Source Range.
Dim rng As Range
With ThisWorkbook.Worksheets(wsName).Columns(ColumnsAddress)
Set rng = .Columns(LookupCol).Resize(.Rows.Count - FirstRow + 1) _
.Offset(FirstRow - 1).Find( _
What:="*", _
LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
If rng Is Nothing Then
Exit Sub
End If
Set rng = .Resize(rng.Row - FirstRow + 1).Offset(FirstRow - 1)
End With
' Write values from Source Range to Data Array.
Dim Data As Variant: Data = rng.Value
' Define Result Array.
Dim Result As Variant: ReDim Result(1 To UBound(Data, 1), 1 To 1)
' Declare additional variables.
Dim cLookup As Variant ' Current Lookup Value
Dim cCriteria As Variant ' Current Criteria Value
Dim i As Long ' Rows Counter
' Write values from Data Array to Result Array.
For i = 1 To UBound(Data, 1)
If Data(i, LookupCol) <> cLookup Then
cLookup = Data(i, LookupCol)
cCriteria = Data(i, CriteriaCol)
End If
Result(i, 1) = cCriteria
Next i
' Write from Result Array to Destination Column Range.
rng.Columns(ResultCol).Value = Result
End Sub
Related
I have a spreadsheet that has values that looks similar to below :
Is there any possible way to create VBA to join all the separate data together for each ID and Class into one row? So that the ending result would look like below?
Sub JoinRowsData()
Dim lastRow As Long, i As Long, j As Long, k As Long
Application.ScreenUpdating = False
lastRow = Range("C" & Rows.Count).End(xlUp).Row
For i = 2 To lastRow
For j = i + 1 To lastRow
If Cells(i, 2) = Cells(j, 2) Then
For k = 5 To 10
If (Cells(i, k) = "" And Cells(j, k) <> "") Then
Cells(i, k) = Cells(j, k)
End If
Next
End If
Next
Next
Application.ScreenUpdating = True
End Sub
The following will do it. See the comments for an explanation how it works. It uses arrays to process the data which is much faster than process cells directly.
Option Explicit
Public Sub JoinRowsData()
Dim ws As Worksheet ' define worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastRow As Long ' get last used row in worksheet
LastRow = GetLastUsed(xlByRows, ws)
Dim LastCol As Long ' get last used column in worksheet
LastCol = GetLastUsed(xlByColumns, ws)
' Read data into an array for faster processing
Dim Data() As Variant
Data = ws.Range("A1", ws.Cells(LastRow, LastCol)).Value2
' define an output array with the same size
Dim Output() As Variant
ReDim Output(1 To UBound(Data, 1), 1 To UBound(Data, 2))
Dim outRow As Long ' output row index
Dim iRow As Long
For iRow = 1 To LastRow ' loop through all rows in data
' if column 1 contains data it is a new output row
If Data(iRow, 1) <> vbNullString Then
outRow = outRow + 1
End If
' loop through all columns in a data row
Dim iCol As Long
For iCol = 1 To LastCol
If Data(iRow, iCol) <> vbNullString Then ' check if current cell has data
If Output(outRow, iCol) <> vbNullString Then
' add a line break if there is already data in the output cell
Output(outRow, iCol) = Output(outRow, iCol) & vbLf
End If
' add the data to the output cell
Output(outRow, iCol) = Output(outRow, iCol) & Data(iRow, iCol)
End If
Next iCol
Next iRow
' write all the output data from the array back to the cells
ws.Range("A1", ws.Cells(LastRow, LastCol)).Value2 = Output
End Sub
' find last used row or column in worksheet
Public Function GetLastUsed(ByVal RowCol As XlSearchOrder, ByVal InWorksheet As Worksheet) As Long
With InWorksheet
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
Dim LastCell As Range
Set LastCell = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=RowCol, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If RowCol = xlByRows Then
GetLastUsed = LastCell.Row
Else
GetLastUsed = LastCell.Column
End If
Else
GetLastUsed = 1
End If
End With
End Function
please help i want to sort the name column such that each name starts after every blank cell.
I want it look something like this..pls help it's a pretty long column
Option Explicit
Sub SetNamePosition()
Dim arr As Variant
Dim i As Long: i = 1 ' for Loop
Dim j As Long: j = 1 ' for Array
Dim lastRow As Long: lastRow = Cells(Rows.Count, 1).End(xlUp).Row
Dim rngColB As Range: Set rngColB = Range("B2:B" & lastRow)
Dim rngNames As Range: Set rngNames = Range("C1") ' Temporary range
' Get column B names only
rngColB.SpecialCells(xlCellTypeConstants, 2).Copy
rngNames.PasteSpecial (xlPasteValues)
Application.CutCopyMode = False
Set rngNames = Range(rngNames, rngNames.End(xlDown))
' Load rngNames to array
arr = Application.Transpose(rngNames)
' Clear rng of column B and rngNames
rngColB.Clear
rngNames.Clear
' Insert names
For i = 2 To lastRow
' set name
Cells(i, 1).Offset(0, 1).Value = arr(j)
' find next cell
i = Cells(i, 1).End(xlDown).Row + 1
j = j + 1
Next i
End Sub
I's probably better to remove the empty ranges before making the array, but here's one way to distribute the names:
Loading the range ito an array, then go through the numbers and look for empty ranges.
This assumes that we are working with column "A" and "B" (1 and 2), starting at the top.
Sub test()
Dim arr As Variant
Dim lastRow As Long, i As Long, j As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
arr = Application.Transpose(Range("B2:B" & lastRow))
Range("B2:B" & lastRow).Clear
j = 1
For i = 2 To lastRow
Cells(i, 2) = arr(j)
j = j + 1
If j >= UBound(arr) Then Exit For
While arr(j) = "" And j < UBound(arr)
j = j + 1
Wend
While Not Cells(i, 1).Value = ""
i = i + 1
Wend
Next i
End Sub
Any leftover names will be removed
I want to remove duplicates based on the text in Column I and sum the values in Column C, the data in the other columns doesn't matter.
I do not want a pivot table and I am aware they are the preferred option for this type of thing.
An example of what I'd like to achieve:
I found VBA code and tried to modify it. It doesn't delete all the lines.
Sub Sum_and_Dedupe()
With Worksheets("data")
'deal with the block of data radiating out from A1
With .Cells(1, 1).CurrentRegion
'step off the header and make one column wider
With .Resize(.Rows.Count - 1, .Columns.Count + 1).Offset(1, 0)
.Columns(.Columns.Count).Formula = "=sumifs(c:c, i:i, i2)"
.Columns(3) = .Columns(.Columns.Count).Value
.Columns(.Columns.Count).Delete
End With
'remove duplicates
.RemoveDuplicates Columns:=Array(9), Header:=xlYes
End With
.UsedRange
End With
End Sub
This should be an answer to your question.
However, code might require adaptation if the range in which you look becomes very long.
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim LastRow As Long, LastCol As Long, a As Double, i As Long
Dim Rng As Range
Dim Cell As Variant, Estimate As Variant
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set Rng = ws.Range(ws.Cells(2, 9), ws.Cells(LastRow, 9))
For Each Cell In Rng
i = 0
a = 0
For Each Estimate In Rng
If Estimate.Value = Cell.Value Then
i = i + 1 'Count nr of intances
a = a + ws.Cells(Estimate.Row, 3).Value 'sum booking value
If i > 1 Then
ws.Rows(Estimate.Row).Delete
i = 1
LastRow = LastRow - 1
End If
End If
Next Estimate
ws.Cells(Cell.Row, 3).Value = a 'Enter sum in booked this week
Next Cell
End Sub
You'll either need to change your current sheet name to data, or change the first two lines of this code to fit your needs. sh = the data sheet that you showed us. osh = an output sheet that this code will generate. Note also if column C or I move you can update the positions easily by changing colBooked and colEstimate. If you have more than a thousand unique estimate entries then make the array number larger than 999.
Sub summariseEstimates()
Dim sh As String: sh = "data"
Dim osh As String: osh = "summary"
Dim colBooked As Integer: colBooked = 3
Dim colEstimate As Integer: colEstimate = 9
Dim myArray(999) As String
Dim shCheck As Worksheet
Dim output As Worksheet
Dim lastRow As Long
Dim a As Integer: a = 0
Dim b As Integer
Dim r As Long 'row anchor
Dim i As Integer 'sheets
'Build summary array:
With Worksheets(sh)
lastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For r = 2 To lastRow
If r = 2 Then 'first entry
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
Else
For b = 0 To a
If VBA.LCase(VBA.Replace(.Cells(r, colEstimate), " ", "")) = VBA.LCase(VBA.Replace(VBA.Split(myArray(b), ",")(0), " ", "")) Then 'match
myArray(b) = VBA.Split(myArray(b), ",")(0) & "," & VBA.Split(myArray(b), ",")(1) + .Cells(r, colBooked)
Exit For
End If
Next b
If b = a + 1 Then 'completed loop = no match, create new array item:
a = a + 1
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
End If
End If
Next r
End With
'Create summary sheet:
On Error Resume Next
Set shCheck = Worksheets(osh)
If Err.Number <> 0 Then
On Error GoTo 0
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
Err.Clear
Else
On Error GoTo 0
If MsgBox("*" & osh & "* sheet already exists. Proceed to delete and recreate?", vbOKCancel, "Summary") = vbCancel Then
Exit Sub
Else
Application.DisplayAlerts = False
Worksheets(osh).Delete
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
End If
End If
'Output to summary sheet:
With Worksheets(osh)
.Cells(1, 1).Value = "ESTIMATE"
.Cells(1, 2).Value = "BOOKED THIS WEEK"
For b = 0 To a
.Cells(b + 2, 1).Value = VBA.Split(myArray(b), ",")(0)
.Cells(b + 2, 2).Value = VBA.Split(myArray(b), ",")(1)
Next b
.Columns("A:B").AutoFit
End With
End Sub
I am new to VBA and was trying to write a macro to check duplicates among a column. I have values in columns from A to Z with varying last row number, some may be 5 while some may be 10. Is there any way to check if duplicate value exist among a column and then print "duplicate" on the first row (I dont have any values in the first row for all the columns). I need this for varying last row and last column number.
You can try:
Option Explicit
Public Sub Get_Unique_Count_Paste_Array()
Dim Ob As Object
Dim rng As Range
Dim i As Long
Dim str As String
Dim LR As Long
Dim Item As Variant
With Worksheets("Sheet1")
For i = 1 To 26
Set Ob = CreateObject("scripting.dictionary")
LR = .Cells(.Rows.Count, i).End(xlUp).Row
For Each rng In .Range(Cells(2, i), Cells(LR, i))
str = Trim(rng.Value)
If Len(str) > 0 Then
Ob(str) = Ob(str) + 1
End If
Next rng
For Each Item In Ob.keys
If .Cells(1, i).Value = "" Then
.Cells(1, i).Value = Item
ElseIf .Cells(1, i).Value <> "" Then
.Cells(1, i).Value = .Cells(1, i).Value & ", " & Item
End If
Next Item
Next i
End With
End Sub
Edited Version:
Option Explicit
Public Sub Get_Unique_Count_Paste_Array()
Dim Ob As Object
Dim rng As Range
Dim i As Long
Dim str As String
Dim LR As Long
Dim Item As Variant
With Worksheets("Sheet1")
For i = 1 To 26
Set Ob = CreateObject("scripting.dictionary")
LR = .Cells(.Rows.Count, i).End(xlUp).Row
For Each rng In .Range(Cells(2, i), Cells(LR, i))
str = Trim(rng.Value)
If Len(str) > 0 Then
Ob(str) = Ob(str) + 1
End If
Next rng
For Each Item In Ob.keys
If .Cells(1, i).Value = "" And Ob(Item) > 1 Then
.Cells(1, i).Value = "Duplicate"
Exit For
End If
Next Item
Next i
End With
End Sub
A slight alteration of #error 1004's idea
Private d As Scripting.Dictionary
Private s As String
Function Get_Dupe_Summary(rngInput As Excel.Range) as string
Dim c As Excel.Range
Set d = New Scripting.Dictionary
For Each c In rngInput.Cells
If d.Exists(c.Value) Then
Get_Dupe_Summary = Get_Dupe_Summary & _
IIf(Len(Get_Dupe_Summary) > 0, ",", "") & _
"Dupe : " & c & " on row " & c.Row
Else
d.Add c.Value, 1
End If
Next c
End Function
How to retrieve values from predefined row and a column range (Incremental) to text boxes (Incremental) such that for example value of cell “J4” populate in "textbox1" and its column heading in "Label1" , value of cell “k4” populate in "textbox2" and its column heading in "Label2" and so on ............. value of cell“BG4” populate in "textbox50" and its column heading in "Label50".
I have tried the followings
Private Sub CommandButton1_Click()
Dim i As Long, lastrow As Long
Dim ws As Worksheet
Dim fcolumn As Long
Dim lcolumn As Long
Set ws = Worksheets("md")
lastrow = ws.Cells.Find(What:="*",
SearchOrder:=xlRows,SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
fcolumn = 9
lcolumn = 50
For i = 2 To lastrow
fcolumn = fcolumn + 1
If ws.Cells(i, "A").Value = Val(Me.TextBox_orderno) Then
If Sheets("md").Cells(i, fcolumn).Value <> 0 Then
Me.Label1 = ws.Cells(2, fcolumn)
Me.TextBox1 = Sheets("md").Cells(i, fcolumn).Value
End If
If Sheets("md").Cells(i, fcolumn).Value <> 0 Then
Me.Label2 = ws.Cells(2, fcolumn)
Me.TextBox1 = Sheets("md").Cells(i, fcolumn).Value
End If
Next
End Sub
I'm still not sure exactly what you are doing, but see if this helps you along. It should at least show you how to dynamically refer to the names of controls which alter only in the number at the end.
Private Sub CommandButton1_Click()
Dim i As Long, lastrow As Long
Dim ws As Worksheet
Dim fcolumn As Long
Dim lcolumn As Long
Set ws = Worksheets("md")
lastrow = ws.Cells.Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row + 1
fcolumn = 9
lcolumn = 50
For i = 2 To lastrow
fcolumn = fcolumn + 1
If ws.Cells(i, "A").Value = Val(Me.Controls("TextBox" & i - 1)) Then
If Sheets("md").Cells(i, fcolumn).Value <> 0 Then
Me.Controls("Label" & i - 1).Value = ws.Cells(2, fcolumn)
Me.Controls("Textbox" & i - 1).Value = Sheets("md").Cells(i, fcolumn).Value
End If
End If
Next
End Sub