I have column H that contains long GET requests on sheet 1 such as:
H
GET /profiles/text/23493495_3492/g93id93kd
GET /edit/result/393493/te3903k4d
I would like to have a second sheet with the following type of list in columns A and B:
A B
23493495 identifier1
3903k4 realid2
g93id realid3
Ultimately, I would like a function that will search sheet 1 column H for any of the values in sheet 2 column A. Most of the time there is no separator so I need it to search for strings within the GET string. Once a value in sheet 2 column A is matched with a value in sheet 1 column H, I would like the function to take the corresponding text in sheet 2 column B and print it in sheet 1 column I. There may be multiple matches in a cell, so that would need to be taken into account. So if using the example above:
In H1, there would be a match of 23493495 and g93id within the string. I would like sheet 1 column I to display:
I
identifier1, realid3
I initially started with the below code where I had to specify the list but it doesn't use a second sheet or print the corresponding text of the match. So I would rather have something that meets my needs above, but below is an example of what I have tried so far:
=ListSearchB(J2, "23493495 g93id")
With this module I found that I modified a little:
Function ListSearchB(text As String, wordlist As String, Optional caseSensitive As Boolean = False)
Dim strMatches As String
Dim res As Variant
Dim arrWords() As String
arrWords = Split(wordlist)
On Error Resume Next
Err.Clear
For Each word In arrWords
If caseSensitive = False Then
res = InStr(LCase(text), LCase(word))
Else
res = InStr(text, word)
End If
If res > 0 Then
strMatches = strMatches & word
End If
Next word
If Len(strMatches) <> 0 Then
strMatches = Right(strMatches, Len(strMatches))
End If
ListSearchB = strMatches
End Function
That gives me:
23493495g93id in column I, and I wasn't sure how to separate the two with a comma.
In general though, I would prefer, to use some way to pull the list from sheet 2 and display the value in column I as specified initially.
Give this a try - just adjust the sheet names where commented before running
Sub your_sub()
Dim sGet As Worksheet
Dim sIDs As Worksheet
Dim rget As Range
Dim rIds As Range
'ADJUST SHEET NAME
With Worksheets("GET")
Set rget = Range(.Range("H1"), .Range("h" & .Rows.count).End(xlUp))
End With
'ADJUST SHEET NAME
With Worksheets("IDs")
Set rIds = Range(.Range("A1"), .Range("A" & .Rows.count).End(xlUp))
End With
mys = vbNullString
i = 1
For Each cget In rget
For Each cIds In rIds
If InStr(cget.Value, cIds) <> 0 Then
mys = mys & ", " & cIds.Offset(0, 1).Value
End If
Next cIds
If mys <> vbNullString Then
mys = Right(mys, Len(mys) - 2)
'ADJUST SHEET NAME
Worksheets("GET").Range("I" & i).Value = mys
End If
i = i + 1
mys = vbNullString
Next cget
End Sub
Related
I'm trying desperately to learn Excel well enough to do this on my own but I can't figure this out. I really appreciate any help you can give me. I posted before with not nearly enough information, so this is the repost with more info.
A document is pasted in cell A9.
It fills every cell below it with lines of data, up to A200.
The lines of data look like this:
192800002001 19280 G RG474 56 DAY PMI COMPLETE
19280A001001 19280 G CB359 AN/PRC-152A 56 DAY PMI
19280A005001 19280 G CB360 AN/PRC-152A 56 DAY PMI
I need the program to search each cell in column A for the words that look like "RG474" or "CB359" and search in a reference table on a different sheet in the same book. The table on the reference table looks like this
RG474 | xxx474 0 | 0 | IN RACK | AF6
CB915 | xxx359 0 | 0 | IN RACK | AF6
For every match found it pastes the row from the reference table into the row of the match next to the pasted document (columns L-Q).
I've found some code online that I've tried to no avail, the two things I tried are here:
Dim lastRw1, lastRw2, nxtRw, m
'Determine last row with data, refrene
lastRw1 = Sheets("380 Refrence").Range("A" & Rows.Count).End(xlUp).Row
'Determine last row with data, Import
lastRw2 = Sheets("analyser").Range("A" & Rows.Count).End(xlUp).Row
'Loop through Import, Column A
For nxtRw = 9 To lastRw2
'Search Sheet1 Column C for value from Import
With Sheets("380 Refrence").Range("A9:A" & lastRw1)
Set m = .Find(Sheets("analyser").Range("A" & nxtRw), LookIn:=xlValues, lookat:=xlWhole)
'Copy Import row if match is found
If Not m Is Nothing Then
Sheets("analyser").Range("A" & nxtRw & ":F" & nxtRw).Copy _
Destination:=Sheets("380 Refrence").Range("L" & m.Row)
End If
End With
Next
End Sub
Sub CopyImportData()
Dim lastRw1, lastRw2, nxtRw, m
Dim code As String, RefRow As Integer
Dim rowValues
'Determine last row with data, 380 Refrencerene
lastRw1 = Sheets("380 Refrence").Range("A" & Rows.Count).End(xlUp).Row
'Determine last row with data, Import
lastRw2 = Sheets("analyser").Range("A" & Rows.Count).End(xlUp).Row
For Row = 9 To lastRw2
With Sheets("analyser").Cell(Row, 1)
'meet the laziest error handling ever to find your 380 Refrenceerence value
code = WorksheetFunction.Mid(.Value, WorksheetFunction.IfError(WorksheetFunction.IfError(WorksheetFunction.Search("CB??? ", .Value), WorksheetFunction.Search("RG??? ", .Value)), 1), 5)
End With
With Sheets("380 Refrence")
'Use Excel Match to find the 380 Refrenceerence row, which is offset by 8
'I swear I'll stop using iferror
380 RefrenceRow = WorksheetFunction.IfError(WorksheetFunction.Match(code, .Range("A9:A" & lastRw1), 0) + 8, -1)
'-1 is our safeword, copy the range
If RefRow <> -1 Then
.Range("A" & RefRow & ":F" & RefRow).Copy Destination:=Worksheets("analyser").Range("L" & Row)
End If
End With
Next Row
End Sub
I didn't write either of these and don't fully understand them, but I do get the Gist of it.
Here's a very trimmed down duplicate of the workbook: https://drive.google.com/open?id=1qCz8DUCz6tA5-KbxKDnvRq_KiBDkl4W5
This worked for me - I skipped some of the "find last cell" bits so you'll need to adjust for that
Sub Tester()
Dim c As Range, v, f
Dim ws380 As Worksheet, wsAn As Worksheet
Set ws380 = ThisWorkbook.Sheets("380 Reference")
Set wsAn = ThisWorkbook.Sheets("analyser")
For Each c In wsAn.Range("A1:A50") 'for example
If Len(c.Value) > 0 Then
v = GetMatch(c.Value)
Debug.Print c.Address, v
If Len(v) > 0 Then
'got a value - look it up...
Set f = ws380.Range("A9:A5000").Find(v, lookat:=xlWhole, _
lookin:=xlValues)
If Not f Is Nothing Then
f.Resize(1, 6).Copy c.EntireRow.Cells(1, "L") 'copy found row
End If
End If
End If
Next c
End Sub
Function GetMatch(txt As String)
Dim re As Object, allMatches, m
Set re = CreateObject("VBScript.RegExp")
'looking for two upper-case letters then 3 digits, or 3 letters plus 2 digits
' with a word boundary on each end
re.Pattern = "(\b([A-Z]{2}\d{3}\b)|(\b[A-Z]{3}\d{2})\b)"
re.ignorecase = False
re.Global = True
Set allMatches = re.Execute(txt)
For Each m In allMatches
GetMatch = m.Value
Exit For
Next m
End Function
Here's a good vbscript regexp reference:
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/scripting-articles/ms974570(v=msdn.10)?redirectedfrom=MSDN
This code should find the correct cell (in the column corresponding to it's 'length' and the next empty row) in which to output a variable.
I'm getting the error message:
method range of object _worksheet failed
on lines 13 onward containing "outputcolumn"
In the MsgBox lines, the correct column and row number are being displayed, so I am not sure why it is not happy with my outputcolumn in particular.
Private Sub OutputRowAndColumn()
'Choose correct column: Find the length column and name this outputcolumn
Dim cell As Range, outputcolumn As Integer
Set cell = Range("FindLength").Find(Range("Length").Value, LookIn:=xlValues)
If Not cell Is Nothing Then
outputcolumn = cell.Column
End If
MsgBox "Output column is number " & outputcolumn & "."
'Choose correct row: If the cell to the left of "cell" is empty then this is the first row of output otherwise find next empty cell down
If Sheet1.Range(outputcolumn & "4").Offset(0, 1).Value = "" Then
outputrow = 4 ''' error msg '''
ElseIf Sheet1.Range(outputcolumn & "5").Offset(0, 1).Value = "" Then
outputrow = 5
Else
outputrow = Sheet1.Range(outputcolumn & "4").Offset(0, 1).End(xlDown).Row + 1
End If
MsgBox "Output row is number " & outputrow & "."
'Copy values 1, 2 and 3 from sheet 2 to sheet 1
Sheet1.Range(outputcolumn & outputrow).Offset(0, 1).Value = Sheet2.Range("Value1").Value ''' error msg '''
Sheet1.Range(outputcolumn & outputrow).Offset(0, 2).Value = Sheet2.Range("Value2").Value
Sheet1.Range(outputcolumn & outputrow).Offset(0, 3).Value = Sheet2.Range("Value3").Value
End Sub
outputcolumn is a numeric value (you defined it as Integer, but you always should define variables holding row or column numbers as long to avoid overflow errors).
So let's say outputcolumn gets the number 2 (column B). You write Sheet1.Range(outputcolumn & "4"). To access a range by it's address, You would have to write something like Range("B4"), but what you write is Range(2 & "4"), which means Range("24"), and that is an invalid address for a Range.
You could try to translate the column number 2 to a B, but there is an easier way to access a cell when you know the row and column number: Simply use the cells-property:
If Sheet1.Cells(4, outputcolumn).Offset(0, 1).Value = "" Then
' (or)
If Sheet1.Cells(4, outputcolumn+1).Value = "" Then
Just note that the order of the parameters is row, column.
"outputcolumn" is numeric in your case and when using .Range(), it needs to be a proper alphanumeric cell reference like "C5", not all numeric.
I haven't tried it directly but changing this ...
If Not cell Is Nothing Then
outputcolumn = cell.Column
End If
... to this ...
If Not cell Is Nothing Then
outputcolumn = Split(cell.Address, "$")(1)
End If
... will go a long way to helping you.
My workplace is changing CMS systems and we have around 5,000 products to import. The problem comes with image URL formatting as the two systems are laid out vastly different. I need a function or VB code to convert one cell:
Main|1|Vaterra/VTR03014C-1.jpg;VTR03014C|2|Vaterra/VTR03014C-2.jpg;VTR03014C|3|Vaterra/VTR03014C-3.jpg;VTR03014C|4|Vaterra/VTR03014C-4.jpg;VTR03014C|5|Vaterra/VTR03014C-5.jpg;VTR03014C|6|Vaterra/VTR03014C-6.jpg;VTR03014C|7|Vaterra/VTR03014C-7.jpg;VTR03014C|8|Vaterra/VTR03014C-8.jpg;VTR03014C|9|Vaterra/VTR03014C-9.jpg;VTR03014C|10|Vaterra/VTR03014C-10.jpg;VTR03014C|11|Vaterra/VTR03014C-11.jpg;VTR03014C|12|Vaterra/VTR03014C-12.jpg;VTR03014C|13|Vaterra/VTR03014C-13.jpg;VTR03014C|14|Vaterra/VTR03014C-14.jpg
into two cells containing:
Vaterra/VTR03014C-1.jpg
and this is where it gets tricky:
Vaterra/VTR03014C-2.jpg;Vaterra/VTR03014C-3.jpg;Vaterra/VTR03014C-4.jpg;Vaterra/VTR03014C-5.jpg;Vaterra/VTR03014C-6.jpg;Vaterra/VTR03014C-7.jpg;Vaterra/VTR03014C-8.jpg;Vaterra/VTR03014C-9.jpg;Vaterra/VTR03014C-10.jpg;|Vaterra/VTR03014C-11.jpg;Vaterra/VTR03014C-12.jpg;Vaterra/VTR03014C-13.jpg;Vaterra/VTR03014C-14.jpg
Notice how the "Main|1|" has been removed also, the tricky part is that not all of these begin with or contain "Main|1|" and not all of the options begin with or contain "Vaterra".
The main steps would be to remove each image's suffixes and then capture the line of text up to ".jpg" and move it to a separate cell.
As you have VBA tag, here is a quickest VBA approach.
Assuming your your data is in column A starting from row 1 on sheet1.
This macro will write the below two lines in column B and C respectively.
Column B
Vaterra/VTR03014C-1.jpg
Column C
Vaterra/VTR03014C-2.jpg;Vaterra/VTR03014C-3.jpg;Vaterra/VTR03014C-4.jpg;Vaterra/VTR03014C-5.jpg;Vaterra/VTR03014C-6.jpg;Vaterra/VTR03014C-7.jpg;Vaterra/VTR03014C-8.jpg;Vaterra/VTR03014C-9.jpg;Vaterra/VTR03014C-10.jpg;|Vaterra/VTR03014C-11.jpg;Vaterra/VTR03014C-12.jpg;Vaterra/VTR03014C-13.jpg;Vaterra/VTR03014C-14.jpg
Here is the macro.
Public RegMatchArray
Sub test()
Dim sh As Worksheet
Dim rowCount As Long
Dim i, j As Integer
Dim strValue, strValue1, strValue2 As String
Set sh = Sheets("Sheet1")
rowCount = sh.Range("A1048576").End(xlUp).Row
For i = 1 To rowCount
strValue = sh.Cells(i, 1).Value
If InStr(1, strValue, "Main|1|") > 0 Then
strValue = Replace(strValue, "Main|1|", "")
End If
iPos = InStr(1, strValue, ";")
strValue1 = Left(strValue, iPos - 1)
strValue2 = Mid(strValue, iPos + 1, Len(strValue) - iPos - 1)
Call splitUpRegexPattern(strValue2, "([\w\s-]+?)\/([\w\s-]+?\.jpg)")
For j = LBound(RegMatchArray) To UBound(RegMatchArray)
If j < 1 Then
strValue2 = RegMatchArray(j)
Else
strValue2 = strValue2 & ";" & RegMatchArray(j)
End If
Next
sh.Cells(i, 2).Value = strValue1
sh.Cells(i, 3).Value = strValue2
Next
Set sh = Nothing
End Sub
Public Function splitUpRegexPattern(targetString, strPattern)
Dim regEx As New RegExp
Dim strReplace As String
Dim arrArray()
i = 0
'CREATE THE REGULAR EXPRESSION
regEx.Pattern = strPattern
regEx.IgnoreCase = True
regEx.Global = True
'PERFORM THE SEARCH
Set Matches = regEx.Execute(targetString)
'REPORTING THE MATCHES COLLECTION
If Matches.Count = 0 Then
RegMatchArray = ""
Else
'ITERATE THROUGH THE MATCHES COLLECTION
For Each Match In Matches
'ADD TO ARRAY
ReDim Preserve arrArray(i)
arrArray(i) = Match.Value
i = i + 1
Next
RegMatchArray = arrArray
RegExpMultiSearch = 0
End If
If IsObject(regEx) Then
Set regEx = Nothing
End If
If IsObject(Matches) Then
Set Matches = Nothing
End If
End Function
Note: You have to add "Microsoft VBSript Regular Expressions 5.5" reference by going into Tools -> References.
If you don't want to keep the original column A, change the below lines. This will delete the original data and give you the result in column A and B.
From:
sh.Cells(i, 2).Value = strValue1
sh.Cells(i, 3).Value = strValue2
To:
sh.Cells(i, 1).Value = strValue1
sh.Cells(i, 2).Value = strValue2
With some tweeks, you will be able to make it happen without VBA.
First, replace | and / with ; so that you can have a consistent delimiter.
Also, you can remove Main|1| by replacing it with empty space.
Now, choose Data => Text to Columns
Choose the option Delimeted
you can now use the delimeter semicolon and you will have data in separate cells with the as in each cell.
You can now remove unwanted entries.
As an alternate, here is a formula solution. Assuming the large single block of text is in cell A1, put this formula in cell B1 and copy down until it starts giving you errors:
=TRIM(MID(SUBSTITUTE("|"&$A$1,";",REPT(" ",LEN($A$1))),LEN($A$1)*(ROW(A1)-1)+1+LOOKUP(2,1/(MID(SUBSTITUTE("|"&$A$1,";",REPT(" ",LEN($A$1))),LEN($A$1)*(ROW(A1)-1)+ROW(INDIRECT("1:"&LEN($A$1))),1)="|"),ROW(INDIRECT("1:"&LEN($A$1)))),LEN($A$1)))
The errors mean that there are no more entries to return, so you can delete the cells with errors, and then select all the cells with the formula -> Copy -> Right-click -> Paste Special -> Values to convert them to just be text instead of formulas. (I highly recommend doing that because the Indirect function is volatile and can greatly slow down your workbook if you have many formula cells with it.)
I have an output exported to Excel which lists paths and filenames.
The paths and filenames are on separate rows however. If the path is consistent the filename is simply listed on the next row. Then the next path is on the next line followed by filenames ect.
C:\
file1.doc
C:\Windows\
file2.doc
file3.doc
file4.doc
C:\Windows\Folder\
file5.doc
I need to concatenate all the paths with the filenames. All paths begin with c:\ (or other drive letters which can be defined). For the example above the following output is required:
C:\file1.doc
C:\Windows\file2.doc
C:\Windows\file3.doc
C:\Windows\file4.doc
C:\Windows\Folder\file2.doc
Happy to have white spaces as these can be filtered out in Excel.
Thanks,
Jono
VBA approach:
Sub test()
Dim ws As Worksheet
Dim iRow As Long
Dim i As Integer
Dim strFirstValue, strScndValue, strNewValue, strValue As String
Dim startFlag, endFlag As Boolean
Set ws = Sheets(1)
iRow = ws.Range("A1048576").End(xlUp).Row
strFirstValue = ws.Range("A2:A2")
strFirstValue = "": strScndValue = ""
startFlag = False
strFirstValue = ws.Range("A2:A2")
For i = 2 To iRow 'Assuming you have header, otherwise change 2 to 1
If endFlag Then
strFirstValue = ws.Range("A" & i & ":A" & i)
End If
strValue = strFirstValue
strScndValue = ws.Range("A" & i + 1 & ":A" & i + 1)
If InStr(strValue, ":") > 0 Then
startFlag = True
If Not strScndValue = "" Then
If Not InStr(strScndValue, ":") > 0 Then
strNewValue = strFirstValue & strScndValue
ws.Range("B" & i + 1 & ":B" & i + 1) = strNewValue
endFlag = False
Else
endFlag = True
End If
End If
End If
Next i
'To remove the row with drive info
For i = 2 To iRow
strValue = ws.Range("B" & i & ":B" & i)
If strValue = "" Then
ws.Range("B" & i & ":B" & i).EntireRow.Delete
End If
Next i
Set ws = Nothing
End Sub
Before:
After:
With data in column A, this macro will put the results in column B:
Sub dural()
Dim s As String, J As Long, r As Range
J = 1
For Each r In Intersect(ActiveSheet.UsedRange, Range("A:A"))
s = r.Text
If s = "" Then Exit Sub
If Right(s, 1) = "\" Then
pref = s
Else
Cells(J, 2).Value = pref & s
J = J + 1
End If
Next r
End Sub
Non-VBA solution, which will require some helper columns:
Assuming your data is in column A, and you don't care about sorting the results / having blanks within the results:
Put this in cell B2 and copy down [I recommend you have a header row for row 1]
=if(mid(A2,2,2)=":/",A2,B1)
This puts the new file path in column B, and if it's not a new file path (doesn't start with "x:/"), it takes the file path from column the previous cell.
In cell C2, copied down:
=if(mid(A2,2,2)=":/","",B2&A2)
This checks if the line you're on is a file path or a file name. If it's a file name, it adds the file name to the file path and displays as a single string. If it's a filepath, it returns a blank.
Alternatively you could save a tiny bit of processing time by using columns B-D instead of B-C. Some calculation is wasted here because we are doing the same check ("does cell A2 include ':/'?") twice, so excel needs to calculate it twice. Like so:
Put this in Cell B2:
=mid(a2,2,2)=":/"
Returns TRUE if the cell in column A is a filepath; returns FALSE if the cell in column A is a filename. Then put this in cell C2 and copy down:
=if(B2,A2,B1)
Works same as above, but uses the test already defined in cell B2. Put this in cell D2 and copy down:
=if(B2,"",B2&A2)
If you do want to sort your results column, there's just 2 extra steps (this is kind of unnecessary, but if you want to present / print your data in some format, you will need to either do this or manually copy + paste values):
Add an extra column to the right of the final column from my above response. Here, you want to check to see whether your current row is a new filepath + filename, or if it is blank (meaning column A was a filepath). I will assume you used my second option above, using columns B-D.
In column E, starting at E2 and copied down:
=if(B2,B1,B1+1)
If B2 is TRUE, the row is a filepath, and doesn't create a new filename + filepath. Therefore, we can keep the last counter. Otherwise, add a new counter.
In column F, starting at F2 and copied down:
=if(row()-1>max(E:E),"",index(D:D,match(row()-1,E:E,0)))
This looks at your results column, column D, which is unsorted and contains blanks. If the current row number in column F (minus 1 for the header row) is no bigger than the biggest counter in column D, it returns the matching item for that row number from column D.
Hope this helps you in similar situations in the future.
I want to update the contents of a cell in a workbook. My code looks a little like this:
ProductionWorkBook.Sheets("Production Schedule").Cells(StartRow, 1).Value = EstJobName(i)
The cells are referenced using Cells(StartRow, 1) Where StartRow was a pre-declared and pre-defined integer variable that specifies the row and "1" denotes the column.
EDIT:
Now, I want to change this code to reference the columns by the column HEADERS instead.
For example, the header of a column is: "Fab Hours Date", how do I reference that?
Yes, you can simply use the letter name for the column in quotes:
Cells(StartRow, "A")
Edited to answer your further question:
to look for a specific column name, try this:
columnNamesRow = 1 ' or whichever row your names are in
nameToSearch = "Fab Hours" ' or whatever name you want to search for
columnToUse = 0
lastUsedColumn = Worksheets("Foo").Cells(1, Worksheets("Foo").Columns.Count).End(xlToLeft).Column
For col = 1 To lastUsedColumn
If Worksheets("Foo").Cells(columnNamesRow, col).Value = nameToSearch Then
columnToUse = col
End If
Next col
If columnToUse > 0 Then
' found the column you wanted, do your thing here using "columnToUse" as the column index
End If
Here are two different functions to get what you want. To use them, you'd have to put them in your code.
Function ColumnNumberByHeader(text As String, Optional headerRange As Range) As Long
Dim foundRange As Range
If (headerRange Is Nothing) Then
Set headerRange = Range("1:1")
End If
Set foundRange = headerRange.Find(text)
If (foundRange Is Nothing) Then
MsgBox "Could not find column that matches header: " & text, vbCritical, "Header Not Found"
ColumnNumberByHeader = 0
Else
ColumnNumberByHeader = foundRange.Column
End If
End Function
Function ColumnNumberByHeader2(text As String, Optional headerRange As Range) As Long
If (headerRange Is Nothing) Then
Set headerRange = Range("1:1")
End If
On Error Resume Next
ColumnNumberByHeader2 = WorksheetFunction.Match(text, headerRange, False)
If Err.Number <> 0 Then
MsgBox "Could not find column that matches header: " & text, vbCritical, "Header Not Found"
ColumnNumberByHeader2 = 0
End If
On Error GoTo 0
End Function
Example Calls:
ColumnNumberByHeader ("Extn")
ColumnNumberByHeader("1718", Range("2:2"))
Or in your case:
ProductionWorkBook.Sheets("Production Schedule"). _
Cells(StartRow, ColumnNumberByHeader("Fab Hours Date")).Value = EstJobName(i)
ProductionWorkBook.Sheets("Production Schedule").Range("A" & StartRow).Value = EstJobName(i)
Unless you mean the column is a named range you defined?
ProductionWorkBook.Sheets("Production Schedule").Range("E"& StartRow).Value = ...
will do the job.
Though keep in mind that using hard coded references like the column letter will risk that the macro breaks when the sheet is edited (e.g. a column is inserted). It's therefore better to use a named range and Offset to access:
ProductionWorkBook.Sheets("Production Schedule").Range("StartCell").Offset(StartRow-1).Value
Now you only need to provide the name StartCellto your fist cell (make sure that it's a local name in the Name Manager)