Excel VBA find in Table - vba

Im having a list of event participants in an Excel Sheet (Col A: Lastname; Col B: Firstname) and a Membership Table with the same plus info colums like birthdate and sex.
No I want to loop through the event list and do some actions on the birthday/sex of the participants. I can express that in MySQL
SELECT birthdate, sex FROM members WHERE lastname = LASTNAME AND firstname = FIRSTNAME
Where LASTNAME & FIRSTNAME are pulled from the participants table. I can figure out how to create a Loop through the event table but I got trouble on how to pull the data from the Membership Table.
Im just not used to Excel VBA so any help to start me off would be greatly appreciated
So far I got following Loop:
Dim participantCount As Integer
Dim sh As Worksheet
Dim rw As Range
Set sh = Sheets(INP_tblakt.Value)
For Each rw In sh.Rows
If sh.Cells(rw.Row, 1).Value = "" And sh.Cells(rw.Row, 2).Value = "" Then
Exit For
End If
participantCount = participantCount + 1
Next rw
EDIT: To Clearify
I got the loop above in wich I want to insert a "function" wich looks up in another sheet the row where A? = sh.Cells(rw.Row,1) and B? = sh.Cells(rw.Row,2) So that I then can get the value from D? and E? to use it for further calculation.
The VBA function Find does only support the matching of one Colum. I now found MATCH and IDEX but couldnt succesfully implement them.
(Hopefully this does help to understand the question, Thanks in advance for help)

