Goal: Add the string "Z" to a select few columns for all rows except the header. Concatenate only on select headers i.e. headers defined in the array.
Dim header As Range
ArrayCheck = Array("CarTime", "BusTime", "PlaneTime")
LastRow = desWS1.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lcol = desWS1.Cells(1, Columns.Count).End(xlToLeft).Column
For Each header In desWS1.Range(desWS1.Cells(1, 1), desWS1.Cells(1, lcol))
For i = LBound(ArrayCheck) To UBound(ArrayCheck)
If header = ArrayCheck(i) Then
desWS1.Range(desWS1.Cells(2, header.Column), desWS1.Cells(LastRow, header.Column)) & "Z"
End If
Next i
Next
all entries in these columns are of the form: yyyy-mm-ddThh:mm:ss
#SiddharthRout the current cell is: 2020-09-07T13:08:46, and the output i want is: 2020-09-07T13:08:46Z. So yep, you're right, it's a string. – Jak Carty 2 mins ago
In my below code, I will take a sample of both date and date stored as text. I have commented the code so you should not have a problem understanding it. But if you do then simply post back.
Is this what you are trying?
Code:
WAY 1
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, lCol As Long
Dim ArrayCheck As Variant
Dim i As Long, j As Long
Dim rng As Range
ArrayCheck = Array("CarTime", "BusTime", "PlaneTime")
'~~> Set this to the relevant worksheet
Set ws = Sheet1
With ws
'~~> Find last row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last col
lCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
'~~> Loop though the cell in 1st row
For i = 1 To lCol
'~~> Loop through the array
For j = LBound(ArrayCheck) To UBound(ArrayCheck)
'~~> Check if they match
If .Cells(1, i).Value2 = ArrayCheck(j) Then
'~~> Set your range from cell 2 onwards
Set rng = .Range(.Cells(2, i), .Cells(lRow, i))
'~~> Add "Z" to the entire range in ONE GO i.e without looping
'~~> To understand this visit the url below
'https://stackoverflow.com/questions/19985895/convert-an-entire-range-to-uppercase-without-looping-through-all-the-cells
rng.Value = Evaluate("index(Concatenate(" & rng.Address & ",""Z""" & "),)")
End If
Next j
Next i
End With
End Sub
Note: For the sake of clarity, I am not joining the string ",""Z""" & "),)")
In Action
WAY 2
Introducing a 2nd way
This code writes to array and then works with it.
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long, lCol As Long
Dim ArrayCheck As Variant
Dim i As Long, j As Long, k As Long
Dim rng As Range
Dim tmpAr As Variant
ArrayCheck = Array("CarTime", "BusTime", "PlaneTime")
'~~> Set this to the relevant worksheet
Set ws = Sheet1
With ws
'~~> Find last row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last col
lCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
'~~> Loop though the cell in 1st row
For i = 1 To lCol
'~~> Loop through the array
For j = LBound(ArrayCheck) To UBound(ArrayCheck)
'~~> Check if they match
If .Cells(1, i).Value2 = ArrayCheck(j) Then
'~> Set your range
Set rng = .Range(.Cells(2, i), .Cells(lRow, i))
'~~> Store the value in array
tmpAr = rng.Value2
'~~> Work with array
For k = 1 To UBound(tmpAr)
tmpAr(k, 1) = tmpAr(k, 1) & "Z"
Next k
'~~> write the array back to worksheet
rng.Resize(UBound(tmpAr), 1).Value = tmpAr
End If
Next j
Next i
End With
End Sub
In Action
Related
I have a report with Data in columns A-L.
The number or rows in the report is dynamic.
I need to delete every row after the first instance of 0000004473 in column I.
I saw code for a similar question. In my spreadsheet the macro does the opposite and delete every row before 0000004473.
Sub Sample()
Dim rng As Range
Dim sRow As Long, lastRow As Long
With Sheets("Sheet1")
'find Terminations
Set rng = .Range("I:I").Find(what:="0000004473", after:=.Range("I1"))
If Not rng Is Nothing Then
'~~> Get the start row
sRow = rng.Row + 1
'find last row
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastRow = .Cells.Find(what:="*", _
after:=.Range("I1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lastRow = 1
End If
'I use lastRow + 1 to prevent deletion "0000004773" when it is on lastrow
.Range(sRow & ":" & lastRow + 1).Delete Shift:=xlUp
End If
End With
End Sub
Don't use .find to get the last row. If you have data before .range("I1") it will use that as your lastrow value because it stops once it gets a match.
If you put Debug.Print lastRow, sRow where I have it below you can see what the range will be.
Sub Sample()
Dim rng As Range
Dim sRow As Long, lastRow As Long
With Sheets("Sheet1")
'find Terminations
Set rng = .Range("I:I").Find(what:="0000004473", after:=.Range("I1"))
If Not rng Is Nothing Then
'~~> Get the start row
sRow = rng.Row + 1
'find last row
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastRow = .Cells(.Rows.Count, "I").End(xlUp).Row
Else
lastRow = 1
End If
Debug.Print lastRow, sRow
'I use lastRow + 1 to prevent deletion "0000004773" when it is on lastrow
.Range(sRow & ":" & lastRow + 1).Delete Shift:=xlUp
End If
End With
End Sub
Assuming "I" is the column with the most values, if it isn't use whatever column does.
I have some data in my worksheet and what I want to achieve is that I want to add data after 1 column from the last column with data.
I have tried the Range().End property but I think I am not able to implement it correctly.
colnum = Sheet3.Range("A:Z").End(xlToLeft).Column + 2
The above code gives me the column number as '1' and plus 2 means it gives column number '3' which is 'C'.
Data in my worksheet is something like this:
I want a dynamic search and my code should find column 'E' as the last column with data and start adding data from column 'F'. Any suggestions are appreciated.
This function returns an array where the first element is the last row number, and the second element the last column number.
Function LastRowCol(Worksht) As Long()
'Uncomment if on worksheet
'Application.Volatile
Dim WS As Worksheet, R As Range
Dim LastRow As Long, LastCol As Long
Dim L(1) As Long
Select Case TypeName(Worksht)
Case "String"
Set WS = Worksheets(Worksht)
Case "Worksheet"
Set WS = Worksht
End Select
With WS
Set R = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not R Is Nothing Then
LastRow = R.Row
LastCol = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
L(0) = LastRow
L(1) = LastCol
LastRowCol = L
End Function
Sub change_code_name()
'Requires Trust Access to VBA Object Model
Dim wbk As Object, sheet As Object
ActiveWorkbook.VBProject.name = "VBAProject"
Set wbk = ActiveWorkbook.VBProject.VBComponents(ActiveWorkbook.CodeName)
wbk.name = "wbk_code_name"
Set sheet = ActiveWorkbook.VBProject.VBComponents(ActiveWorkbook.Sheets(1).CodeName)
sheet.name = "sheet_code_name"
End Sub
In the case of your code line, assuming your worksheet CodeName is Sheet3, it would be something like:
colnum = LastRowCol(Sheet3)(1) + 1
and would apply to the active workbook.
See also Error in finding last used cell in Excel with VBA for an extensive discussion of the various methods of finding the last row/column and the pitfalls of each.
Edit:
Examining your screenshot, it seems that you might want to exclude Row 1 from the testing. If that is the case, then try this slightly modified code:
Option Explicit
Function LastRowCol(Worksht, Optional excludeRows As Long = 0) As Long()
'Uncomment if on worksheet
'Application.Volatile
Dim WS As Worksheet, R As Range
Dim LastRow As Long, LastCol As Long
Dim L(1) As Long
Dim searchRng As Range
Select Case TypeName(Worksht)
Case "String"
Set WS = Worksheets(Worksht)
Case "Worksheet"
Set WS = Worksht
End Select
If excludeRows > 0 Then
With WS
Set searchRng = Range(.Cells(excludeRows + 1, 1), .Cells(.Rows.Count, .Columns.Count))
End With
Else
Set searchRng = WS.Cells
End If
With searchRng
Set R = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByRows, _
searchdirection:=xlPrevious)
If Not R Is Nothing Then
LastRow = R.Row
LastCol = .Cells.Find(what:="*", after:=.Cells(1, 1), _
LookIn:=xlFormulas, lookat:=xlPart, searchorder:=xlByColumns, _
searchdirection:=xlPrevious).Column
Else
LastRow = 1
LastCol = 1
End If
End With
L(0) = excludeRows + 1
L(1) = LastCol
LastRowCol = L
End Function
and, in your macro:
colnum = LastRowCol(Sheet3,1)(1) + 1
Currently trying to append all cells in each row into the first cell of that row, and iterate through every row. Problem is I'm dealing with ~3000 rows with about 20 columns of data in each row. Is there any better way to append all cells in a row into one single cell without using a for loop? That could narrow down the code to a single for loop and may speed up the process.
Tried making a nested for loop that iterates through every row then every column per row. It works, but takes far too long when dealing with a large amount of data.
Sub AppendToSingleCell()
Dim value As String
Dim newString As String
Dim lastColumn As Long
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
For j = 1 To lastRow
lastColumn = Cells(j, Columns.Count).End(xlToLeft).Column
For i = 2 To lastColumn
If IsEmpty(Cells(j, i)) = False Then
value = Cells(j, i)
newString = Cells(j, 1).value & " " & value
Cells(j, 1).value = newString
Cells(j, i).Clear
End If
Next i
Next j
End Sub
Load everything into a variant array and loop that instead of the range. load the output into another variant array and then put that data as one back in the sheet.
Sub AppendToSingleCell()
With ActiveSheet
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, "A").End(xlUp).row
Dim lastColumn As Long
lastColumn = .Cells.Find(What:="*", After:=.Range("a1"), LookIn:=xlValue, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Dim dtaArr() As Variant
dtaArr = .Range(.Cells(1, 2), .Cells(lastRow, lastColumn)).value
Dim otArr() As Variant
ReDim otArr(1 To lastRow, 1 To 1)
Dim i As Long
For i = LBound(dtaArr, 1) To UBound(dtaArr, 1)
For j = LBound(dtaArr, 2) To UBound(dtaArr, 2)
If dtaArr(i, j) <> "" Then otArr(i, 1) = otArr(i, 1) & dtaArr(i, j) & " "
Next j
otArr(i, 1) = Application.Trim(otArr(i, 1))
Next i
.Range(.Cells(1, 2), .Cells(lastRow, lastColumn)).Clear
.Range(.Cells(1, 1), .Cells(lastRow, 1)).value = otArr
End With
End Sub
It's a bit long, but pretty straight forward.
Explanation inside the code's comments.
Code
Option Explicit
Sub AppendToSingleCell()
Dim newString As String
Dim LastRow As Long, LastColumn As Long
Dim Sht As Worksheet
Dim FullArr As Variant, MergeCellsArr As Variant
Dim i As Long, j As Long
Set Sht = ThisWorkbook.Sheets("Sheet1") ' <-- rename "Sheet1" to your sheet's name
With Sht
LastRow = FindLastRow(Sht) ' call sub that finds last row
LastColumn = FindLastCol(Sht) ' call sub that finds last column
' populate array with enitre range contents
FullArr = .Range(.Cells(1, 1), .Cells(LastRow, LastColumn))
ReDim MergeCellsArr(1 To LastRow) ' redim 1-D array for results (same number of rows as in the 2-D array)
' looping through array is way faster than interfacing with your worksheet
For i = 1 To UBound(FullArr, 1) ' loop rows (1st dimension of 2-D array)
newString = FullArr(i, 1)
For j = 2 To UBound(FullArr, 2) ' loop columns (2nd dimension of 2-D array)
If IsEmpty(FullArr(i, j)) = False Then
newString = newString & " " & FullArr(i, j)
End If
Next j
MergeCellsArr(i) = newString ' read new appended string to new 1-D array
Next i
' paste entire array to first column
.Range("A1").Resize(UBound(MergeCellsArr)).value = MergeCellsArr
End With
End Sub
'=======================================================================
Function FindLastCol(Sht As Worksheet) As Long
' This Function finds the last col in a worksheet, and returns the column number
Dim LastCell As Range
With Sht
Set LastCell = .Cells.Find(What:="*", After:=.Cells(1), LookAt:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
FindLastCol = LastCell.Column
Else
MsgBox "Error! worksheet is empty", vbCritical
Exit Function
End If
End With
End Function
'=======================================================================
Function FindLastRow(Sht As Worksheet) As Long
' This Function finds the last row in a worksheet, and returns the row number
Dim LastCell As Range
With Sht
Set LastCell = .Cells.Find(What:="*", After:=.Cells(1), LookAt:=xlPart, LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False)
If Not LastCell Is Nothing Then
FindLastRow = LastCell.Row
Else
MsgBox "Error! worksheet is empty", vbCritical
Exit Function
End If
End With
End Function
If you're interested in a shorter solution.... It assumes your data begins in cell A1.
Public Sub CombineColumnData()
Dim arr As Variant
Dim newArr() As Variant
Dim varTemp As Variant
Dim i As Long
arr = ActiveSheet.Range("A1").CurrentRegion.Value
ReDim newArr(1 To UBound(arr, 1))
For i = LBound(arr, 1) To UBound(arr, 1)
varTemp = Application.Index(arr, i, 0)
newArr(i) = Join(varTemp, "")
Next i
With ActiveSheet.Range("A1")
.CurrentRegion.Clear
.Resize(UBound(arr, 1), 1) = Application.Transpose(newArr)
End With
End Sub
**'Dim raport As Worksheet
'Dim daty As String
'Dim lcolumn As Long
'Dim mycolaaa As String
'Dim dataT As Variant
'Set raport = ActiveWorkbook.Sheets("sheet1")
'raport.Activate
'lcolumn = raport.Cells(1, Columns.Count).End(xlToLeft).Column
'daty = ("A1:xy1")
'With raport
'raport.Range(daty).Select
'End With
'Selection.Copy
'dataT = Application.Transpose(Data)
'With tarws
'CopyRangeAddress = .Range("A2:A100").Address
' .Range(CopyRangeAddress).PasteSpecial xlPasteValues
'.Range(CopyRangeAddress).PasteSpecial xlPasteFormats
'.Range(CopyRangeAddress).PasteSpecial xlPasteColumnWidths
'End With
srcws.Activate
'With srcws
'.Range(sortrangeaddress).Select
'End With
'Selection.Copy
'Paste the Sort Range on to the target worksheet
'The CopyRangeAddress will be A1 through the last Row
'and column 2 -- so something like A1:B2
'With tarws
'CopyRangeAddress = .Range(.Cells(pasteRow, pasteCol), _
'.Cells(pasteRow + lrow - 2, 2)).Address
'.Range(CopyRangeAddress).PasteSpecial xlPasteValues
'.Range(CopyRangeAddress).PasteSpecial xlPasteFormats
'.Range(CopyRangeAddress).PasteSpecial xlPasteColumnWidths
'End With**
How to add "daty" cells instead of sortrangeadress? It is data from source worksheet in one row as header of below cells that you helped me with transposing. Thank you a lot for previous answers!
From your pictures it looks like you are attempting to un-pivot a pivot table. Your best bet to tackle this is to create smaller ranges for each "date" groupings. The code below provides an example of how to move through the groupings.
Option Explicit
Public Sub Example()
Const firstDataCell As Long = 3 'Column C
Const columnsInDataGroup As Long = 10
Const DataRowStart As Long = 2 'Row 2
'Worksheet with the source data
Dim srcWs As Worksheet
Set srcWs = ActiveWorkbook.Sheets("Sheet1")
'Worksheet to write data to
Dim tarWs As Worksheet
Set tarWs = ActiveWorkbook.Sheets("Sheet2")
'Get the last row of data
Dim lRow As Long
lRow = LastRow(srcWs)
'Get the last column containing data
Dim lCol As Long
lCol = LastColumn(srcWs)
'This are the first columns you seem to
'want to sort the data on
Dim SortRangeAddress As String
SortRangeAddress = "A2:B" & Trim(CStr(lRow))
'This variable will contain the address of
'each Date Data Group as your macro
'loops across the columns
Dim dateDataGroupRangeAddress As String
Dim row As Long
Dim col As Long
Dim pasteRow As Long: pasteRow = 1
Dim pasteCol As Long: pasteCol = 1
Dim CopyRangeAddress As String
For col = firstDataCell To lCol Step columnsInDataGroup
'Copy the Sort Range from the source worksheet to
'the target worksheet.
With srcWs
.Range(SortRangeAddress).Select
End With
Selection.Copy
'Paste the Sort Range on to the target worksheet
'The CopyRangeAddress will be A1 through the last Row
'and column 2 -- so something like A1:B2
With tarWs
CopyRangeAddress = .Range(.Cells(pasteRow, pasteCol), _
.Cells(pasteRow + lRow - 2, 2)).Address
.Range(CopyRangeAddress).PasteSpecial xlPasteValues
End With
'Copy the next source date data group. The width of the selection
'is determine by columnsInDataGroup constant set above less 1
'Think of the first .Cells as 1 and the second .Cells as
'columnsInDataGroup - 1.
With srcWs
dateDataGroupRangeAddress = .Range(.Cells(DataRowStart, col), _
.Cells(lRow, col + columnsInDataGroup - 1)).Address
.Range(dateDataGroupRangeAddress).Select
End With
Selection.Copy
'Paste the next source date date group to the target worksheet
'CopyRangeAddress here will move 2 columns over from the
'Start of the sort data range (Columns A & B) to start the
'paste in column C
With tarWs
CopyRangeAddress = .Range(.Cells(pasteRow, pasteCol + 2), _
.Cells(pasteRow + lRow - 2, columnsInDataGroup + 2)).Address
.Range(CopyRangeAddress).PasteSpecial xlPasteValues
End With
pasteRow = pasteRow + lRow - 1
Next col
End Sub
Function LastRow(ByRef sh As Worksheet)
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
End Function
Function LastColumn(ByRef sh As Worksheet)
LastColumn = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
End Function
I have 100K Excel file that has many employee info, I want to shift all existence data to the first row for this employee, the picture below will be louder than my words, can a VBA code do this? or there is a trick in excel that I am not aware of
Try following code.
Sub Demo()
Dim ws As Worksheet
Dim cel As Range, rng As Range
Dim lastRow As Long, lastCol As Long, i As Long
Dim fOccur As Long, lOccur As Long, colIndex As Long
Dim dict As Object, c1
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your data range
Set dict = CreateObject("Scripting.Dictionary")
With ws
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row 'last row with data in Column A
lastCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column 'last column with data in Sheet1
Set rng = .Range("A1:A" & lastRow) 'set range in Column A
c1 = .Range("A2:A" & lastRow)
For i = 1 To UBound(c1, 1) 'using dictionary to get uniques values from Column A
dict(c1(i, 1)) = 1
Next i
colIndex = 16 'colIndex+1 is column number where data will be displayed from
For Each k In dict.keys 'loopthrough all unique values in Column A
fOccur = Application.WorksheetFunction.Match(k, rng, 0) 'get row no. of first occurrence
lOccur = Application.WorksheetFunction.CountIf(rng, k) 'get row no. of last occurrence
lOccur = lOccur + fOccur - 1
'copy range from left to right
.Range(.Cells(fOccur, 1 + colIndex), .Cells(lOccur, lastCol + colIndex)).Value = .Range(.Cells(fOccur, 1), .Cells(lOccur, lastCol)).Value
'delete blanks in range at right
.Range(.Cells(fOccur, 1 + colIndex), .Cells(lOccur, lastCol + colIndex)).SpecialCells(xlCellTypeBlanks).Delete Shift:=xlUp 'delte blank rows
Next k
End With
Application.ScreenUpdating = True
End Sub
Try the below. You can amend the below code to match where you want to move the range:
Dim oW As Worksheet: Set oW = ThisWorkbook.Worksheets("Sheet8")
With oW.UsedRange
.Cut .Offset(0, .Columns.Count + 2)
End With