Excel formula loop until condition - excel

I am concatenating every columns of the rows on excel sheet and I am already done with the concatenating of every columns.
My problem is I only need to concatenate the rows that does not contain letter T on column A starting row 3. please see image below
sample formula
=Sheet1!A3&Sheet1!B3&Sheet1!C3&Sheet1!D3&Sheet1!E3&Sheet1!F3&Sheet1!G3&Sheet1!H3&Sheet1!I3&Sheet1!J3&Sheet1!K3&Sheet1!L3&Sheet1!M3&Sheet1!N3&Sheet1
on that image, you can see the result below of the concatenated columns from the above details, but not all the time the rows to be concatenated there has the same number(like on the above image that has only 3 rows to be computed), do you know some code or formula for this matter?

You can try
=TEXTJOIN(A1:N1)
Or whatever your range should be.
But dynamically in VBA it done like this:
Dim row, col As Integer
Dim curStr As String
row = 1
col = 1
curStr = ""
Do While Sheets("Input").Cells(row, col) <> ""
Do While Sheets("Input").Cells(row, col) <> ""
curStr = curStr + Sheets("Input").Cells(row, col)
col = col + 1
Loop
col = 1
Sheets("Output").Cells(row, col) = curStr
row = row + 1
curStr = ""
Loop

Use this UDF - User Defined Function
Function conc(rangetoconc As Range) As String
Dim finalresult As String
Dim cell As Range
finalresult = vbNullString
If InStr(1, rangetoconc.Cells(1, 1), "T") = 0 Then
For Each cell In rangetoconc
If CStr(cell.Value) <> vbNullString And CStr(cell.Value) <> " " Then
finalresult = finalresult & CStr(cell.Value)
End If
Next
End If
conc = finalresult
End Function

I just figured out the answer to my question after reading some excel formula online.
=IF(A1="","",
IF(LEFT(A1,1)="T","",
IF('Sheet1'!A2<>"",'Sheet1'!A2&'Sheet1'!B2&'Sheet1'!C2&'Sheet1'!D2&'Sheet1'!E2&'Sheet1'!F2&'Sheet1'!G2&'Sheet1'!H2&'Sheet1'!I2&'Sheet1'!J2&'Sheet1'!K2&'Sheet1'!L2&'Sheet1'!M2&'Sheet1'!N2&'Sheet1'!O2&'Sheet1'!P2&'Sheet1'!Q2&'Sheet1'!R2&'Sheet1'!S2,"T "&MIN(ROW(A2:A3))+ROWS(A2)-3 & " sum of invoice "& SUM('Sheet1'!H:H))))

Related

VBA: How can I fill in cells in one column based on the collective information of iterative ranges?