Assuming the members table is an Excel Table, you can combine MATCH and OFFSET to get what you need. There may be a faster way but this is what I got.
Consider below table (on same sheet for screenshot):
C3 is the cell with formula that finds the DOB for matching FirstName A3 and LastName B3 from the members table.
The formula is:
=IF(MATCH(A3,members[FirstName],0)=MATCH(B3,members[LastName],0),
OFFSET(members[[#Headers],[DOB]],MATCH(A3,members[FirstName],0),0),
"No Match")
Since we need to exact match 2 fields (FirstName and LastName), we need the Matches to be on the same position. Hence the condition is:MATCH(A3,members[FirstName],0)=MATCH(B3,members[LastName],0)
Once an exact match is found, we can use OFFSET to locate the n'th row from the field we want to extract:OFFSET(members[[#Headers],[DOB]],MATCH(A3,members[FirstName],0),0) It means get the value from matched row of table members with header "DOB". Change the text DOB to the desired header name of your lookup table.
Otherwise "No Match" is returned.

Related

Manipulating Excel spreadsheet, removing rows based on values in a column and then removing more rows based on values in another column

I have a rather complicated problem.
I have a log file that when put into excel the column "I" contains event IDs, and the column J contains a custom key that keeps a particular even grouped.
All i want to do is remove any rows that do not contain the value of say 102 in the event id column.
And THEN i need to check the custom key (column J) and remove rows that are duplicates since any duplicates will falsely show other statistics i want.
I have gotten as far as being able to retrieve the values from the columns using com objects and .entirecolumn cell value etc, but I am completely stumped as to how i can piece together a solid way to remove rows. I could not figure out how to get the row for each value.
To give a bit more clarity this is my thought process on what i need to do:
If cell value in Column I does not = 102 Then delete the row that cell contains.
Repeat for all rows in spreadsheet.
And THEN-
Read every cell in column J and remove all rows containing duplicates based on the values in column J.
Save spreadsheet.
Can any kind persons help me?
Additional Info:
Column I holds a string that is an event id number e.g = 1029
Column J holds a string that is a mix of numbers and letters = 1ASER0X3NEX0S
Ellz, I do agree with Macro Man in that your tags are misleading and, more importantly, I did indeed need to know the details of Column J.
However, I got so sick of rude posts today and yours was polite and respectful so I've pasted some code below that will do the trick ... provided Column J can be a string (the details of which you haven't given us ... see what Macro Man's getting at?).
There are many ways to test for duplicates. One is to try and add a unique key to a collection and see if it throws an error. Many wouldn't like that philosophy but it seemed to be okay for you because it also gives you a collection of all the unique (ie remaining) keys in Column J.
Sub Delete102sAndDuplicates()
Dim ws As Worksheet
Dim uniques As Collection
Dim rng As Range
Dim rowPair As Range
Dim iCell As Range
Dim jCell As Range
Dim delRows As Range
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set rng = Intersect(ws.UsedRange, ws.Range("I:J"))
Set uniques = New Collection
For Each rowPair In rng.Rows
Set iCell = rowPair.Cells(, 1)
Set jCell = rowPair.Cells(, 2)
On Error Resume Next
uniques.Add jCell.Value2, jCell.Text
If Err = 457 Or iCell.Value2 = 102 Then
On Error GoTo 0
If delRows Is Nothing Then
Set delRows = rowPair.EntireRow
Else
Set delRows = Union(delRows, rowPair.EntireRow)
End If
End If
Next
If Not delRows is Nothing then
MsgBox delRows.Address(False, False) & " deleted."
delRows.Delete
End If
End Sub
There are a number of ways in which this can be done, and which is best will depend on how frequently you perform this task and whether you want to have it fully automated. Since you've tagged your question with VBA I assume you'll be happy with a VBA-based answer:
Sub removeValues()
Range("I1").Select 'Start at the top of the I column
'We are going to go down the column until we hit an empty row
Do Until IsEmpty(ActiveCell.Value) = True
If ActiveCell.Value <> 102 Then
ActiveCell.EntireRow.Delete 'Then delete the row
Else
ActiveCell.Offset(1).Select 'Select the cell below
End If
Loop
'Now we have removed all non-102 values from the column, let`s remove the duplicates from the J column
Range("A:J").RemoveDuplicates Columns:=10, Header:=xlNo
End Sub
The key line there is Range("A:J").RemoveDuplicates. It will remove rows from the range you specify according to duplicates it finds in the column you specify. In that case, it will remove items from the A-J columns based on duplicates in column 10 (which is J). If your data extends beyond the J column, then you'll need to replace "A:J" with the appropriate range. Note that the Columns value is relative to the index of the first column, so while the J column is 10 when that range starts at A (1), it would be 2 for example if the range were only I:J. Does that make sense?
(Note: Using ActiveCell is not really best practice, but it's the method that most obviously translates to what you were trying to do and as it seems you're new to VBA I thought it would be the easiest to understand).

Getting corresponding data from another sheet

I'm rather new with excel and no good at VB, and couldn't find a way to solve the following:
I have one sheet which contains data about articles and another sheet which contains only the title of the article and the amount of times it is cited.
For example the first sheet contains data in these columns:
First Author Second Author Other Authors Publication Year Title Published In More Info
and the second in these:
Title Count
I want to copy all the data from the first sheet to the corresponding row on the second sheet (based on the article title). Some titles may appear twice on the first sheet, but should only be copied once to the second. Also, it would be good if all lines that were copied from the first sheet to the second would be highlighted so that I could see if there were any mistakes.
A VBA solution is also welcome.
No need for VBA. This is a simple INDEX-MATCH combination.
Set-up:
Sheet1:
Sheet2:
Enter the following formula to Sheet2!C2, drag down and right:
=INDEX(Sheet1!$A:$G,MATCH(Sheet2!$A2,Sheet1!$E:$E,0),MATCH(Sheet2!C$1,Sheet1!$A$1:$G$1,0))
Result:
Let us know if this helps.
The code below will give you an Idea how to match the cells. Assuming you have the title in column 10 in sheet1 and column 1 in sheet2
dim i as integer
dim j as integer
for i = 1 to 'number of rows in sheet1
for j = 1 to 'number of rows in sheet2
if sheet1.cells(i, 1) = sheet2.cells(j, 1) then
'do what ever you want with the matching records
end if
next j
next i

Can you give columns in Excel a variable name?

I have a fairly large Excel spreadsheet, there are around 4500 rows with 35 columns.
Our VBA code references columns by their letter (aka A1, A2, A3 ... A4500) when modifying field values ... this is a limitation as we are unable to move columns around within our sheet without having to update all of our code to where that column has moved to.
Is it possible to give a column a variable name?
If column A currently holds First Name, is it possible to name this column First_Name so that regardless of where this column moves, it retains the First_Name reference?
Then in our VBA code we can say First Name instead of A1?
Thanks!
The easiest way is to dimension (35) variables or Enums that correspond to the either columns or column numbers.
Option Explicit
Public Const Name As String = "A"
Public Const Age As String = "C"
Public Const ID As String = "F"
...
then when referring to the columns use the defined names ie. Range(Name & 13).
this way you only ever need to modify the public constants when the order of column changes.
if you reference columns by numbers consider using Enum
Public Enum Col
Name = 1
ID = 2
Age = 3
End Enum
Sub Main()
Cells(1, Col.ID) = "ID HEADER"
End Sub
Here's a version that dynamically finds the column:
Sub test()
Do While True ' Ctrl-Break to stop
MsgBox ColByName(InputBox("Name:"))
Loop
End Sub
Function ColByName(colName As String) As Long
Dim iCol As Long
For iCol = 1 To UsedRange.Columns.Count ' need better upper range for code off of a sheet
If Cells(1, iCol) = colName Then
ColByName = iCol
Exit Function
End If
Next iCol
ColByName = -1
End Function
I prefer the ENum route but sometimes it is just not practical and it can introduce maintenance issues.
An alternate approach is a collection indexed by name - rebuild the collection when the columns are moved etc. Note that the test shows the default compare is case sensitive.
A technique I use frequently is to leverage an Excel table. Using this command will create a special object within your worksheet that you can then reference within your VBA code. (You can, of course, add a new Excel table programatically using VBA if you like.)
For example, if you have a data in your worksheet like:
+--------------+-------------+
| First_Name | Last_Name |
+--------------+-------------+
| John | Smith |
| Sally | Jones |
+--------------+-------------+
Once you convert it into an Excel table, you will be able to reference it using code like:
Option Explicit
Public Sub SampleCode()
'
' selects Last_Name column
'
Range("Table1[Last_Name]").Select
'
' prints "Jones"
'
Debug.Print Cells(3, Range("Table1[Last_Name]").Column).Value
End Sub
Excel will create a sequential table name for your starting with Table1. You can change this name within Excel or give it a unique name if you create it programmatically.

Excel VBA Code to transpose multiple columns into multiple rows

Can somebody please help, i'm a newbie in VBA, i would like to thank you in advance for your help. If there are any links where my question has already been adressed please point me in the right direction. I have searched here and on other sites but i couldn't find anything that atleast get me started. I would like to create VBA code that loops through cells in every row and transpose them into seperate rows as illustrated below.
I have an excel spreadsheet with 12000 rows of data and multiple columns. For each row i have columns "A" to "Q" as static fields. Name, DOB, ID_Number, e.t.c. From columns "Q" to "DD", i have repeated data on "City" and "RegDate" columns, for example: City1, RegDate1, City2, RegDate2, City3, RegDate3,..& CityN, RegDateN. The City names are used as Headers and the data cells show "R" under every city where a person is registered and the registration date in the column next to it, otherwise there is no entry.
I would like to transform this data so that for every City where a person is registered, i will have a new row of data showing the static fields "A" to "P", "City", and "RegDate" i.e. after column "P" i will only have two columns "City" and "RegDate".
I'm really struggling with creating code that loops from Column Q to Column DD creating a new row when ever an "R" is encountered copying rows Columns "A" to "P" and inserting the Name of the City in the new column "Q" and the RegDate in the new Column "R", before moving on to the next row until all the 12000 rows have been transformed.
Any help to get me started would be greatly appreciated especially on setting up the looping that creates a new role for every record with an "R" encountered.
Have you considered just putting this information in two new columns to the far right (DE and DF)? Then you could either hide Q to DD, or delete those columns.
As for the looping code to do this:
Dim cr As Long 'current row
Dim cc As Long 'current column
For cr = 2 To 12000
For cc = 17 To 108 Step 2
If Cells(cr, cc).Value = "R" Then
'make column 109 (DE) in current row = city name
Cells(cr, 109).Value = Cells(1, cc).Value
'make column 110 (DF) in current row = date of registration
Cells(cr, 110).Value = Cells(cr, cc + 1).Value
End If
Next
Next

compare huge text files using vba

I gotta serious problem here.. any kind of help is much appreciated!!
I have two huge text files (130 MB)each with thousands of records in each. I need to compare the two files using vba or by any means and generate a spreadsheet which includes the header and with two additional columns. The two additional columns will be the file name and in the next column it should display in which particular column is error. Each record will be having multiple discrepancies. One file can have the records which cannot be found in the other file. So this condition should also be recorded in the spreadsheet.
Example:
Media Events: Taking one record from each.
00000018063|112295|000|**0009**|
PROL:
00000018063|112295|000|**0013**|
In the above example, the records are from two files. The highlighted ones are the differences between the records. So the output should be like this..
HH_NUMBER | CLASS_DATE | MV_MIN DURATION File Mismatc Mismatch Reason
00000018063 | 112295 | 000 **0009** Media Events Mismatches in DURATION
00000018063 | 112295 | 000 **0013** PROL Mismatches in DURATION
00000011861 | 112295 | 002 0126 Media Events missing in PROL file
It seems there are three problems here:
1) Find matching records (first column) between two files.
2) Compare records that match on the first column - if there is a difference, record what the difference is
3) If a record exists in one file but not the other, record that.
I am going to assume that the two "huge files" are in fact separate sheets in the same excel workbook, and that the records are sorted on the first key. This will speed up processing significantly. But speed is a secondary concern, I assume. I also assume there is a third sheet where you put the output.
Here is an outline of VBA code - you will have to do a bit of work to get it "just right" for your application, but I hope this gets you going.
Sub compare()
Dim s1 as Worksheet
Dim s2 as Worksheet
Dim col1 as Range
Dim col2 as Range
Dim c as Range
Dim record1 As Range, record2 As Range, output As Range
Dim m
Dim numCols as Integer
numCols = 5 ' however many columns you want to compare over
Set s1 = Sheets("Media")
Set s2 = Sheets("Pro")
Set output = Sheets("output").Range("A2")
Application.ScreenUpdating = False
s1.Select
Set col1 = Range("A2", [A2].End(xlDown));
s2.Select
Set col2 = Range("A2", [A2].End(xlDown));
On Error Resume Next
For Each c in col1.Cells
m = Application.Match(c.Value, col2, 0);
If isError(m) Then
' you found a record in 1 but not 2
' record this in your output sheet
output.Value = "Record " & c.Value & " does not exist in Pro"
Set output = output.Offset(1,0) ' next time you write output it will be in the next line
' you will have to do the same thing in the other direction - test all values
' in 2 against 1 to see if any records exist in 2 that don't exist in 1
Else
' you found matching records
Set record1 = Range(c, c.offset(0, numCols))
Set record2 = Range(col2.Cells(m,1), col2.Cells(m,numCols))
' now you call another function to compare these records and record the result
' using the same trick as above to "go to the next line" - using output.Offset(1,0)
End If
Next c
End Sub
You could do this with formulas:
See
MS KB: Use Excel to compare two lists of data
Me Excel.com - Creating a list of non-matching values
ExcelExperts.com - Extracting non-matching entries from two columns in a third column
To give you an idea, basically, if you have two lists in columns A & B, you could use formulas like below in columns C and D to show the matching or non-matching:
In C1,
=If(isna(match(A1,B:B,0)),A1,"")
and, in D1
=IF(Isna(Match(B1,A:A,0)),B1,"")
both copied down.
FURTHER READING:
Excel Index Function and Match Function - Contextures MVP
Excel VLOOKUP and Index & Match - Excel User MVP
Excel User MVP - Excel’s Best Lookup Method: INDEX-MATCH