I have the following macro which is so close to what I need. The issue I have is if the data is already in sheet2 it inserts a new line and the same data where as I don't want it duplicated. I have tried a few things but I cant quite get there
'start with sheet 1
Sheets(1).Activate
Dim rowStartSheet1, rowStartSheet2, lastRowSheet1, lastRowSheet2 As Integer
'change this variable if your row doesn't start on 2 as in this example for sheet1 and sheet2
rowStartSheet1 = 2
rowStartSheet2 = 2
'gets you the last row in sheet 1
lastRowSheet1 = Cells(Rows.Count, 1).End(xlUp).Row
'this entire for block is to check if a data row in sheet 1 is in sheet 2 and if so, copy and paste the rest of the data points
For i = rowStartSheet1 To lastRowSheet1
'case 1 where column C matches column A first time around (no duplicates)
'change this variable if sheet 2 starts on a different row
Sheets(2).Activate
lastRowSheet2 = Cells(Rows.Count, 1).End(xlUp).Row
'loops through sheet 2 column A to check if it matches what we want in sheet1 Column C
For ii = rowStartSheet2 To lastRowSheet2
'inputs if found first time around
If Sheets(1).Cells(i, 3) = Cells(ii, 1) And Cells(ii, 7) = "" Then
Cells(ii, 7) = Sheets(1).Cells(i, 1)
Cells(ii, 8) = Sheets(1).Cells(i, 2)
Exit For
'if sheet2 column G already has info in it, create a new row
ElseIf Sheets(1).Cells(i, 3) = Cells(ii, 1) And Cells(ii, 7) <> "" Then
Rows(ii).Select
Selection.Insert Shift:=xlShiftDown
Cells(ii, 1) = Sheets(1).Cells(i, 3)
Cells(ii, 7) = Sheets(1).Cells(i, 1)
Cells(ii, 8) = Sheets(1).Cells(i, 2)
Exit For
End If
Next ii
Next i
End Sub
All help appreciated
SHEET1
SHEET2
In my code below I refer to columns by their name (like "A", "B") instead of their number as you have done. This isn't intended as criticism. On the contrary, I much prefer to use numbers and usually declare them in enumerations. However, I felt that you might find my code more readable with the syntax I chose.
Sub CopyUniqueItems()
' 09 Aug 2017
Const RsFirst As Long = 2
Const RtFirst As Long = 2
Const Lot As Long = 1
Const Part As Long = 2
Const Col As Long = 3
Dim WsS As Worksheet ' S = Source
Dim WsT As Worksheet ' T = Target
Dim Rng As Range
Dim Itm As Variant
Dim Rs As Long, RsLast As Long ' Row / last row in WsS
Dim Rt As Variant, RtLast As Long ' Row / last row in WsT
Set WsS = Worksheets(1) ' { better to call by name
Set WsT = Worksheets(2) ' { like Worksheets("Sheet2")
RsLast = WsS.Cells(WsS.Rows.Count, "C").End(xlUp).Row
Application.ScreenUpdating = False
For Rs = RsFirst To RsLast
With WsS
Itm = .Range(.Cells(Rs, "A"), .Cells(Rs, "C")).Value
End With
With WsT
RtLast = .Cells(.Rows.Count, "A").End(xlUp).Row
With .Columns("A")
Set Rng = .Range(.Cells(RtFirst), .Cells(RtLast))
End With
On Error Resume Next
Rt = Application.Match(Itm(1, Lot), Rng, 0)
If IsError(Rt) Then
' not found
Rt = Application.Max(RtLast + 1, RtFirst)
Else
' exists already
Rt = Rt + RtFirst - 1
Do
If (.Cells(Rt, "G").Value = Itm(1, Part)) And _
(.Cells(Rt, "H").Value = Itm(1, Col)) Then
Rt = 0
Exit Do
Else
Rt = Rt + 1
End If
Loop While .Cells(Rt, "A").Value = Itm(1, Lot)
.Rows(Rt).Insert Shift:=xlShiftDown
End If
If Rt Then
.Cells(Rt, "A").Value = Itm(1, Lot)
.Cells(Rt, "G").Value = Itm(1, Part)
.Cells(Rt, "H").Value = Itm(1, Col)
End If
End With
Next Rs
Application.ScreenUpdating = True
End Sub
BTW, Dim rowStartSheet1, rowStartSheet2, lastRowSheet1, lastRowSheet2 As Integer declares only lastRowSheet2 as integer. All the others are undefined and therefore variants.
Related
I have workbook with one sheet and 80k lines as shown below. same Client may come up 100 times in sheet1.i need to look for value under ddindex value "1" and value under tier value "2", if these condition match then pick client name and put in new sheet ( sheet2) with their value from column data size. If same client comes again using above condition while going row by row in sheet1 then add( sum it with previous value) data size in second sheet ( sheet2). And also get latest created date and expiry date for same client in second sheet. any idea how to achieve this using VBA ??
so far i come up below code
Option Explicit
Sub find()
Dim i As Long
Dim sheets As Variant
Dim sheet As Variant
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Set ws = ThisWorkbook.sheets("Sheet2")
For i = 2 To ActiveSheet.sheets("sheet1").Range("A2").End(xlDown).Row
If Cells(i, 4).Value = 1 And Cells(i, 6).Value = 2 Then
ws.Range(1 & i).Value = Cells(i, 1).Value
ws.Range("A" & i).Value = Cells(i, 1).Value
End If
Next
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Try something like this:
Sub Summarize()
Dim i As Long, ws1 As Worksheet, ws2 As Worksheet, data, m, client
Dim dict As Object, dataOut, rw As Long
Set dict = CreateObject("scripting.dictionary") 'for tracking unique client id's
Set ws1 = ThisWorkbook.sheets("Sheet1")
Set ws2 = ThisWorkbook.sheets("Sheet2")
data = ws1.Range("A2:F" & ws1.Cells(Rows.Count, 1).End(xlUp).Row).Value 'data to array
ReDim dataOut(1 To UBound(data, 1), 1 To UBound(data, 2)) 'size output array
rw = 0
For i = 1 To UBound(data, 1) 'loop over rows in the array
If data(i, 5) = 1 And data(i, 6) = 2 Then 'processing this row?
client = data(i, 1)
If Not dict.exists(client) Then 'first time for this client?
rw = rw + 1 'increment "row" counter
dict.Add client, rw 'store position in dictionary
dataOut(rw, 1) = client 'add the client id
End If
rw = dict(client) 'find the array "row" to update
If data(i, 2) > dataOut(rw, 2) Then
dataOut(rw, 2) = data(i, 2)
dataOut(rw, 3) = data(i, 3)
End If
dataOut(rw, 4) = dataOut(rw, 4) + data(i, 4)
dataOut(rw, 5) = data(i, 5)
dataOut(rw, 6) = data(i, 6)
End If
Next
'drop the summarized data on the worksheet
If rw > 0 Then ws2.Range("A2").Resize(rw, UBound(data, 2)).Value = dataOut
End Sub
I am comparing two sheets and their columns. My code runs. Problem is it compares most of the values and leaves some values though they are the same.
Sub Peformance()
Dim k As Integer
Dim i As Integer
Dim j As Integer
For i = 1 To 138
If (ActiveWorkbook.Worksheets("report").Cells(i, 6).Value = "course-1") Then
For j = 1 To 138
If (ActiveWorkbook.Worksheets("report").Cells(i, 1).Value = Cells(j, 1)) Then
Cells(j, 4).Value = (ActiveWorkbook.Worksheets("report").Cells(i, 12).Value) / 100
Cells(j, 5).Value = (ActiveWorkbook.Worksheets("report").Cells(i, 20).Value) / 100
Cells(j, 6).Value = (ActiveWorkbook.Worksheets("report").Cells(i, 13).Value)
End If
Next j
End If
Next i
For k = 1 To 138
If (IsEmpty(Cells(k, 4).Value)) Then
Cells(k, 4).Value = 0
Cells(k, 5).Value = 0
End If
If (IsEmpty(Cells(k, 6).Value)) Then
Cells(k, 6).Value = 0
End If
End Sub
In one file (sheet-2) I have students courses like course-1, course-2, course-3 etc.
In the other file (sheet-1) I have students names.
After comparing names (Column-1 of sheet-2 and sheet-1) I have to copy the performance from sheet-2 to sheet-1.
It runs but is not showing output for some students whose names are same.
Also how can I add the feature of case sensitive?
Sample Data
Sheet2:
Name
Email
External
Course
Course-ID
Course-Slug
Work-Percentage
A
a#gmail.com
12
A
course
course-1
63%
B
b#gmail.com
13
A
course
course-1
19%
Sheet1:
Name
Work-Percentage
A
B
So sheet1 column Work-Percentage will copy data from Work-Percentage column after comparing the name and course-Slug from sheet-2
Double For...Next
Adjust the name of the Destination Worksheet (dws) because I named it Course-1.
StrComp is taking care of the case-sensitivity issues.
This is just a quick fix for you to learn (understand). Otherwise, the efficiency can vastly be improved.
Not tested.
The Code
Option Explicit
Sub Peformance()
' Constants
Const sFirstRow As Long = 2
Const dFirstRow As Long = 2
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets("Report")
Dim sLastRow As Long: sLastRow = sws.Cells(sws.Rows.Count, 1).End(xlUp).Row
Dim k As Long
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets("Course-1")
Dim dLastRow As Long: dLastRow = dws.Cells(dws.Rows.Count, 1).End(xlUp).Row
Dim i As Long
' Loop
For i = dFirstRow To dLastRow
For k = sFirstRow To sLastRow
If StrComp(sws.Cells(k, 6).Value, "course-1", vbTextCompare) = 0 _
And StrComp(dws.Cells(i, 1).Value, sws.Cells(k, 1).Value, _
vbTextCompare) = 0 Then
' Student was found in Source Worksheet.
dws.Cells(i, 4).Value = sws.Cells(k, 12).Value / 100
dws.Cells(i, 5).Value = sws.Cells(k, 20).Value / 100
dws.Cells(i, 6).Value = sws.Cells(k, 13).Value
Exit For ' Student was found, no need to loop any longer.
End If
Next k
If k > sLastRow Then
' Student wasn't found in Source Worksheet.
If IsEmpty(dws.Cells(i, 4)) Then
If IsEmpty(dws.Cells(i, 6)) Then
dws.Cells(i, 4).Resize(3).Value = 0
Else
dws.Cells(i, 4).Resize(2).Value = 0
End If
End If
End If
Next i
End Sub
I have a task to restructure a two column excel sheet and expand it. Here is a picture to show what needs to be done, the data on the left of the green column is the original data and the data on the right is how it should look, but its only done for the first entry, I need to replicate it for all 10,000 rows of data.
To explain more indepth, each CRD it needs to be expanded to 160 rows, and go from 1978->2018 while listing the quarters out for each year. What is the best approach? Is it possible to write a macro to solve this ?
The following expects Sheet1 ans Sheet2 to be the names. And goes for 158 quarters.
Option Explicit
Sub doFromThru()
' clear contents
Sheets("Sheet2").Select
Cells.Select
Selection.ClearContents
Range("A1").Select
Cells(1, "A") = "CRD"
Cells(1, "B") = "Year"
Cells(1, "C") = "Quarter"
Cells(1, "D") = "QuarterNumerical"
Cells(1, "E") = "Disclosure"
Dim nOutRow As Integer
nOutRow = 1
' step thru all the rows on the input sheet
Dim nInRow As Long, maxInRow As Long, nInCRD As String, nInDisc As String
maxInRow = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
For nInRow = 2 To maxInRow
nInCRD = Sheets("Sheet1").Cells(nInRow, "A")
nInDisc = Sheets("Sheet1").Cells(nInRow, "L")
' create the new rows on Sheet2
Dim dFrom As String, nQtr As Integer
dFrom = DateValue("Oct 1978") ' starting from here
For nQtr = 1 To 158
nOutRow = nOutRow + 1
Sheets("Sheet2").Cells(nOutRow, "A") = nInCRD
Sheets("Sheet2").Cells(nOutRow, "B") = Format$(dFrom, "yyyy")
Sheets("Sheet2").Cells(nOutRow, "C") = Format$(dFrom, "Q")
Sheets("Sheet2").Cells(nOutRow, "D") = nQtr
Sheets("Sheet2").Cells(nOutRow, "E") = nInDisc
dFrom = DateAdd("Q", 1, dFrom)
Next nQtr
Next nInRow
End Sub
Add some diagnostics to tell you more. After nOutRow = nOutRow + 1
Sheets("Sheet2").Cells(1, "G") = nInRow
Sheets("Sheet2").Cells(1, "H") = nOutRow
Sheets("Sheet2").Cells(1, "I") = nQtr
Sheets("Sheet2").Cells(1, "J") = nInDisc
Untested.
Code assumes you want to start from Q4 of the year 1978 and will loop
for 159 quarters after 1978 Q4. (If necessary, you can change this by changing the value of TOTAL_QUARTERS and START_QUARTER in the code)
You will need to change "Sheet1" in the code to whatever the name of your sheet is.
Code tries to overwrite the contents of columns CH to CL on said sheet. So you might want to save a copy of your workbook before running.
Code:
Option Explicit
Sub ExpandRows()
Const START_YEAR as long = 1978
Const START_QUARTER as long = 4
Const TOTAL_QUARTERS as long = 160
With thisworkbook.worksheets("Sheet1")
Dim lastRow as long
lastRow = .cells(.rows.count, "A").row
Dim inputCRD() as variant
inputCRD = .range("A2:A" & lastRow).value2
Dim inputDisclosure() as variant
inputDisclosure = .range("L2:L" & lastRow).value2
Dim yearOffset as long
Dim quarterIndex as long
Dim numericalQuarterIndex as long
Dim totalRowCount as long
totalRowCount = (lastRow - 1) * TOTAL_QUARTERS ' -1 to skip first row
Dim outputArray() as variant
Redim outputArray(1 to totalRowCount, 1 to 5)
Dim readIndex as long
Dim writeIndex as long
For readIndex = lbound(inputCRD,1) to ubound (inputCRD,1)
quarterIndex = START_QUARTER
For numericalQuarterIndex = 1 to TOTAL_QUARTERS
writeIndex = writeIndex + 1
outputArray(writeIndex, 1) = inputCRD(readIndex, 1)
outputArray(writeIndex, 2) = START_YEAR + yearOffset
outputArray(writeIndex, 3) = quarterIndex
outputArray(writeIndex, 4) = numericalQuarterIndex
outputArray(writeIndex, 5) = inputDisclosure(readIndex, 1)
If quarterIndex < 4 then
quarterIndex = quarterIndex + 1
Else
yearOffset = yearOffset + 1
quarterIndex = 1
End if
Next numericalQuarterIndex
Next readIndex
.range("CH2").resize(ubound(outputArray,1), ubound(outputArray,2)).value2 = outputArray
End with
End sub
I am not sure if the title is correct. Please correct me if you have a better idea.
Here is my problem: Please see the picture.
This excel sheet contains only one column, let's say ColumnA. In ColumnA there are some cells repeat themselvs in the continued cells twice or three times (or even more).
I want to have the excel sheet transformed according to those repeated cells. For those items which repeat three times or more, keep only two of them.
[Shown in the right part of the picture. There are three Bs originally, target is just keep two Bs and delete the rest Bs.]
It's a very difficult task for me. To make it easier, it's no need to delete the empty rows after transformation.
Any kind of help will be highly appreciated. Thanks!
#
Update:
Please see the picture. Please dont delete the items if they show again...
EDITED - SEE BELOW Try this. Data is assumed to be in "Sheet1", and ordered data is written to "Results". I named your repeted data (A, B, C, etc) as sMarker, and values in between as sInsideTheMarker. If markers are not consecutive, the code will fail.
Private Sub ReOrderData()
Dim lLastRow As Long
Dim i As Integer
Dim a As Integer
Dim j As Integer
Dim sMarker As String
Dim sInsideTheMarker As String
'Get number of rows with data:
lLastRow = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
j = 0
k = 1
a = 2
'Scan all rows with data:
For i = 1 To lLastRow
If (Worksheets("Sheet1").Cells(i + 1, 1).Value = Worksheets("Sheet1").Cells(i, 1).Value) Then 'If two consecutive cells holds the same value
j = j + 1
If j = 1 Then
k = k + 1
a = 2
sMarker = Worksheets("Sheet1").Cells(i, 1).Value
Worksheets("Results").Cells(k, 1).Value = sMarker
End If
Else 'If not same values in consecutive cells
sInsideTheMarker = Worksheets("Sheet1").Cells(i, 1).Value
Worksheets("Results").Cells(k, a).Value = sInsideTheMarker
a = a + 1
j = 0
End If
Next i
End Sub
EDITION: If you want results in the same sheet ("Sheet1"), and keep the empty rows for results to look exactly as your question, try the following
Private Sub ReOrderData()
Dim lLastRow As Long
Dim i As Integer
Dim a As Integer
Dim j As Integer
Dim sMarker As String
Dim sInsideTheMarker As String
'Get number of rows with data:
lLastRow = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
j = 0
k = 1
a = 5
'Scan all rows with data:
For i = 1 To lLastRow
If (Worksheets("Sheet1").Cells(i + 1, 1).Value = Worksheets("Sheet1").Cells(i, 1).Value) Then 'If two consecutive cells holds the same value
j = j + 1
If j = 1 Then
k = i
a = 5
sMarker = Worksheets("Sheet1").Cells(i, 1).Value
Worksheets("Sheet1").Cells(k, 4).Value = sMarker
End If
Else 'If not same values in consecutive cells
sInsideTheMarker = Worksheets("Sheet1").Cells(i, 1).Value
Worksheets("Sheet1").Cells(k, a).Value = sInsideTheMarker
a = a + 1
j = 0
End If
Next i
End Sub
If you can delete the values that have more than two counts, then I suggest that this might work:
Sub count_macro()
Dim a As Integer
Dim b As Integer
a = 1
While Cells(a, 1) <> ""
b = WorksheetFunction.CountIf(Range("A1:A1000"), Cells(a, 1))
If b > 2 Then
Cells(a, 1).Delete Shift:=xlUp
End If
b = 0
a = a + 1
Wend
End Sub
This should do it. It takes input in column A starting in Row 2 until it ends, and ignores more than 2 same consecutive values. Then it copies them in sets and pastes them transposed. If your data is in a different column and row, change the sourceRange variable and the i variable accordingly.
Sub SETranspose()
Application.ScreenUpdating = False
Dim sourceRange As range
Dim copyRange As range
Dim myCell As range
Set sourceRange = range("A2", Cells(Rows.count, 1).End(xlUp))
Dim startCell As range
Set startCell = sourceRange(1, 1)
Dim i As Integer
Dim haveTwo As Boolean
haveTwo = True
For i = 3 To Cells(Rows.count, 1).End(xlUp).Row + 1
If Cells(i, 1).Value = startCell.Value Then
If haveTwo Then
range(startCell, Cells(i, 1)).Copy
startCell.Offset(0, 4).PasteSpecial Transpose:=True
Application.CutCopyMode = False
haveTwo = False
End If
End If
'if the letter changes or end of set, then copy the set over
'If LCase(Left(Cells(i, 1).Value, 1)) <> LCase(startCell.Value) Or _
'i = Cells(Rows.count, 1).End(xlUp).Row + 1 Then
If Len(Cells(i, 1).Value) > 1 Then
Set copyRange = Cells(i, 1)
copyRange.Copy
Cells(startCell.Row, Columns.count).End(xlToLeft).Offset(0, 1).PasteSpecial
Application.CutCopyMode = False
'Set startCell = sourceRange(i - 1, 1)
ElseIf Len(Cells(i, 1).Value) = 1 And Cells(i, 1).Value <> startCell.Value Then
Set startCell = sourceRange(i - 1, 1)
haveTwo = True
End If
Next i
'clear up data
Set sourceRange = Nothing
Set copyRange = Nothing
Set startCell = Nothing
Application.ScreenUpdating = True
End Sub
I have a search function which works perfectly for searching for Exact Numerical values, However I need to adapt it so it searches for text within a cell and only extracts that text. For example it searches column 7. In column 7 there may be a cell containing the words Interface - HPT, SAS, LPT Ideally I would like to search for the word Interface - HPT then extract Only this text from the cell. I also need the search function to be able to do this for multiple different values. So for example run a search for Interface - HPT
Interface - SAS and Interface LPT separate from each other. Is this Possible ?
Here is the code I have at the moment:
Sub InterfaceMacro()
Dim Headers() As String: Headers = _
Split("Target FMECA,Part I.D,Line I.D,Part No.,Part Name,Failure Mode,Assumed System Effect,Assumed Engine Effect", ",")
Worksheets.Add().Name = "Interface"
Dim wsInt As Worksheet: Set wsInt = Sheets("Interface")
wsInt.Move after:=Worksheets(Worksheets.Count)
wsInt.Cells.Clear
Application.ScreenUpdating = False
With wsFHA
For i = 0 To UBound(Headers)
.Cells(2, i + 2) = Headers(i)
.Columns(i + 2).EntireColumn.AutoFit
Next i
.Cells(1, 2) = "Interface TABLE"
.Range(.Cells(1, 2), .Cells(1, UBound(Headers) + 2)).MergeCells = True
.Range(.Cells(1, 2), .Cells(1, UBound(Headers) + 2)).HorizontalAlignment = xlCenter
.Range(.Cells(1, 2), .Cells(2, UBound(Headers) + 2)).Font.Bold = True
End With
Dim SourceCell As Range, FirstAdr As String
Dim RowCounter As Long: RowCounter = 3
Dim SearchTarget() As String
SearchTarget = Split("9.1,18.0", ",")
For i = 0 To UBound(SearchTarget)
If Worksheets.Count > 1 Then
For j = 1 To Worksheets.Count - 1
With Sheets(j)
Set SourceCell = .Columns(7).Find(SearchTarget(i), LookAt:=xlWhole)
If Not SourceCell Is Nothing Then
FirstAdr = SourceCell.Address
Do
wsInt.Cells(RowCounter, 2).Value = SearchTarget(i)
wsInt.Cells(RowCounter, 3).Value = .Cells(SourceCell.Row, 6).Value
wsInt.Cells(RowCounter, 4).Value = .Cells(3, 10).Value
wsInt.Cells(RowCounter, 5).Value = .Cells(2, 10).Value
wsInt.Cells(RowCounter, 6).Value = .Cells(SourceCell.Row, 2).Value
For k = 0 To SourceCell.Row - 1
If .Cells(SourceCell.Row - k, 3).Value <> "continued." Then
wsFHA.Cells(RowCounter, 7).Value = .Cells(SourceCell.Row - k, 3).Value
Exit For
End If
Next k
wsInt.Cells(RowCounter, 8).Value = .Cells(SourceCell.Row, 14).Value
Set SourceCell = .Columns(7).FindNext(SourceCell)
RowCounter = RowCounter + 1
Loop While Not SourceCell Is Nothing And SourceCell.Address <> FirstAdr
End If
End With
Next j
End If
Next i
End Sub
The part I believe needs editing is this section
Dim SourceCell As Range, FirstAdr As String
Dim RowCounter As Long: RowCounter = 3
Dim SearchTarget() As String
SearchTarget = Split("9.1,18.0", ",")
For i = 0 To UBound(SearchTarget)
If Worksheets.Count > 1 Then
For j = 1 To Worksheets.Count - 1
With Sheets(j)
Set SourceCell = .Columns(7).Find(SearchTarget(i), LookAt:=xlWhole)
If Not SourceCell Is Nothing Then
FirstAdr = SourceCell.Address
You can define the array to search the same way as you define it for numbers.
To search also part of the cell content you need to change .Find(SearchTarget(i), LookAt:=xlWhole) to .Find(SearchTarget(i), LookAt:=xlPart).
VBA looks in formulas / results the same way as it works in Find / Replace dialog. (set .LookIn to either xlValues or xlFormulas)