So basically, I have a table, where in Column A, I have a repeated list of data with multiple duplicates. In Column B, I have different sets of data related to column A, and then same as Column C. What I wish to have excel do for me is to give me the table (A15:C18), where the Column A data is one cell for each value, and then all the related information is shown right next to it in a concatenated manner.
My approach so far
So basically, what I have done so far is the following approach:
Since Column A are the only thing common in defining the different
lines, so I came up with a way to define each line as a unique line,
using the following =(A1&"--"&countif($A$1:A1;A1). This formula then
helps me identify each row as A--1, A--2, A--3, B--1, B--2 and so
on. Hence I have numbered each data in each row, in a way.
Since I want a unique list, therefore using VBA I get a unique list of variables in a new sheet. Hence, I get the list of just A, B and C in a column instead of them being duplicates.
Once I have the unique list, I then use the =countif() function to count how many times, the variable shows up in the original list (Column A). Like for example, A shows up three times, hence I know that I need to extract 3 rows for this specific data point.
Then using that information, I then proceed to use the =vlookup() function, using the equation =VLOOKUP(A&"--"&1;TABLE;COLUMN)&", "&VLOOKUP(A&"--"&2;TABLE;COLUMN)&", "&VLOOKUP(A&"--"&3;TABLE;COLUMN) .... so on depending on how many times the variable is in the list.
Finally, for example, I then write the vlookup code, and I can then extract all the cells into one place.
PROBLEMS and HELP
So the above approach I have described works on a pilot scale with lots of manual changes for different data points. The problems are the following:
The process of using =vlookup() is based on the =countif() function. I wrote down a very long formula using IF statements saying that =IF(COUNTIF()=1;VLOOKUP(VAR&--&1;TABLE..);IF(COUNTIF()=2;VLOOKUP(VAR&--&1;TABLE..)&","&VLOOKUP(VAR&--&2;TABLE..); ...
Basically, I wrote the IF statement depending on the value of the countIf value. If it is 1, then extract the values for --1, but if it is 2, then do it for --1 and --2, and vice versa. But this is such a bad approach because I can not write this equation for 100 duplicates and if a data point shows up more than 100 Times, then this approach is useless. hence, I would like to know, if it is somehow possible to use a loop VBA code for excel to do a vlookup? So if countif is 4, then do a vlookup from --1 until --4 and so on?
Another limtation is that even though B shows up 2 times in column A, but it only has 1 data point in Column B, and that is the information I need. Hence I should be focusing on that instead of using the =countif() on Column A. **Any suggestions on how I can count, how much information is infront of a data point in the second column? The data in Columns B and C are unique so there are no duplicates. **
I am stuck with the above two points, which kind of makes up the engine of the workbook. So any help or suggestion on how to approach this problem would be greatly appreciated!
Below is the updated image to show the general approach:
Interesting problem, here's my approach.
Pre-requisites:
You need Microsoft Scripting Runtime: Tools -> References -> Microsoft Scripting Runtime -> check the box
There are 2 components of this code.
First the sub you will run:
Sub Concatenate_Data()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("YourWorksheetsName")
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim r As Long
Dim dict As New Scripting.Dictionary
Dim letterStr As String
For r = 2 To lastRow
letterStr = ws.Cells(r, 1).Value
If dict.Exists(letterStr) Then
If Not ws.Cells(r, 2).Value = "" Then
dict(letterStr).Candy = dict(letterStr).Candy & ", " & ws.Cells(r, 2).Value
End If
If Not ws.Cells(r, 3).Value = "" Then
dict(letterStr).Juice = dict(letterStr).Juice & ", " & ws.Cells(r, 3).Value
End If
Else
dict.Add letterStr, New Letter
If Not ws.Cells(r, 2).Value = "" Then
dict(letterStr).Candy = ws.Cells(r, 2).Value
End If
If Not ws.Cells(r, 3).Value = "" Then
dict(letterStr).Juice = ws.Cells(r, 3).Value
End If
End If
Next r
Dim k As Variant
r = 2
For Each k In dict.Keys
ws.Cells(r, 5).Value = k
ws.Cells(r, 6).Value = dict(k).Candy
ws.Cells(r, 7).Value = dict(k).Juice
r = r + 1
Next
End Sub
Next is the Class (it is named Letter):
Public Candy As String
Public Juice As String
Here is the input and output I get:
Good Luck!
Related
I have to average sets of 3 columns.
EXAMPLE:
Blood_Patient1_0_R1, Blood_Patient1_0_R2, Blood_Patient1_0_R3
There average is in a new column Blood_Patient1_0
Similarly, Blood_Patient1_3_5_R1, Blood_Patient1_3_5_R2, Blood_Patient1_3_5_R3
The average is in a new column Blood_Patient1_3_5
This process is being repeated for 8 such sets of columns.
Currently I am averaging using the formula: IF(ISERROR(AVERAGE(B7:D7)),"",AVERAGE(B7:D7)) and auto-filling 21,000 plus rows.
Since there is a pattern in column headings, I was thinking to automate the whole process.
This is what I have thought so far in terms of algorithm:
0, 3_5, 6_25 are time values in column headers.
at each time instant, there are 3 replicates R1, R2,R3 as part of column headers
for time array [3.5h, 6.25h, 9.5h, 11.5h, 16.5h, 25h, 49h, and 156h
]
create a new column
for rows from 2 to 21458
average over replicates from R1 to R3 using above formula
I do not know how to write this in excel. Any help would be appreciated.
Give this a go.
This solution assumes that you have a continuous data set, that is, no gaps between the columns you wish to search through.
Firstly, you will need to include this function. Paste it into the same module as the subroutine. The purpose of this function is to allow the string in each heading to be compared against an array of substrings, as opposed to the single substring permitted by the InStr function.
Function Time_Search(strCheck As String, ParamArray anyOf()) As Boolean
Dim item As Long
For item = 0 To UBound(anyOf)
If InStr(1, strCheck, anyOf(item)) <> 0 Then
Time_Search = True
Exit Function
End If
Next
End Function
Next, paste in this subroutine. I have assummed that the dataset begins at cell A1. Also, I have allowed for a dynamic range, should the number of columns or rows ever change.
Sub Insert_Average_Columns()
Dim HeaderRange As Range
Dim LastRow As Long
Dim c As Range
Set HeaderRange = Range(Range("A1"), Range("A1").End(xlToRight))
LastRow = Range("A" & Rows.Count).End(xlUp).Row
For Each c In HeaderRange.Cells
If Right(c.Value, 2) = "R3" Then
If Time_Search(c.Value, "3_5", "6_25", "9_5", "11_5", "16_5", "25", "49", "156") Then
c.Offset(0, 1).EntireColumn.Insert
c.Offset(0, 1) = "Average of " & Left(c.Value, Len(c.Value) - 3)
c.Offset(1, 1).FormulaR1C1 = "=IFERROR(AVERAGE(RC[-3]:RC[-1]),"""")"
c.Offset(1, 1).AutoFill Range(c.Offset(1, 1).Address, Cells(LastRow, c.Offset(1, 1).Column))
End If
End If
Next c
End Sub
There is one issue with your data. If you want the procedure to insert an average column for T = 25, then it will do so for all columns where T contains the string "25". If there are T= 8.25, 10.25, 15.25, etc, these will all have averages applied. The only way around it would be to include more of the heading string in the parameter array, but I presume you will be dealing with a variable Blood_Patient ID so that probably isn't an option.
Without using VBA (I can do it in VBA, but just want to try whether a macro can do it as well, but I haven't figured it out yet),
I have two sheets. Sheet A includes a column of names such as its cell like:
Wright
Sheet B includes a column of names well, but with more letters like title in one cell such as:
Mr. Wright
Sheet A to B is in a relationship of one-to-many (Wright in Sheet A might have multiple rows with Mr.Wright in Sheet B).
If in Sheet B, how to write a macro with some function to achieve: to check whether 'Mr.Wright' has a substring in a cell in Sheet A.
(I think about it might be easier to start from Sheet A: might with regex, find all matches in Sheet B with INDEX or MATCH first. It's much better if it can be done from Sheet B in one shot)
Create a macro called sub_in_name.
Option Explicit
Sub sub_in_name()
Dim x, i As Long
Dim endofcells1, endofcellsmany As Long
endofcells1 = WorksheetFunction.CountA(Range("A:A"))
endofcellsmany = WorksheetFunction.CountA(Range("B:B"))
For x = 1 To endofcells1
For i = 1 To endofcellsmany
If (InStr(1, Cells(i, 2), Cells(x, 1), vbTextCompare)) Then
Cells(i, 2 + x).Value = "True"
Else
Cells(i, 2 + x).Value = "False"
End If
Next i
Next x
End Sub
Intr(start, SearchStr, SearchInStr, vbaoption) is the main function to make this work. Cells(i, 2 + x) is indexed based off the number of non-empty cells in column "A"
Make sure to clear the cells content for each trial; after column "B".
For example put in Column "A" & Column "B" and you will get columns "C:D"
Column "A" Column "B" Column "C" Column "D"
Wright Mr. Wright True False
Roger Wright Jr. True False
Wright the Ivth. True False
Sally False False
Roughly similar logic. Originally I wanted to use existing macro functions to do it. Finally it ended up like making a customized function like below:
Inspired by another thread in stack overflow. I made a code to do full checking like user3553260's. But I think a function is not a bad choice as well, considering if the efficiency is not the top one concern here.
Function LookupName(lookupValue As Variant, lookupRange As Range) As String
Dim r As Long
Dim c As Long
Dim s As String
s = "No"
For r = 1 To lookupRange.Rows.Count
For c = 1 To lookupRange.Columns.Count
If Not IsEmpty(lookupRange.Cells(r, c).Value) Then
If InStr(LCase(lookupValue), LCase(lookupRange.Cells(r, c).Value)) Then
s = "Yes"
Exit For
End If
End If
Next
Next
LookupName = s
End Function
I'm trying to filter a table with an advanced filter, in which all items can appear EXCEPT for items with an Article Number that contains (or starts with, it doesn't matter) "PDE", "Q", or "M". The regular Excel filter only allows for two filtering criteria, which is why I need an advanced filter. (To clarify, there are many columns in the table, and Article Number is one of those columns.)
However, I have so far been unable to find a way to filter for items that do NOT contain something, although I've done a fair amount of researching, including the Microsoft tutorial here. Any ideas? Please let me know if you need more information.
My first intuition is that it is not directly possible. I would write a formula like =if(<complex condition>,"Y","N") in an auxiliary column and then filter on that column.
You can do this with AutoFilter You must build an Array of all the elements in the column excluding duplicates and excluding PDE Q and M
Here is an example for column D in which the header row is row #2
Sub FilterExcludeThree()
Dim N As Long, i As Long, c As Collection
Set c = New Collection
N = Cells(Rows.Count, "D").End(xlUp).Row
On Error Resume Next
For i = 3 To N
v = Cells(i, "D").Value
If v = "PDE" Or v = "M" Or v = "Q" Or v = "" Then
Else
c.Add v, CStr(v)
End If
Next
On Error GoTo 0
ReDim ary(0 To c.Count - 1)
For i = 1 To c.Count
ary(i - 1) = c.Item(i)
Next i
ActiveSheet.Range("D2:D" & N).AutoFilter Field:=1, Criteria1:=ary, _
Operator:=xlFilterValues
End Sub
I think the issue might be with having only a single column (though I see no reference to that in the documentation you quote). If your list is in ColumnA, say A7:A17, please try inserting a new ColumnA with say in A2 (A1 blank):
=AND(ISERROR(SEARCH("M",B7)),ISERROR(SEARCH("Q",B7)),ISERROR(SEARCH("PDE",B7)))
then Advanced Filter, List range: $A$6:$B$17, Criteria range: $A$1:$A$2.
I have a list of about 300 items, that I need spaced out every 8 cells as opposed to being one after the other. I'm sure there is an easy way to do this, however my brain is failing me. I have a feeling my terminology is hurting hence why I can't find an answer.
=IF(MOD(ROW()+7;8)=0;INDEX(A:A;INT(ROW()/8)+1);"")
Given that data begins at A1 and formula is used from row 1 (coulmn is not important).
Try below code
Sub Main()
Dim lastRow As Long
lastRow = Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To lastRow
If i = 1 Then
Cells(i, 5).Value = Cells(i, 1)
Else
Cells((i - 1) * 9, 5).Value = Cells(i, 1)
End If
Next
End Sub
Output
Can you please post a screen shot or add some additional detail?
Two things come to my mind for possible solutions:
1) Use Text to columns (if that is where you're going with this) or
2) Use a formula like =LEFT(A1, 10) in the 8th column and fill down (10 can be changed to whatever the first part of the string is that needs to be separated).
Provide some additional info and I'll take another look!
So, I think I have arrived at a solution for a database index problem, I have been having. It is throwing out claims that have duplicate "TIC" numbers. Of course these duplicates I have are 10 years apart. My solution is to go to the import file, and add a letter to the "TIC" field, e. g. "C" for Canada Claims, "B" for Brazil Claims etc. I need it to populate the correct letter in the "TIC" field, based on the data in the "Country" field. Here is the code I am trying to use now, but it is not working:
Sub AppendToTIC()
Dim d As Range
For Each d In Selection
If d.Value = "Canada" Then e.Value = "C" & e.Value
Next
End Sub
Something is off in my logic, I think, but I am not sure. Any help would be much appreciated.
Thanks
if you are looking to append just the letter to the beginning of the next columns cell then this would work.
Issues you have
You do not define the range of D
you use D to loop through a selection but you necer select anything.
Sub AppendToTIC()
Dim d As Range
Set d = Selection
For Each c In d
c.Offset(0, 1).Value = Left(c.Value, 1) & c.Offset(0, 1).Value
Next
End Sub