Excel VBA - Get row of latest change in a parameter - vba

I have a large data set which looks like this:
Employee ID |Job| Function| Level|Date of change
1 | x | a | A1 | 01/05/2014
1 | y | a | A1 | 02/04/2015
1 | y | a | A2 | 25/08/2015
1 | z | a | A3 | 27/12/2015
1 | z | c | A3 | 01/03/2016
2 | t | b | B1 | 12/05/2013
2 | v | b | B1 | 13/04/2014
2 | w | b | B3 | 12/01/2016
Each row contains a change in either job, function or level.
I need to create a table which puts together the latest change in level for each employee (so for employee 1, it would be row 4). So far I have used a combination of conditional formatting and pivots but I was wondering if there is a way to do this quicker in VBA.
Thanks!

Without VBA
This assumes that there are genuine dates in column E with format dd/mm/yyyy, In G1 enter the Array Formula:
=MAX(IF(A:A=1,E:E,""))
This gives the latest date for employee 1
Array formulas must be entered with Ctrl + Shift + Enter rather than just the Enter key.
Then in G2 enter:
=SUMPRODUCT(--(A1:A9=1)*(E1:E9=G1)*(ROW(1:9)))
This gives the row number of the record you are interested in.
From there you can use INDEX() to get any information from that row.
NOTE:
The formulas in G1 and G2 can be combined into a single cell if desired.
EDIT#1:
The same set of formulas should work with text values for the employee id as well as numbers:

Not sure this is the best solution, but since this is a one-off exercise and it did the trick I used this:
VBA to find all the rows where there was a change in level, and write a "yes" in column "F" where applicable:
Sub JLChange()
Dim Data As Worksheet
Dim i As Long
Dim lastrow As Long
Set Data = ThisWorkbook.Worksheets("Data")
lastrow = Data.Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lastrow
If Cells(i + 1, 4).Value <> Cells(i, 4).Value And_
Cells(i + 1,1).Value = Cells(i, 1).Value Then
Cells(i + 1, 6).Value = "Yes"
Else
Cells(i + 1, 6).Value = "No"
End If
Next i
End Sub
For all the records in column "F", I used the formulas suggested by Gary's student to get the very last change.
Alternatively, you can copy-paste this database of changes in a new sheet, sort by ID and by date of change from newest to oldest, then use vlookup to get the first entry for each ID.

Related

Excel. copy text string and number of repeats to new columns

Given two columns (A and B), one with a text and one with an integer, such as:
A | B
pen | 3
pen | 5
How could I fill the columns C, D, E [...] with the concatenation of the given string on each row with all integers starting from 1 until the specified number?
The desired output for the given example would be:
A | B | C | D | E | F | G
pen | 3 | pen01 | pen02 | pen03 | |
pen | 5 | pen01 | pen02 | pen03 | pen04 | pen05
With a simple vba sub this can be achieved:
Sub CreateValues
With ActiveSheet
Dim LastRow as Long: LastRow = .Range("A" & .Rows.Count).End(xlup).Row
For i = 1 To LastRow
Max_Num = .Cells(i, 2)
For j = 1 to Max_Num
.Cells(i, j + 2) = .Cells(i, 1) & Format(j, "00")
Next j
Next i
End With
End Sub
If you are looking for a formula solution without needing to resort to VBA, you can use this formula in C1 and drag in both dimensions:
=IF(COLUMNS($C1:C1)<=$B1,CONCATENATE($A1,TEXT(COLUMNS($C1:C1),"00")),"")

Attempting to Write a Loop in VBA

I am currently trying to write a short loop to condense a list of received items into a concise itemized report. I scan the barcode of each item I receive and it goes into Column A, if there is a quantity of more than 1 it goes into Column B.
Here is my thought process in order to remove all duplicates of items in column A and combine their totals in B:
Count the numbers of lines in column A, set as 'N'
Check all cells in column B up to 'N' and set blank cells to 1
Compare A1 to A2 thru AN, if the same combine B values and delete the line (If A1 and A2 matched, and both B cell values are 1, then A1 remains the same, B1 now has a value of 2, and the second line gets deleted.)
Repeat the loop for all values of A up to AN-1 compared to AN.
I know N will need to be reduced after each row deletion and I am pretty new to VBA so I always have trouble writing loops.
Any suggestions at pages to look at or simple structures I could use in my code would be greatly appreciated.
EDIT: Trying to turn table 1 into table 2
Table 1 ----------------------------- Table 2
Column A Column B | Column A Column B
11233 | 11233 4
11233 2 | 9987 7
9987 | 7452 1
11233 |
9987 6 |
7452 |
Sub Summator()
ActiveSheet.Columns("A:B").Sort Key1:=ActiveSheet.Range("A2"), Order1:=xlAscending, Header:=xlGuess
lastRow = Range("A65000").End(xlUp).Row
For i = 1 To lastRow
If Cells(i, 2) = "" Then Cells(i, 2) = 1
Next i
For i = lastRow To 2 Step -1
If Cells(i, 1) = Cells(i - 1, 1) Then
Cells(i - 1, 2) = Cells(i - 1, 2) + Cells(i, 2)
Cells(i, 2).EntireRow.Delete
End If
Next i
End Sub