I need to fill in Col H (see red text in image for an example) as follows:
There are 3 subjects listed (separated by grey background) (Col C for number)
Each subject has multiple data points (one per row-Col D for data point "name")
(not shown) each subject multiple data points is run on multiple tests and sorted by test, subject, timepoint. (See Col E for Test), which means each "unifying ID"/"Subject ID" is used more than once. This data should be considered separately (for example, subject 8 Adiponectin results should not be compared with subject 8 Areg data)
Some of the data is not detected by the test and is marked in both Col J ("<LLOQ") and Col I ("Yes" for <LLOQ aka not detected).
I need help designing a program that answers if "all samples from this subject (and test) are below LLOQ?". Thus the program needs to detect that each subject's data must be viewed in a chunk fill out Col H "All Samples below LLOQ?" before moving on to the next subject. If there are no <LLOQ samples in the range, then each cell in Col H will be "No". If there are some samples <LLOQ and some samples that are NOT <LLOQ, then each cell in Col H within the range will be "No" (see grey subject). However, if All samples for a subject are <LLOQ, then the values in H must be "Yes" for all cells within the range.
In another Sub() I figured out how to reset values for each new subject using "C01D01.00" as a reset cue. This works to fill in data that is not reliant on the cells in a range (such as "Is the Baseline below LLOQ?" in col G. But I cannot figure out how to "set" a range, read through the range, identify if any cells are "no" in Col I and then return "no" in Col H (or "yes" in Col H if there are no "no" in Col I with in the range, and then move onto the next "range"). Ideas?
See below for how I programmed Col G.
Sub BaselineBelowLLOQ()
Sheets("Cyt-Data").Activate
Dim NewSubject As String
Dim SubjectBL As String
Dim BaselineRow As Integer
For i = 2 To 1000000
If Sheets("Cyt-Data").Cells(i, 2).Value = "" Then
Exit For
End If
NewSubject = Cells(i, 3).Value
If Not SubjectBL = NewSubject And Cells(i, 4).Value = "C01D01.00" Then
SubjectBL = NewSubject
BaselineRow = i
ElseIf Not SubjectBL = NewSubject And Not Cells(i, 4).Value = "C01D01.00" Then
SubjectBL = ""
End If
If Not SubjectBL = "" Then
If Cells(BaselineRow, 9).Value = "Yes" Then
Cells(i, 7).Value = "Yes"
Else
Cells(i, 7).Value = "No"
End If
End If
Next i
End Sub
Something like this should work:
Sub BaselineBelowLLOQ()
Dim ws As Worksheet, i As Long, dict As Object, k As String
Dim subjId, testName, num1 As Long, num2 As Long
Set dict = CreateObject("scripting.dictionary")
Set ws = ThisWorkbook.Worksheets("Cyt-Data") 'or ActiveWorkbook...
For i = 2 To ws.Cells(Rows.Count, "B").End(xlUp).Row
subjId = ws.Cells(i, "C").Value
testName = ws.Cells(i, "E").Value
k = subjId & "<>" & testName 'SubjectId<>TestName combination
If Not dict.exists(k) Then 'new combination?
'count all rows for this combo
num1 = Application.CountIfs(ws.Columns("C"), subjId, _
ws.Columns("E"), testName)
'count rows for this combo with "Yes" in Col I
num2 = Application.CountIfs(ws.Columns("C"), subjId, _
ws.Columns("E"), testName, _
ws.Columns("I"), "Yes")
dict.Add k, IIf(num1 = num2, "Yes", "No") 'compare counts for this combo
'and store the Yes/No outcome
End If
'tag the row using the value we already figured out
ws.Cells(i, "H").Value = dict(k)
Next i
End Sub

Excel VBA - Loop a specific column in structured table and get corresponding cells values from other columns

I start using structured table with VBA code, but here I face a problem which I do not manage by myself.
I have a structured table in which I loop through 1 column and need to get values from other columns depending on some criteria:
if the cells value is "Finished"
then I take 3 dates (dateScheduled, dateRelease and dateReady) from 3 other columns and perform some calculations and tests based on these dates
the problem is that I can get the values of the date columns (they are well formatted and have values in it), so none of the next actions triggered by the first if is working.
Here is part of the whole code of my macro, I hope this is sufficient to figure out what is wrong.
For Each valCell In Range("thisIsMyTable[Task Status]").Cells
If valCell = "Finished" Then
dateScheduled = Range("thisIsMyTable[End Date]").Cells
dateRelease = Range("thisIsMyTable[Release Date]").Cells
dateReady = Range("thisIsMyTable[Date Ready]").Cells
totalFinishCat = totalFinishCat + 1
daysToFinished = daysToFinished + DateDiff("d", dateReady, dateRelease)
If Range("thisIsMyTable[Time Spent]").Cells = "" Then
timeTotalFinished = timeTotalFinished + Range("thisIsMyTable[Time estimate]").Cells + Range("thisIsMyTable[Extra hours]").Cells
Else
timeTotalFinished = timeTotalFinished + Range("thisIsMyTable[Time Spent]").Cells
End If
If dateRelease >= dateStartReport Then
monthFinished = monthFinished + 1
timeMonthFinished = timeMonthFinished + Range("thisIsMyTable[Time Spent]").Cells
daysToFinishedMonth = daysToFinishedMonth + DateDiff("d", dateReady, dateRelease)
If dateRelease > dateScheduled Then
afterDue = afterDue + 1
diff = DateDiff("d", dateScheduled, dateRelease)
afterDay = afterDay + diff
Else
beforeDue = beforeDue + 1
diff = DateDiff("d", dateRelease, dateScheduled)
beforeDay = beforeDay + diff
End If
End If
End If
Next valCell
I have tried out by adding .value or .value2 like so:
dateScheduled = Range("thisIsMyTable[End Date]").Cells.value
or
dateScheduled = Range("thisIsMyTable[End Date]").Cells.value2
but it does not work better. I have checked by adding .select like so:
dateScheduled = Range("thisIsMyTable[End Date]").Cells.select
and this will select the entire column, not the cells as I expect. So it appears that my method to just get the cells value is not appropriate.
Any help is welcome
If you create a lookup of column names to column number, you can loop through the rows of the table and extract the value using Range(1, columnno). For example
Option Explicit
Sub MyMacro()
Dim ws As Worksheet, tb As ListObject
Dim r As ListRow
Dim sStatus As String, dEnd As Date, dRelease As Date, dReady As Date
' table
Set ws = Sheet1
Set tb = ws.ListObjects("thisIsMyTable")
' lookup column name to number
Dim dict As Object, c As ListColumn
Set dict = CreateObject("Scripting.Dictionary")
For Each c In tb.ListColumns
dict.Add c.Name, c.Index
Next
' scan table rows
For Each r In tb.ListRows
sStatus = r.Range(1, dict("Task Status"))
If sStatus = "Finished" Then
dEnd = r.Range(1, dict("End Date"))
dRelease = r.Range(1, dict("Release Date"))
dReady = r.Range(1, dict("Date Ready"))
Debug.Print dEnd, dRelease, dReady
End If
Next
End Sub

Vlookup, return multiple values to a cell

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

Excel VBA unique ID generator

I am trying to generate unique IDs for topics of discussion. The data will be like so:
Status ID Topic Commentary
Open FIL-1 FILM
Open FIL-2 FILM
Closed LAN-1 LANG.
Open LAN-2 LANG.
The idea is that when on a new row regardless of whether it was added above or below of the last unique ID I use VBA to find the next ID. So for example above if I were to add another row at the top with the topic LANG. then it would find that LAN-2 is the lastest ID and +1 to it to become LAN-3.
I got this working when the topics were all the same with the code below (topics were all "FIL" but now there are multiple topics):
Private Function getNextID() As String
Dim row As Integer
Dim currentID As Integer
currentID = 0
' Loop round rows
For row = MIN_ROW To MAX_ROW
' Only use rows which are not blank
If Worksheets(DISCUSS).cells(row, ID).Value <> "" Then
If Mid$(Worksheets(DISCUSS).cells(row, ID).Value, InStr(3, Worksheets(DISCUSS).cells(row, ID).Value, "-") + 1) > currentID Then
currentID = Mid$(Worksheets(DISCUSS).cells(row, ID).Value, InStr(3, Worksheets(DISCUSS).cells(row, ID).Value, "-") + 1)
End If
End If
Next row
getNextID = "FIL" & "-" & currentID + 1
End Function
Does anyone know how I can set an array with the topic abbreviations used in the ID and use the code i've already written to loop through the same process using the abbreviations in the array to get the next ID for the specific topic being added?
I adjusted the code you had to include an array like you needed, it does mean you will have to pass into your procedure the name of the topic you are requesting an ID for, this could be automated if needed but its hard to know what the bigger picture is for you project so I have left it as this: -
Private Function getNextID(ByVal StrTopic As String) As String
Static AryTopics(2, 1) As String
Dim row As Integer
Dim currentID As Integer
Dim LngCounter As Long
currentID = 0
'By having the array declared static and being a fixed size, it will only get built once
'then rememebered
If AryTopics(0, 0) = "" Then
AryTopics(0, 0) = "FILM"
AryTopics(0, 1) = "FIL"
AryTopics(1, 0) = "LANG."
AryTopics(1, 1) = "LAN"
AryTopics(2, 0) = "GEOG."
AryTopics(2, 1) = "GEO"
End If
'The topic must be passed into the proce to know what to get the ID for
'This gets the related topic code from the array
For LngCounter = 0 To UBound(AryTopics, 1)
If AryTopics(LngCounter, 0) = Trim(UCase(StrTopic)) Then
StrTopic = AryTopics(LngCounter, 1)
Exit For
End If
Next
' Loop round rows
For row = MIN_ROW To MAX_ROW
' Only use rows which are not blank
If Worksheets(DISCUSS).Cells(row, ID).Value <> "" Then
'This checks to see if the ID starts with the related topic code we care about, if it does then we keep checking
If Left(Trim(UCase(Worksheets(DISCUSS).Cells(row, ID).Value)), Len(StrTopic) + 1) = StrTopic & "-" Then
If Mid$(Worksheets(DISCUSS).Cells(row, ID).Value, InStr(3, Worksheets(DISCUSS).Cells(row, ID).Value, "-") + 1) > currentID Then
currentID = Mid$(Worksheets(DISCUSS).Cells(row, ID).Value, InStr(3, Worksheets(DISCUSS).Cells(row, ID).Value, "-") + 1)
End If
End If
End If
Next row
'Output include the topic code
getNextRiskID = StrTopic & "-" & currentID + 1
End Function
This code does the trick, except the first entry for some reason (the Evaluate Formula button shows it's working, but right at the end it replaces the value with 0).
So, manually add the first ID then run the code from row 3 to the last row of your list (you'll also need to add code to ignore empty rows).
Public Sub Test()
Dim x As Long
For x = 3 To 7
AddID ThisWorkbook.Worksheets("Sheet1").Cells(x, 2)
Next x
End Sub
Public Sub AddID(Target As Range)
'Formula using A1 style:
'=LEFT($C7,3) & "-" & COUNTIF($B$2:INDEX($B:$B,ROW()-1),LEFT($C7,3) & "*")+1
'Relative column (ID is 1 column left of Topic).
Target.FormulaR1C1 = "=LEFT(RC[1],3) & ""-"" & COUNTIF(R2C:INDEX(C,ROW()-1), LEFT(RC[1],3) & ""*"")+1"
'Absolute column (ID is column B, Topic is column C)
'Target.FormulaR1C1 = "=LEFT(RC3,3) & ""-"" & COUNTIF(R2C2:INDEX(C2,ROW()-1), LEFT(RC3,3) & ""*"")+1"
Target = Target.Value
End Sub

Excel macro to loop through range until value found, populate range below found cell with formula

I have a large table, sometimes with hundreds of rows.
This table is generated by another application that exports to excel.
One column has the heading "Adjusted Price".
I want all the rows in this column to contain a formula (presently they're all 0's).
I want to automate this process because the table gets regenerated all the time.
This column may not always be in the same place. So I need a macro that can find this column ("Adjusted Price") and then fill all the cells in that column with a formula (with the exception of the first row of course).
Can this be done?
Thanks in advance :)
Your homework is to figure out how to plug it in!
Option Explicit
Sub setAdjustedPrice()
Dim column As Integer
Dim adjustedPriceColumn As String
Dim found As Boolean
Dim rowCount As Long
column = 1
rowCount = 1
Do While Range(FncAlphaCon(column) & rowCount).Value <> "" And found = False
If (Range(FncAlphaCon(column) & rowCount).Value = "Adjusted Price") Then
found = True
adjustedPriceColumn = FncAlphaCon(column)
Else
column = column + 1
End If
Loop
If found = True Then
Do While rowCount < ActiveSheet.UsedRange.Rows.count
rowCount = rowCount + 1
Range(adjustedPriceColumn & rowCount) = "YOUR FORMULA"
Loop
Else
MsgBox ("'Adjusted Price' column not found, cannot continue.")
End If
End Sub
Private Function FncAlphaCon(aNumber As Integer) As String
' Fixed version 27/10/2011
Dim letterArray As String
Dim iterations As Integer
letterArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
If aNumber <= 26 Then
FncAlphaCon = (Mid$(letterArray, aNumber, 1))
Else
If aNumber Mod 26 = 0 Then
iterations = Int(aNumber / 26)
FncAlphaCon = (Mid$(letterArray, iterations - 1, 1)) & (Mid$(letterArray, 26, 1))
Else
'we deliberately round down using 'Int' as anything with decimal places is not a full iteration.
iterations = Int(aNumber / 26)
FncAlphaCon = (Mid$(letterArray, iterations, 1)) & (Mid$(letterArray, (aNumber - (26 * iterations)), 1))
End If
End If
End Function

Resources