I need to retrieve the last but one row value in column A/B(Value in Date)
Ex1: Row 3 : last column is AB. I need Row 3 Column AA value (Date) in Row 3 Column C.
Ex 2: Row 4:last column in AS: I need Column AR value (date) in ROw 4 column C.
Ex 3:Row 5:last column in BC: I need Column BB value (date) in ROw 5 column C.
there can b one or 2 blank values.
Please let me know if there are any excel formula to tackle this scenario.
with iteration you could use an array-formula like: (for row 3)
=INDIRECT("R3C"&MAX(COLUMN(3:3)*(LEN(3:3)>0))-1, 0)
without you need to exclude the cell itself... having it at column C it would be something like: (for row 3)
=INDIRECT("R3C"&MAX(COLUMN($D3:$ZZ3)*(LEN($D3:$ZZ3)>0))-1, 0)
if you want to simply auto fill the formula down then replace the "R3C" with "R"&ROW()&"C"
The formulas shown here are array formulas and must be confirmed with Ctrl+Shift+Enter.
If you get inclined to VBA, you could try something like this:
' In C3 type =GetSecondLastColumnValue()
' This function will keep on checking values on column at a time to the right
' If 3 continuous empty cells are found, it will assume that there are no more
' columns with values after that. The last known cell with value will be identified
' and value of column just to its left will be plugged in to column C
Public Function GetSecondLastColumnValue()
Dim Looper As Integer
Looper = 1
Const NoOfContinuousEmptyColumns As Integer = 3
Dim m_Address As String, m_LastKnownColumnWithValue
m_Address = Selection.Address
m_LastKnownColumnWithValue = m_Address
Do
m_Address = Range(m_Address).Offset(0, 1).Address
If Range(m_Address).Value = "" Then
Looper = Looper + 1
If Looper > NoOfContinuousEmptyColumns Then Exit Do
Else
Looper = 1
m_LastKnownColumnWithValue = m_Address
End If
Loop
m_LastKnownColumnWithValue = Range(m_LastKnownColumnWithValue).Offset(0, -1).Address
GetSecondLastColumnValue = Range(m_LastKnownColumnWithValue).Value
End Function
Example:
A B C D E F
1 abc def ab cd
2 abc def xy zx st
In C1, type =GetSecondLastColumnValue () and it will be populated with ab.
In C2, type the same formula and it will be populated with zx
This is just an example of what you can do with VBA. Do NOT use this function in production. It is insanely greedy and slow. It's just an illustration.
This subroutine is much faster:
' This method keeps on going to far right till it reaches the end of the columns
' Then it comes back to the last column with value, and hops to the column to its left, and remembers its value
' That value goes in column C. It loops through all rows you specify in startrow and endrow
Sub PopulateColumnC()
Dim StartRow As Integer, EndRow As Integer, RowLoop As Integer
StartRow = 1
EndRow = 3
Dim m_Address As String
For RowLoop = StartRow To EndRow
Range("A" & RowLoop).Select
m_Address = Selection.Address
Do
Selection.End(xlToRight).Select
If m_Address = Selection.Address Then
Selection.End(xlToLeft).Select
Exit Do
Else
m_Address = Selection.Address
End If
Loop
m_Address = Range(Selection.Address).Offset(0, -1).Address
Range("C" & RowLoop).Value = Range(m_Address).Value
Next
End Sub
Related
is there anyway to return multiple values from a vlookup? I would like col I in Sheet 1 to return multiple values to a cell, or is there another way of displaying this (would rather not pivot)?
Sheet 1 : has all my unique values (Col F, and returning values in Col I),
Sheet 3: Col A has duplicate string values which correspond to unique strings in Col B which are unique, including blanks.
EDIT
Sheet 1 or desired result :
Sheet 1: Current
Sheet 3 Current:
Current formula
=VLOOKUP(F2,Sheet3!A:B,2,FALSE)
Returns mostly 0's, due to the blanks or multiple values corresponding to the unique values.
In terms of VBA then, you have to change the code a bit from what was in the link I sent you. This should work:
Option Explicit
Function vlookupmulti(rngLookup As Variant, rngSource As Range, col As Double) As String
Dim d As Double, strCell As String
'Error if range has less columns than col
If rngSource.Columns.Count < col Then
vlookupmulti = CVErr(xlErrNA)
Exit Function
End If
'Loop through rows in the lookup column
For d = rngSource.Row To rngSource.Rows.Count
If rngLookup = Sheets(rngSource.Parent.Name).Cells(d, rngSource.Column).Value Then
strCell = Sheets(rngSource.Parent.Name).Cells(d, rngSource.Column + col - 1).Value
If Len(strCell) > 0 Then vlookupmulti = vlookupmulti & strCell & ", "
End If
Next d
'Remove comma at end
If Right(vlookupmulti, 2) = ", " Then
vlookupmulti = Left(vlookupmulti, Len(vlookupmulti) - 2)
End If
'Give error if no results
If vlookupmulti = "" Then
vlookupmulti = CVErr(xlErrNA)
End If
End Function
I have wbk1.worksheet(1) and wbk2.worksheet(1).
wbk1.worksheet(1) has a list of values in column A
wbk2.worksheet(2) has the same list of values that may occur multiple times in column A with a number value in the offset(0,1) cell.
I need to do an index or match to find all of values in wbk2 and sum all of the offset(0,1) values. Then take that sum and put it in the offset(0,1) cell in wbk1.worksheets(1).
Example:
Workbook 1, sheet 1
Column A Column B
value 1
value 2
value 3
Workbook 2, sheet 1
Column A Column B
value 1 15
value 2 2
value 1 3
value 1 12
End Result:
Workbook 1, sheet 1
Column A Column B
value 1 30
value 2 2
value 3 0
I've tried doing a for each loop, but I'm still a noob to vb, so clearly not doing something right:
For Each x In rngWbk1
Set cellrngwbk2 = wbk2.Worksheets(1).Cells.Find(What:=x, LookIn:=xlValues)
If Not cellrngwbk2 Is Nothing Then
For Each y In rngwbk1
If y = cellrngwbk2 Then
total = total + cellrngwbk2.Offset(0, 1).Value
Else
End If
Next y
x.Offset(0, 1).Value = total
total = 0 'resets total value for next x value
Else
End If
next x
If VBA is not a requirement, a simple =SUMIF() statement has the same effect.
The function would look something like this:
=SUMIF([Wbk2.xlsx]Sheet1!A2:A5,Table1[[#This Row],[ID]],[Wbk2.xlsx]Sheet1!B2:B5)
There is more efficient way. You can use SUMIF formula to calculate values and then rewrite formula with result values. If rngWbk1 corresponds to the values in column A in wbk1.worksheets(1), you can use following code:
Dim frm As String
Dim startCell As String
startCell = Replace(rngWbk1.Cells(1, 1).Offset(0, -1).Address, "$", "")
frm = "=SUMIF('[" & wbk2.Name & "]" & wbk2.Worksheets(1).Name & "'!A:A," & startCell & ", '[" & wbk2.Name & "]" & wbk2.Worksheets(1).Name & "'!B:B)"
With rngWbk1.Offset(0, 1)
.Formula = frm
.Calculate
.Value = .Value
End With
If rngWbk1 doesn't correspond values in column A, you need to adjust startCell in example to startCell = "A2" and change With rngWbk1.Offset(0, 1) to sth like With wbk1.Worksheets(1).Range("B1:B100")
I have two columns of data as follows:
A1: A; A2: B; A3: C and B1: 1; B2:2; B3:3
Is there a simple loop in VBA to have on column C the concatenated values as:
C1: A1; C2: A2; C3: A3; C4: B1; C5: B2; C6: B3; etc?
I would use modulo arithmetic to achieve this. The subroutine below assumes that the last row in column C that needs to be populated is lLastRow (I set it =25 for testing):
Sub test()
Dim lLastRow As Long, ACnt As Long, BCnt As Long
ACnt = 1
' Last row of column C that needs to be filled with Data
lLastRow = 25
For BCnt = 1 To lLastRow
' Use modulo arithmetic to point to the right cell in column B
Cells(BCnt, 3) = Cells(ACnt, 1) & IIf(BCnt Mod 3 <> 0, BCnt Mod 3, 3)
' We should increment column A every three rows
ACnt = IIf((Cells(BCnt, 3).Row) Mod 3 = 0, ACnt + 1, ACnt)
' Reset column A pointer when we go beyond 3
ACnt = IIf(ACnt = 4, 1, ACnt)
Next BCnt
End Sub
Column B number:
The idea is that (starting from the first row), the modulo of each row and 3 will be 0,1,2 and will recycle in this fashion forever. Whenever the row number is a multiple of 3, the modulo returns 0 and therefore we need to refer to 3 on column B (i.e., cell (B3). Else, modulo returns exactly what we want (which is either 1 or 2).
Column A number:
This is simpler: every 3rd row we switch row.. So when (Cells(BCnt, 3).Row) Mod 3 = 0, we increment the pointer of column A. Of course, when we reach the 4th row, we need to return back to the 1st row.
I hope this helps!!
I prefer to avoid VBA when possible, so given a data setup like this:
The formula in cell C1 and copied down is:
=IF(ROW(A1)>COUNTA(A:B),"",INDEX(A:B,MOD(ROW(A1)-1,COUNTA(A:A))+1,1+(ROW(A1)>COUNTA(A:A))))
EDIT
As an alternate interpretation, pointed out by Ioannis, given a data setup like this:
The formula in cell C1 and copied down is:
=IF(ROW(A1)>COUNTA(A:A)*COUNTA(B:B),"",INDEX(A:A,INT((ROW(A1)-1)/COUNTA(B:B))+1)&INDEX(B:B,1+MOD(ROW(A1)-1,COUNTA(B:B))))
I can't see it any simpler than below
Sub concat()
Dim i As Integer
With ActiveSheet
For i = 1 To .UsedRange.Rows.Count
.Cells(i, 3) = .Cells(i, 1) & .Cells(i, 2)
Next i
End With
End Sub
Or maybe there is:
Sub concat2()
With ActiveSheet
.Range("C1") = "=CONCATENATE(RC[-2],RC[-1])"
.Range("C1").AutoFill Destination:=.Range("C1:C" & .UsedRange.Rows.Count)
End With
End Sub
That would also make non-vba users understand what column C actually is ;)
Can't you do something like:
Dim i,j AS int
For i = 1 to 3
For j = 1 to 6
NewVal = C(j):A(i)
Next j
Next i
This is untested, but gives you direction of where to go..
I'm trying to generate words in Column B from a list of given words in Column A.
Right now my code in Excel VBA does this:
Function GetText()
Dim GivenWords
GivenWords = Sheets(1).Range(Sheets(1).[a1], Sheets(1).[a20])
GetText = A(Application.RandBetween(1, UBound(A)), 1)
End Function
This generates a word from the list I have provided in A1:A20, but I don't want any duplicates.
GetText() will be run 15 times in Column B from B1:B15.
How can I check for any duplicates in Column B, or more efficiently, remove the words temporarily from the list once it has been used?
For example,
Select Range A1:A20
Select one value randomly (e.g A5)
A5 is in Column B1
Select Range A1:A4 and A6:A20
Select one value randomly (e.g A7)
A7 is in Column B2
Repeat, etc.
This was trickier than I thought. The formula should be used as a vertical array eg. select the cells where you want the output, press f2 type =gettext(A1:A20) and press ctrl+shift+enter
This means that you can select where your input words are in the worksheet, and the output can be upto as long as that list of inputs, at which point you'll start getting #N/A errors.
Function GetText(GivenWords as range)
Dim item As Variant
Dim list As New Collection
Dim Aoutput() As Variant
Dim tempIndex As Integer
Dim x As Integer
ReDim Aoutput(GivenWords.Count - 1) As Variant
For Each item In GivenWords
list.Add (item.Value)
Next
For x = 0 To GivenWords.Count - 1
tempIndex = Int(Rnd() * list.Count + 1)
Aoutput(x) = list(tempIndex)
list.Remove tempIndex
Next
GetText = Application.WorksheetFunction.Transpose(Aoutput())
End Function
Here's how I would do it, using 2 extra columns, and no VBA code...
A B C D
List of words Rand Rank 15 Words
Apple =RAND() =RANK(B2,$B$2:$B$21) =INDEX($A$2:$A$21,MATCH(ROW()-1,$C$2:$C$21,0))
copy B2 and C2 down as far as the list, and drag D down for however many words you want.
Copy the word list somewhere, as every time you change something on the sheet (or recalculate), you will get a new list of words
Using VBA:
Sub GetWords()
Dim Words
Dim Used(20) As Boolean
Dim NumChosen As Integer
Dim RandWord As Integer
Words = [A1:A20]
NumChosen = 0
While NumChosen < 15
RandWord = Int(Rnd * 20) + 1
If Not Used(RandWord) Then
NumChosen = NumChosen + 1
Used(RandWord) = True
Cells(NumChosen, 2) = Words(RandWord, 1)
End If
Wend
End Sub
Here is the code. I am deleting the cell after using it. Please make a backup of your data before using this as it will delete the cell contents (it will not save automatically...but just in case). You need to run the 'main' sub to get the output.
Sub main()
Dim i As Integer
'as you have put 15 in your question, i am using 15 here. Change it as per your need.
For i = 15 To 1 Step -1
'putting the value of the function in column b (upwards)
Sheets(1).Cells(i, 2).Value = GetText(i)
Next
End Sub
Function GetText(noofrows As Integer)
'if noofrows is 1, the rand function wont work
If noofrows > 1 Then
Dim GivenWords
Dim rowused As Integer
GivenWords = Sheets(1).Range(Sheets(1).Range("A1"), Sheets(1).Range("A" & noofrows))
'getting the randbetween value to a variable bcause after taking the value, we can delete the cell.
rowused = (Application.RandBetween(1, UBound(GivenWords)))
GetText = Sheets(1).Range("A" & rowused)
Application.DisplayAlerts = False
'deleting the cell as we have used it and the function should not use it again
Sheets(1).Cells(rowused, 1).Delete (xlUp)
Application.DisplayAlerts = True
Else
'if noofrows is 1, there is only one value left. so we just use it.
GetText = Sheets(1).Range("A1").Value
Sheets(1).Cells(1, 1).Delete (xlUp)
End If
End Function
Hope this helps.
I have data in (Sheet4) columns A to I:
I'm trying to compare data for all rows (Only on column A and B) to see if any of the rows is duplicated, if it is: excel should highlight both rows.
Example:
A B C......I
s 1 x
s 3 w
e 5 q
s 1 o
Row 1 and 4 should be highlighted as values are the same for column A and B.
I shouldn't modify the sheet (no modification to the columns or rows should be done to the sheet), and the number of rows is not always known (not the same for all files).
Is there an easy way (using macros) to do this???
This is an attempt I have tried, but it is increasing my file to 7MB!!!!! I'm sure there should be an easier way to compare rows for an unknown number of rows and just highlight the dupllicates if they exist:
Public Sub duplicate()
Dim errorsCount As Integer
Dim lastrow As Integer
Dim lastrow10 As Integer
errorsCount = 0
lastrow = Sheet4.Cells(Rows.Count, "A").End(xlUp).Row 'is the row number of the last non-blank cell in the specified column
lastrow10 = lastrow
Sheet10.Range("B1:B" & lastrow10).Value = Sheet4.Range("A1:A" & lastrow).Value
Set compareRange = Sheet10.Range(column + "2:" & Sheet10.Range(column + "2").End(xlDown).Address)
For Each a In Sheet10.Range(column + "2:" & Sheet10.Range(column + "2").End(xlDown).Address)
c = a.Value
If c <> Null Or c <> "" Then
If name = "testing" Then
If WorksheetFunction.CountIf(compareRange, c) > 1 Then
a.Interior.ColorIndex = 3
errorsCount = errorsCount + 1
End If
End If
End If
Next a
If errorsCount > 0 Then
MsgBox "Found " + CStr(errorsCount) + " errors"
Else
MsgBox " No errors found."
End If
End Sub
Silly answer to you.
J1 or just duplicate sheet.
J1 =CONCATENATE(A1,"#",B1) > drag down > J:J > conditional format > highlight cells rules > duplicate values.
(* replace the # to any string which you think not possible in the original A:A and B:B.)
I do this all the time.
To collect all duplicates just SORT with color.