VBA filter result row count wrong

Language: Excel VBA
Scenario:
I have a source range (rngDTRef_AllRecord) that i need to insert the data into the destination range (rngRTDC_AllDetail)
for each of the row(rngCurrRow) in the source range (rngDTRef_AllRecord), it will filter the destination range (rngRTDC_AllDetail)
if the filter yield result, it will add some data to the result row (Note: each of the result is unique)
else it will add a new row to the destination range (rngRTDC_AllDetail)
below is the code:
For Each rngCurrRow In rngDTRef_AllRecord.Rows
intRTDC_RowBegin = 7
intRTDC_ColIdxTotal = 20
intRTDC_RowLast = fntGetNewLastRow 'this is some function get last row of rngRTDC_AllDetail due to might add in new row
Set rngRTDC_AllDetail = shtRTDC.Range(shtRTDC.Cells(intRTDC_RowBegin, 1), shtRTDC.Cells(intRTDC_RowLast, intRTDC_ColIdxTotal))
rngRTDC_AllDetail.AutoFilter
rngRTDC_AllDetail.AutoFilter Field:=intRTDC_ColIdxAcc, Criteria1:=rngCurrRow.Cells(1, intDTSource_ColIdxAccCode), Operator:=xlAnd
rngRTDC_AllDetail.AutoFilter Field:=intRTDC_ColIdxText, Criteria1:=rngCurrRow.Cells(1, strCurrAccCodeText), Operator:=xlAnd
Dim rngResult As Range
Set rngResult = rngRTDC_AllDetail.rows.SpecialCells(xlCellTypeVisible)'rngRTDC_AllDetail.SpecialCells(xlCellTypeVisible) also not work
'after filter, it will be only 1 result or none
If (rngResult.Rows.Count > 0) Then
'if the filter have result, do something here.
else
'add new row
End If
Next
My problem is after the filter, from the excelworksheet, i can see that have only 1 record, but
rngResult.Rows.Count = 2 'for the first filter record (that have 1 row only) in rngRTDC_AllDetail, i suspect due to it include the header, but i am not sure what wrong.
rngResult.Rows.Count = 1 'for the rest of the filter record that have 1 row
even worse is when there is no record after the filter, rngResult.Rows.Count = 1
Any advice will be appreciate. TQ.
Ok. After spent some time on it, I found out the solution already.
Below is some note for who facing similar problem.
Objective:
To insert "value" to columnC, when
columnA = "a" AND columnB ="b" AND the row is between 1 to 10 only
A B C
1 columnA | columnB | ColumnC
2 a | b | value
3 a | x |
4 x | x |
5 x | x |
6 c | b |
7 a | b | value
8 x | x |
9 x | x |
10 a | b | value
11 a | b |
12 a | b |
...
'insert value at columnC
ActiveSheet.Range("A1:B10").AutoFilter Field:=1, Criteria1:="a", Operator:=xlAnd
ActiveSheet.Range("A1:B10").AutoFilter Field:=2, Criteria1:="b", Operator:=xlAnd
Dim rng As Range
For Each rng In ActiveSheet.AutoFilter.Range.Range("A1:B10").SpecialCells(xlCellTypeVisible).Rows
If (rng.Row <> 1) Then 'no the header
ActiveSheet.Cells(rng.Row, "c") = "value" 'set value at C2,C7,C10
End If
Next rng
'count the total row visible
Dim rngA As Range
Set rngA = ActiveSheet.AutoFilter.Range.Range("A1:B10")
Debug.Print rngA.Columns(1).SpecialCells(xlCellTypeVisible).Count - 1 'result 3
'Reference:http://www.contextures.com/xlautofilter03.html
Note1**: "ActiveSheet.AutoFilter.Range" will always include the header and all below row as visible row.
Note2**: "ActiveSheet.AutoFilter.Range.Offset(1, 0).SpecialCells(xlCellTypeVisible).Rows" will offset the range 1 row below only, not suitable if you need to set the value at the result row.

