I have a 2D array in the following format. (Unsure how to format this so it appears in a table format. The first and second columns are 1 character each and the 3rd columns is 2 characters)
a 1 aa
a 2 ab
b 1 ba
b 2 bb
c 1 ca
c 2 cb
d 1 da
d 2 db
e 1 ea
e 2 eb
f 1 fa
f 2 fb
I need to first search for "c" in the first column. If that is found, I need to search for "2" in the second and find the corresponding value in the 3rd. In this case, I finally need the value "cb".
Here is what I have so far but it isn't working correctly since I don't see the desired results
Public Sub Readinto_array()
Dim TheArray As Variant
Dim i As Long, j As Long, k As Long
Dim found As Boolean
TheArray = Range("G20:I31").Value
found = False
For i = LBound(TheArray) To UBound(TheArray)
For j = LBound(TheArray, 2) To UBound(TheArray, 2)
MsgBox TheArray(i, j)
If TheArray(i, j) <> "c" Then
Exit For
Else
If StrComp(TheArray(i, j + 1), "2", vbTextCompare) = 0 Then
MsgBox "found"
found = True
Exit For
End If
End If
Next j
If found Then
Exit For
End If
Next i
End Sub
Not sure why you have to loop for the columns since you know there's always 3...
So this seems easier.
Public Sub Readinto_array()
Dim TheArray As Variant
Dim i As Long
TheArray = Range("G20:I31").Value
For i = LBound(TheArray) To UBound(TheArray)
If TheArray(i, 1) = "c" And TheArray(i, 2) = "2" Then
MsgBox (TheArray(i, 3))
End If
Next i
End Sub
Or further simplified using innate excel objects.
Public Sub Readinto_array()
Dim MyRange As Range
Set MyRange = Range("G20:I31")
For Each cell In MyRange
If cell.Value = "c" And Cells(cell.Row, cell.Column + 1) = "2" Then
MsgBox (Cells(cell.Row, cell.Column + 2).Value)
End If
Next
End Sub
You could also do this with a worksheet formula. For example, if E1 contains your column1 value; and B1 your column2 value, try:
G2: =INDEX(ThirdColumn,SUMPRODUCT((FirstColumn=E1)*(SecondColumn=E2)*ROW(ThirdColumn)))
I see a tree like structure and think xml but to keep it simple use a Dictionary...
In the VBA Editor - using the Tools/References menu add a reference to Microsoft Scripting Runtime.
Write a function to create the dictionary:
Public Function LookErUp() As Dictionary
Dim i As Integer
Dim d As Dictionary
Set d = New Dictionary
Dim col1() As Variant
col1 = Array("a", "b", "c", "d", "e", "f")
Dim col2 As Dictionary
For i = 0 To UBound(col1)
Set col2 = New Dictionary
col2.Add 1, col1(i) & "a"
col2.Add 2, col1(i) & "b"
d.Add col1(i), col2
Next
Set LookErUp = d
End Function
You can test using the dictionary the Test procedure:
Public Sub Test()
Dim ld As Dictionary
Set ld = LookErUp
If ld.Exists("c") Then
If ld("c").Exists(2) Then
MsgBox "Found " & ld("c")(2)
End If
End If
End Sub
Try creating a third column where you concatenate the values from three previous columns i.e. in D1 you would have =A1&B1&C1. Next use that in you vlookup or match. If you do not specify exact match then in case having multiple entries for c 1 you would get the first or last one, depending on comparison type used.
Related
I was looking for some help on creating a sub which does data validation on the values in column C in a worksheet 'Compare' based on possible values listed in a different worksheet 'mapping' in columns C, D, E etc. I wanted to have the possible values use string/pattern characters like # ? * to make the data validation more flexible. There could be anywhere from 1 to 5+ different possible values which varies by key. Validation differences would be spit into an empty column D in worksheet Compare.
An Example with Data is likely most helpful here.
Static sheet 'mapping' . Key is Column A. Possible values in Columns C onwards
A B C D E F G
v1 CDID #### ###? 0
c52 FHAID ER# EP# INVA Z*
c48 PLID *
v24 CUSTID ### ###Q ###P
c22 MATID ???# ??# ?#
q23 LKKID *
Input original sheet 'Compare'. Key is Column B. Column C contains Data to validate
A B C D
c22 MATID RT3FG
v24 CUSTID 456P
v1 CDID 5
q23 LKKID PORTA
Output sheet 'Compare'. Invalid values noted in Column D.
A B C D
c22 MATID RT3FG Error: Invalid value
v24 CUSTID 456P
v1 CDID 5 Error: Invalid Value
q23 LKKID PORTA
Any ideas on how to make this work? Compare worksheet will have all data starting in A1 with no headers. mapping sheet will be quite large with 100+ rows and probably requires a vlookup or similar to find correct row.
Assuming * is anything # is a number and ? is a char I came up with this
Sub CompareToMapping()
Dim mapSheet As Worksheet: Set mapSheet = Sheets("Mapping")
Dim compSheet As Worksheet: Set compSheet = Sheets("Compare")
Dim mcell As Range
Dim ccell As Range
Dim rcell As Range
'Loop throw all the rows in the compare sheet
For Each ccell In compSheet.Range("a1", compSheet.Range("a" & compSheet.Rows.Count).End(xlUp))
'loop through and find a matching row from Mapping sheet
For Each mcell In mapSheet.Range("a1", mapSheet.Range("a" & mapSheet.Rows.Count).End(xlUp))
If mcell = ccell And mcell.Offset(0, 1) = ccell.Offset(0, 1) Then
'loop through valid format strings
For Each rcell In mapSheet.Range(mcell, mapSheet.Cells(mcell.Row, mapSheet.Columns.Count).End(xlToLeft))
ccell.Offset(0, 3) = "Error: Invalid value"
If FormatCorrect(ccell.Offset(0, 2).Text, rcell.Offset(0, 2).Text) Then
'show error in column d
ccell.Offset(0, 3) = ""
Exit For
End If
Next rcell
Exit For
End If
Next mcell
Next ccell
End Sub
Function FormatCorrect(inString As String, inFormat As String) As Boolean
Dim i As Integer: i = 0
Dim curS, curF As String
FormatCorrect = True
' first check for *
If inFormat = "*" Then
FormatCorrect = True
' next check if strings are the same length
ElseIf Len(inString) <> Len(inFormat) Then
FormatCorrect = False
Else
'compare 1 character at a time
For i = 1 To Len(inString)
curS = Mid(inString, i, 1)
curF = Mid(inFormat, i, 1)
If curF = "?" Then ' needs to be a letter
If IsNumeric(curS) Then
FormatCorrect = False
Exit For
End If
ElseIf curF = "#" Then ' needs to be a number
If Not IsNumeric(curS) Then
FormatCorrect = False
Exit For
End If
Else ' needs to be an exact match
If curF <> curS Then
FormatCorrect = False
Exit For
End If
End If
Next i
End If
End Function
Tested and worked for me. Good luck :)
I need to propagate cell values to a row, it's sort of difficult to explain...
I have a cell which is always going to be populated with binary values, for example "01110011"
the number changes according to other formulas.
what I need to do is take similar adjacent values and populate a raw with them...
a picture is worth a thousand words I suppose...
http://s28.postimg.org/mf42j9ftp/223.jpg
So basically I need to take the A1 cell and split it across a row...
and I have no idea what so ever how it's done.
I think you will find the LEFT, RIGHT, and MID functions useful. If you were to put all the values that you need to split, like 01110011 (the binary string you used as an example), in column A, you could split it in columns B, C, D and E with the following formulas:
Column B:
=LEFT($A1,1)
Column C:
=MID($A1,2,3)
Column D:
=MID($A1,5,2)
Column E:
=RIGHT($A1,2)
The LEFT function takes a cell as the first argument and the number of characters you want from that cell starting with the leftmost character. The RIGHT function does the same but from the rightmost character. The MID function takes the cell as the first argument, the index of the character you wish to begin from as the second argument, and the number of characters you wish to return as the third argument.
This should help you
Sub splitCell()
Dim cellContent As String
Dim partOfCell As String
Dim columnCounter As Integer
'just to be sure set row format as text to support 00
Rows(2).ClearContents
Rows(2).NumberFormat = "#"
cellContent = CStr(Cells(1, 1))
columnCounter = 1
If Len(cellContent) > 0 Then
partOfCell = Mid(cellContent, 1, 1)
End If
For i = 2 To Len(cellContent)
If Mid(cellContent, i, 1) = Mid(partOfCell, 1, 1) Then
partOfCell = partOfCell + Mid(cellContent, i, 1)
Else
Cells(2, columnCounter) = partOfCell
partOfCell = Mid(cellContent, i, 1)
columnCounter = columnCounter + 1
End If
Next i
Cells(2, columnCounter) = partOfCell
End Sub
Try this option as well,
Sub ListStringIntoB()
'Loop through string, list characters into Starting B1 and over
Dim str As String, Cnt As Integer, A1 As Range, Lp As Integer
Dim col As Long, Rng As Range, r As Range
Set A1 = Range("A1")
str = A1
Cnt = Len(A1)
For Lp = 1 To Cnt
col = Cells(1, Columns.Count).End(xlToLeft).Column + 1
Set Rng = Cells(1, col)
Rng = Mid(str, Lp, 1)
Next Lp
End Sub
Thank you guys for trying to help, I finally found the answer to my predicament... I had to use this code to do the trick..
Sub SplitBinaryNumbers()
Dim Bin As Variant
Bin = Application.Transpose(Split(Replace(Replace(Range("A8").Value, "01", "0,1"), "10", "1,0"), ","))
With Range("A20").Resize(UBound(Bin))
.NumberFormat = "#"
.Cells = Bin
End With
End Sub
I hope this helps someone.
My Excel Sheet format is similar to this
Name code1 Name Code2 Name Number Input1 Input2
AB XY GSABPEXY1 110 BA BC
BC BA GSBCPEBA1 120
CD CA GSCDPECA1 13
DC DA GSDCPEDA3 140
BC BA GSBCPEBA3 15
Question:
I want to obtain the values in the Name column by matching the inputs as in Input1 AND Input2.
I need these matched values to be pasted in another sheet's predefined specific row and columns cells
Using excel function countifs i am counting the number of rows having same repeating values in the Column A (name code1) and B (name code2) e.g. in above BC and BA which gives a count of 2.
I would like to use this value (mentioned in #3) for the loop iteration
I got this code which looks very close but it lacks two things ABC is predefined in INSTR function and it doesn't have the AND operator - i do not know how to do it. I am very new to VBA so please pardon me if I am missing something simple and/or providing less info.
Code:
Sub Hostname()
Dim K As Long, r As Range, v As Variant
K = 1
Dim w1 As Worksheet, w2 As Worksheet
Set w1 = Sheets("Sheet2")
Set w2 = Sheets("Sheet3")
w1.Activate
For Each r In Intersect(Range("A:A"), ActiveSheet.UsedRange)
v = r.Value
If InStr(v, "ABC") > 0 Then
r.Copy w2.Cells(K, 1)
K = K + 1
End If
Next r
End Sub
OK, I think I see what you want to do. How about this?
Sub Hostname()
Dim codeName1 As String
Dim codeName2 as string
Dim count As Integer
Dim r As Range
Dim targetRange as Range
Set targetSheet = ThisWorkbook.Sheets("MyTargetSheetName").Range("AddressOfFirstCellForDataInput")
codeName1 = "BC"
codeName2 = "BA"
For Each r In ActiveSheet.Range("A:A")
with r
If .Value2 = codeName1 And .Offset(0, 1).Value2 = codeName2 Then
targetRange.Offset(count, 0).value = .Offset(0, 2).Value2
count = count + 1
ElseIf Len(Trim(.Value2)) = 0 Then
Exit For
end if
End With
Next
Call msgbox("Found " & count & "cells matching Name code 1: " & codeName1 & " Name code 2: " & codeName2)
End Sub
This will count the number of cells which match Name Code 1 and Name Code 2. Cand you adapt this to do what you want?
Suppose I have a column of arbitrary length where each cell contains a string of text. Is there a way to determine what words appear most frequently in the column (not knowing in advance which words to check) and subsequently order these words along with their frequencies in a two column table? Would VBA be best for this task?
As an example, a cell might contain the string "This is a string, and the # of characters inthis string is>0." (errors intentional)
Select a portion of column A and run this small macro ( the table will be placed in cols. B & C :
Sub Ftable()
Dim BigString As String, I As Long, J As Long, K As Long
BigString = ""
' Add code to sum both "All" and "all"
' Add code to separate "." "!" etc. from the word preceeding them so that word
' is also counted in the total. For example: "all." should not be reported as 1 ' "all." but "all" be added to the total count of "all" words.
' Would you publish this new code?
For Each r In Selection
BigString = BigString & " " & r.Value
Next r
BigString = Trim(BigString)
ary = Split(BigString, " ")
Dim cl As Collection
Set cl = New Collection
For Each a In ary
On Error Resume Next
cl.Add a, CStr(a)
Next a
For I = 1 To cl.Count
v = cl(I)
Cells(I, "B").Value = v
J = 0
For Each a In ary
If a = v Then J = J + 1
Next a
Cells(I, "C") = J
Next I
End Sub
Given this:
I'll use a pivot table to get this:
Best part is, if I got more, it's easy to get Top 5, 10, etc. And it'll always result to unique indices. From there, there are all manners of editing and calculation you can do. :)
Using Google Sheets:
index((Transpose(ArrayFormula(QUERY(TRANSPOSE(SPLIT(JOIN(" ",$B$2)," ")&{"";""}),"select Col1, count(Col2) group by Col1 order by count(Col2) desc limit 20 label Col1 'Word', count(Col2) 'Frequency'",0)))),1,$A6+1)&":"&index((Transpose(ArrayFormula(QUERY(TRANSPOSE(SPLIT(JOIN(" ",$B$2)," ")&{"";""}),"select Col1, count(Col2) group by Col1 order by count(Col2) desc limit 20 label Col1 'Word', count(Col2) 'Frequency'",0)))),2,$A6+1)
In the above $B$2 contains the text string
$A6 = 1 will give you the most used word
$A6 = 2 will give you the second most used word
etc.
This is set to do 20 most frequent. If you want more, increase the limit value to whatever you want.
Here's a tiny fix plus an enhancement to the script kindly offered by "Gary's Student". The fix is that while building the collection is apparently not case-sensitive (and this is correct--we probably don't want new items added to the collection that differ only in case from existing items), the IF statement that does the counting IS case-sensitive as written, so it doesn't count correctly. Just change that line to...
If LCase(a) = LCase(v) Then J = J + 1
And here's my enhancement. To use it, you first select one or more columns but NOT their (first) header/label rows. Then run the script, and it gives results for each selected column in a new worksheet--along with that header/label row so you know what you're looking at.
I'm just a dabbler. I just hack stuff when I need to get a job done, so it's not elegant, I'm sure...
Sub FrequencyV2() 'Modified from: https://stackoverflow.com/questions/21858874/counting-the-frequencies-of-words-in-excel-strings
'It determines the frequency of words found in each selected column.
'Puts results in new worksheets.
'Before running, select one or more columns but not the header rows.
Dim rng As Range
Dim row As Range
Dim col As Range
Dim cell As Range
Dim ws As Worksheet
Dim wsNumber As Long 'Used to put a number in the names of the newly created worksheets
wsNumber = 1
Set rng = Selection
For Each col In rng.Columns
Dim BigString As String, I As Long, J As Long, K As Long
BigString = ""
For Each cell In col.Cells
BigString = BigString & " " & cell.Value
Next cell
BigString = Trim(BigString)
ary = Split(BigString, " ")
Dim cl As Collection
Set cl = New Collection
For Each a In ary
On Error Resume Next 'This works because an error occurs if item already exists in the collection.
'Note that it's not case sensitive. Differently capitalized items will be identified as already belonging to collection.
cl.Add a, CStr(a)
Next a
Set ws = Sheets.Add(After:=Sheets(Sheets.Count))
ws.Name = "F" & CStr(wsNumber)
wsNumber = wsNumber + 1
Worksheets(ws.Name).Cells(1, "A").Value = col.Cells(1, 1).Offset(-1, 0).Value 'Copies the table header text for current column to new worksheet.
For I = 1 To cl.Count
v = cl(I)
Worksheets(ws.Name).Cells(I + 1, "A").Value = v 'The +1 needed because header text takes up row 1.
J = 0
For Each a In ary
If LCase(a) = LCase(v) Then J = J + 1
Next a
Worksheets(ws.Name).Cells(I + 1, "B") = J 'The +1 needed because header text takes up row 1.
Next I
Next col
End Sub
I want to highlight excel sheet cell (Column 'A') of when it does not contain the text like "Verify", "Validate" or "Evaluate" in its content if, the value in corresponding cell of Column 'B' holds the value 'Y'.
When the column 'A' can't contains the words like 'Verify', 'Validate' and 'Evaluate' if the corresponding cell in Column 'B' holds value 'N'. So just need to highlight those discrepancies if its there.
Column 'A': Press Enter and verify this and this...
Column 'B': Y
This is quick example. However, the second formula can be shorted in case that column B cannot be blank. See if this fits ...
EDIT:
Here is VBA example as requested. You will need to save the code into current sheet (example: Sheet1 )...
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim str As String
Dim lCol As Long
Dim oCell As Object
Dim oCellFormat As Object
lCol = Target.Column
If lCol = 1 Or lCol = 2 Then
Set oCell = Cells(Target.Row, 1)
Else
GoTo mExit
End If
str = UCase(oCell.Value)
Set oCellFormat = Cells(oCell.Row, 1)
If (str = "VERIFY" Or str = "VALIDATE" Or str = "EVALUATE") Then
If UCase(Cells(oCell.Row, 2).Value) = "N" Then
oCellFormat.Interior.ColorIndex = 3 'red
ElseIf UCase(Cells(oCell.Row, 2).Value) = "Y" Then
oCellFormat.Interior.ColorIndex = 4 'green
Else
oCellFormat.Interior.ColorIndex = 2 'white
End If
Else
oCellFormat.Interior.ColorIndex = 2 'white
End If
GoTo mExit
Exit Sub
mExit:
Set oCell = Nothing 'free resources
Set oCellFormat = Nothing
End Sub