Copy Range, Offset Paste through entire file VBA

Simply, the data looks like this:
ID | Value | Test | Score |
1 | 30 | a | b |
1 | 40 | c | d |
2 | 30 | d | a |
2 | 40 | e | c |
... for 130,000 lines. The value is always 30 or 40, i'd like to replace the 40, Test, and Score and put it in the row with 30 to look like this.
ID | Value | Test | Score | | | |
1 | 30 | a | b | 40 | c | d |
2 | 30 | d | a | 40 | e | c |
I can't seem to wrap my head around the offset-pasting of values. I've tried with Select.Copy,etc -and I have read nothing but bad things about going that route due to performance issues. Any help would be appreciative!
Thanks,
Dan
EDIT: Update -- So I've got it to work, but it will only go through 40 rows before I receive an overflow error. I know this is not optimized or at all the best way to do this - but it was the only thing I could do to figure it out. any input GREATLY appreciative.
I found out that there are a few instances where only 1 ID exists, thus I am checking to see if there are two IDs, if so - copy/paste, otherwise continue to the next row.
Sub Macro4()
' Macro4 Macro
Application.ScreenUpdating = False
Range("H6:K6").Select
Application.CutCopyMode = False
Selection.Copy
Range("L5").Select
ActiveSheet.Paste
Dim r As Integer, ID As Integer, validation As Integer
r = 5
While r < 400
Range("V1:X1").Select
Selection.ClearContents
Range("V1").Select
ID = Cells(r, 1).Value
Selection.Value = ID
Range("W1").Select
ActiveCell.FormulaR1C1 = "=countifs(C[-22],RC[-1])"
validation = Selection.Value
If validation > 1 Then
Cells(r + 1, 8).Select
Range(Selection, Cells(r + 1, 11)).Select
Selection.Copy
Cells(r, 12).Select
ActiveSheet.Paste
r = r + 2
End If
If validation = 1 Then
r = r + 1
End If
Wend
Application.ScreenUpdating = True
End Sub
Assuming ID is in A1, formulae should do it:
in E2 40
in F2 =C3
in G2 =D3
Copy down to suit (ensure 40 does not autoincrement), Select F:G, Paste Special, Values... over the top, filter to select 40 in ColumnB and delete selected rows.
I got it to work by changing:
Dim r As Integer, ID As Integer, validation As Integer
to:
Dim r As Integer, ID as Double, validation As Integer
I read somewhere that double holds significantly larger values -- appreciate all the help, regardless!

Create Formula based on cells

I need to create a Match formula based on the Cells like the example bellow:
+------+--------+--------+
| Col1 | SumCol | Val |
+------+--------+--------+
| 1 | 8 | v1 | '=Match([otherWorkbook]MainSheet!C1,RC3,[otherWorkbook]MainSheet!C8)
| 2 | 5 | v44* | '=Match([otherWorkbook]MainSheet!C2,RC3,[otherWorkbook]MainSheet!C5)
| 3 | 7 | ls* | '=Match([otherWorkbook]MainSheet!C3,RC3,[otherWorkbook]MainSheet!C7)
+------+--------+--------+
To build the formula I need to get the string "[otherWorkbook]MainSheet" and concatenate with Cell from Column Col1. Then i need to get the value from Column Val. Finally I neet to concatenate again the string "[otherWorkbook]MainSheet" with cell from Column SumCol.
I expect a formula like =Match([otherWorkbook]MainSheet!C1,RC3,[otherWorkbook]MainSheet!C8). I could make it using two temp Columns to make the concatenation and then, build the wole formula.
PS: I'm using VBA to make the formula, so i can use it to reach my goal.
Maybe you need something like this :
Sub AddFormulas()
myOtherWorkbook = "[otherWorkbook]"
myFormula1 = "=Match(" & myOtherWorkbook & "MainSheet!C"
myFormula2 = ",RC3," & myOtherWorkbook & "MainSheet!C"
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For x = 2 To lastRow 'Loops from row 2 to the last of column "A"
Cells(x, 4).Formula = myFormula1 & Cells(x, 1) & myFormula2 & Cells(x, 2) & ")"
Next x
End Sub