VBA Split Cells and paste only specific cells - vba

updated*
im new to VBA so help would be appreciated
i have a sheet where i have in column A content in this structure:
A1: Columnheader
A2: 044000 randomwordx (3 spaces between number and randomwords)
A3: 056789 randomwordy (3 spaces between number and randomwords)
A4:
A5: a.) randomwords
A6: 3.randomwords
A7:
A8: 600000 randomwordz (3 spaces between number and randomwords)
A9: 654124 randomwords (3 spaces between number and randomwords)
the delimiter between numbers and randomwords in column A is always 3x spaces
what i want to do is the following:
Go to Column A - select all cells which start with a 6-figures number
split these cells and paste them into column C and D
column C should contain only the starting number, remove any leading zeroes (if cell A2 has for example 044000, cell C2 should be 44000)
column D should only contain the text which comes after the starting number of column A (in this example D2 should be "randomwordx"
cells in column A which are blank or dont start with a 6 figure number should NOT be pasted in column C and D (in this example A4,A5,A6,A7 should NOT be pasted into C and D column)
So it should look like this
Column C:
C1: Columnheader
C2:44000
C3:56789
C4:60000
C5:653124
Column D:
D1: Columnheader
D2:randomwordx
D3:randomwordy
D4:randomwordz
D5:randomwords
I managed only to get this far, so help would be appreciated
Option Explicit
Sub Splitcolumn()
Dim mrg As Range
Dim LastRow As Long
Dim r As Range
Dim splitted() As String
With Sheets("test")
Set mrg = Sheets("test").Range("A4:A" & LastRow)
For Each r In mrg
splitted = Split(r.Value, " ")
r.Value = splitted(0)
r.Offset(2, 3).Value = splitted(1) & " " & splitted(2)
Next r
End With
End Sub
i received runtime error 1004
thanks for your help

This should do what you want it to. I used Portland Runner's answer to this post to set up the RegEx reference in my VBA and learn the syntax for it. Instead of a for each loop, I calculate the last row of column A and use a for loop with that many iterations. The i variable is set to 2 to skip the header in row 1.
Sub SplitCol()
'Set references to active workbook and sheet
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ActiveWorkbook
Set ws = wb.ActiveSheet
'Create Regular Expression object and set up options
Dim regEx As New RegExp
With regEx
.Global = True
.MultiLine = True
.IgnoreCase = False
'[0-9] means that regex will check for all digits
'{6} means that a minimum of 6 consecutive chars must meet the [0-9] criteria
.pattern = "[0-9]{6}"
End With
'All .Methods and .Properties will belong to ws object due to With
With ws
'Determine how many rows to loop through
Dim lastRowA As Long
lastRowA = .Range("A" & .Rows.Count).End(xlUp).Row
'Main loop
Dim i As Integer
For i = 2 To lastRowA
'Make sure there is a value in the cell or code will error out
If Cells(i, 1).Value <> "" Then
'Test regex of cell
If regEx.Test(Split(Cells(i, 1).Value, " ")(0)) Then
'If regex was true, set 3rd column (C) equal to numbers and
'4th column (D) equal everything else
Cells(i, 3).Value = Split(Cells(i, 1).Value, " ")(0)
Cells(i, 4).Value = Split(Cells(i, 1).Value, " ")(1)
End If
End If
Next
End With
'Release regEx object to reduce memory usage
Set regEx = Nothing
End Sub
This is what the code should make the sheet look like.

Related

Excel loop condition based concatenation [duplicate]

This question already has answers here:
PowerQuery: How can I concatenate grouped values?
(3 answers)
Closed 4 years ago.
I am very new to excel macros and i need your help to fix one of my condition based concatenation problem.
i will explain the problem with simple scenario in below:
In my sheet , Column A contains customer name and Column B contains country names. Attached excel screenprint for reference ( column C and Column D will be my expected results)
In the column A, single customer name can be repeated as he can have multiple country representations
In the column B, countries placed as shown in the screenprint.
My expected results will be look alike in the column C and D as shown in the image.
I can do the column C using INDEX and i am able to get the unique values from column A
For the column D ,i am expecting the results in such a way that all countries will be concatenated and separated by ' / ' based on the corresponding customer in column A. I tried some vlookups and indexes, but i am unable
to do it.
it would be really helpful if you could provide any suggestions(function/Macros) how it will be achieved.
I am a lower intermediate vba user, so I will admit that I am sure someone can do this better than , however, this works. Add a button and then click on it, or add this to the worksheet and it will occur whenever you choose for it to be fired:
Option Explicit
Sub listout()
'declare your variables
Dim wbk As Workbook
Dim ws1 As Worksheet
Dim cprange As Range
Dim rmrange As Range
Dim bottomRow As Long
Dim row As Range
Dim countname As Variant
Dim copyname As Variant
Dim nametoRow As Long
'speed up process
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
'set what the variables are
Set wbk = ThisWorkbook
Set ws1 = wbk.Worksheets("Names List")
bottomRow = ws1.Range("A1").End(xlDown).row
'get ird of any excisting values
ws1.Range("C1:D100").ClearContents
'Set the range of the names that you want to copy, and put them into column C
Set cprange = ws1.Range(Range("A1"), Range("A1" & bottomRow))
ws1.Range(Range("C1"), Range("C1" & bottomRow)) = cprange.Value
'then remove all the duplicates
Set rmrange = ws1.Range(Range("C1"), Range("C1" & bottomRow))
rmrange.RemoveDuplicates Columns:=1, Header:=xlNo
'redclare the range as it will be shorter because you got rid of load sof duplicates
Set rmrange = ws1.Range(Range("C1"), Range("C1").End(xlDown))
'loop though each name in the 'unique' list and loop through their names in the original data then add the country to their new location in column D
For Each copyname In rmrange
For Each row In cprange
nametoRow = ws1.Application.WorksheetFunction.Match(copyname, rmrange, False)
countname = row.Offset(0, 1)
If row.Value = copyname Then
If Trim(ws1.Range("D" & nametoRow) & vbNullString) = vbNullString Then
ws1.Range("D" & nametoRow) = countname
Else
ws1.Range("D" & nametoRow) = ws1.Range("D" & nametoRow) & "/ " & countname
End If
End If
Next row
Next copyname
'turn these back on otherwise it messes with your computer/excel
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Here is a more efficient method.
Advanced Filter to remove duplicates from Col A, paste on Col C
Set necessary ranges
Loop through each unique name
Build String
Paste String
Loop 4 - 6 until complete
Assumptions/Actions: You have headers on Col A, B, C, & D. If you have duplicate countries for a person, the country will show up twice on the string.You will need to change "Sheet1" to your sheet name on the 3rd line.
Usually you would need to check if your value is found using the .Find method, but the below logic does not allow for a cell to not be found as it is looping through values determined by filter. It wouldn't make since for a filtered object to not be found in the range it came from.
Option Explicit
Sub CountryList()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim FoundCell As Range, SearchRange As Range, Names As Range, SearchCell As Range
Dim MyString As String, i As Long
Set SearchRange = ws.Range("A2:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
SearchRange.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=ws.Range("C2"), Unique:=True
ws.Range("C2").Delete Shift:=xlShiftUp
Set Names = ws.Range("C2:C" & ws.Range("C" & ws.Rows.Count).End(xlUp).Row)
For Each SearchCell In Names
Set FoundCell = SearchRange.Find(SearchCell)
For i = 1 To Application.WorksheetFunction.CountIf(SearchRange, SearchCell)
MyString = MyString & FoundCell.Offset(, 1) & "/"
Set FoundCell = SearchRange.FindNext(FoundCell)
Next i
SearchCell.Offset(, 1) = Left(MyString, Len(MyString) - 1)
MyString = ""
Next SearchCell
End Sub

Extracting values to fit into changing equations

Not sure how to approach this situation. The best way to explain it might be visually, below is the diluted situation of what I have at hand:
Sheet 1:
Column A Column B
Chocolate 20
Vanilla 10
Strawberry 30
Sheet 2:
Column A
Chocolate + Vanilla
Vanilla / Strawberry
Chocolate / (Strawberry + Vanilla)
Goal:
Sheet 2:
Column A Column B
Chocolate + Vanilla 30
Vanilla / Strawberry 1/3
Chocolate / (Strawberry + Vanilla) 1/2
The trouble I'm having is, I could do an index/match or vlookup approach and pull the numbers corresponding to the flavors (chocolate, vanilla, strawberry) individually - but is there a way for Excel to know what arithmetic function based off Sheet 2, column A to follow instead of me manually adjusting each row to fit the right formula?
So essentially, some sort of function or VBA method that will tell Excel, "Grab & understand the arithmetic symbol/equation in that row and follow that command" so I don't have to adjust each row to the correct math symbol?
if your formulas keep sticking to Excel UI formula input rules then you could give this a try
Sub Main()
Dim cell As Range
Dim strng As String
Dim element As Variant
Dim elementValue As Variant
With Worksheets("Sheet2")
For Each cell In .Range("A1", .Cells(.Rows.Count, 1).End(xlUp))
strng = cell.Value2
For Each element In Split(Replace(Replace(cell.Value2, "(", ""), ")", ""))
If GetValue(CStr(element), elementValue) Then strng = Replace(strng, element, elementValue)
Next
cell.Offset(, 1).Formula = Replace("=" & strng, " ", "")
Next
End With
End Sub
Function GetValue(elementName As String, elementValue As Variant) As Boolean
Dim f As Range
With Worksheets("Sheet1")
Set f = .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).Find(what:=elementName, lookat:=xlWhole, LookIn:=xlValues)
If Not f Is Nothing Then
elementValue = f.Offset(, 1)
GetValue = True
End If
End With
End Function
Basically we could use RegEx to recognize the 'name' and then get the address of the value of the 'name'. Then we could derive a formula in Sheet2.
Sub Test()
Set mysheet2 = ThisWorkbook.Worksheets("Sheet2")
Set regEx = New RegExp
regEx.Pattern = "\w+"
regEx.Global = True
For i = 1 To mysheet2.UsedRange.Rows.Count
formula_str = mysheet2.Cells(i, 1).Value
Set oMatches = regEx.Execute(formula_str)
For Each oMatch In oMatches
formula_str = Replace(formula_str, oMatch, getAdd(oMatch))
Next
mysheet2.Cells(i, 2) = "=" & formula_str
Next
End Sub
'Function to get the address of 'names'
Function getAdd(keyword)
Set mysheet1 = ThisWorkbook.Worksheets("Sheet1")
getAdd = ""
With mysheet1
For i = 1 To .UsedRange.Rows.Count
If .Cells(i, 1) = keyword Then
getAdd = mysheet1.Name & "!" & .Cells(i, 2).Address
Exit For
End If
Next
End With
End Function
Currently this solution has the limit that all the 'names' must be alphabet format. If you want to use names containing digits and other characters, you should change the RegEx pattern.

Partial string match then return value

I'm working on a way to quickly code bank transactions. I have one tab of bank data downloaded (sheet 1) and I want to search the descriptions (column B) for a partial match with sheet 2, column A. Then if match found, return the value from sheet 2, column B to sheet 1 column D; and sheet 2, column C to sheet 1, column E.
Sheet 1
Column A Column B Column C Column D Column E
11/1/17 Transfer from Account 60617829-D 276 {acct} {location}
11/1/17 Transfer from Account 60692022-D 551.46 {acct} {location}
Sheet 2
Column A Column B (acct) Column C (location)
60617829-D 10430 03
60692022-D 10490 09
I was trying to use a solution similar to "Find and Get" described here: Excel Formula/VBA to search partial strings in other sheet
However, the following code returns the first value from sheet 2 to all values on sheet 1 without properly matching them. I think my error is in how I'm trying to use an array when it may not be necessary but I am at a loss.
Sub findAndGet()
Dim sh1, sh2 As Worksheet
Dim tempRow1, tempRow2 As Integer
Dim strList() As String
Dim name As String
Dim index As Integer
'Set sheets
Set sh1 = Sheets("list")
Set sh2 = Sheets("search")
'Set the start row of Sheet1
tempRow1 = 1
'Loop all row from starRow until blank of column A in Sheet1
Do While sh1.Range("A" & tempRow1) <> ""
'Get name
name = sh1.Range("B" & tempRow1)
'Split by space
strList = Split(Trim(name), " ")
'Set the start row of Sheet2
tempRow2 = 1
'Reset flag
isFound = False
'Loop all row from startRow until blank of column A in Sheet2
Do While sh2.Range("A" & tempRow2) <> ""
For index = LBound(strList) To UBound(strList)
'If part of name is found.
If InStr(UCase(sh2.Range("A" & tempRow2)), UCase(strList(index))) > 0 Then
'Set true to search flag
isFound = True
'exit do loop
Exit Do
End If
Next index
'Increase row
tempRow2 = tempRow2 + 1
Loop
'If record is found, set output
If isFound Then
'set account
sh1.Range("D" & tempRow1) = sh2.Range("B" & tempRow2)
'set location
sh1.Range("E" & tempRow1) = sh2.Range("C" & tempRow2)
End If
'Increase row
tempRow1 = tempRow1 + 1
Loop
End Sub
If formula solution is acceptable then assuming that data begins on both sheets on row number 2.
In cell D2 of Sheet1 insert following formula and copy down.
=LOOKUP(2^15,SEARCH(Sheet2!$A$2:$A$3,Sheet1!B2,1),Sheet2!$B$2:$B$3)
In cell E2 of Sheet1 insert following formula and copy down.
=LOOKUP(2^15,SEARCH(Sheet2!$A$2:$A$3,Sheet1!B2,1),Sheet2!$C$2:$C$3)

How can I remove non-numeric characters in cells and concatenate result to URL?

I have a bunch of cells with string like this:
WFM 1601
And this:
WFM 2231, WFM 2402
And this too:
Campaign 1680, 2402, 2784
I used code, below, to split the string in a single cell into multiple columns (max of 3).
Dim Rng As Range
Dim WorkRng As Range
On Error Resume Next
lRow = Range("U" & Rows.Count).End(xlUp).Row
Set MyRows = Range("U19:U" & lRow)
For Each cell In MyRows
splitVals = Split(cell.Value, ",")
totalVals = UBound(splitVals)
Range(Cells(cell.Row, ActiveCell.Column + 1), Cells(cell.Row, ActiveCell.Column + 1 + totalVals)).Value = splitVals
Next
Now, I'm trying to figure out a way to get rid of all NON numeric characters and leave only numbers. Then, concatenate these numbers, which are all IDs for processes in a SharePoint site that I work with, so I want to place the URL for each number, at the end of a static string, and next to the number that was just split into separate columns.
Here is a screen shot.
I have Column U, and I want to generate Column V to Column AA.
I can extract only numbers using the function below.
Function GetNums(target As Range)
Dim MyStr As String, i As Integer
MyStr = ""
If Len(target.Value) = 0 Then GoTo GoExit
If target.Value = "None" Then GoTo GoNone
For i = 1 To Len(target.Value)
If IsNumeric(Mid(target, i, 1)) Then MyStr = MyStr & Mid(target, i, 1)
Next i
GoTo GoExit
GoNone:
GetNums = "None"
Exit Function
GoExit:
GetNums = MyStr
End Function
However, this won't meet the requirement as it checks all characters in a cell, and just turns this: WFM 2231, WFM 2402 . . .
Into this: 22312402
I really need some way to distinguish the two IDs: 2231 2402
I would use Regular Expressions to extract the number groups. If it turns out there are other criteria for what constitutes a valid digit sequence, that would be easier to implement by changing the regex.
Here's an example with your original Data in Column A of the active sheet.
Option Explicit
Sub CreateURL()
Dim RE As Object, MC As Object, M As Object
Const sPat As String = "\b\d+\b" 'whole words that are all digits
Const sBaseURL As String = "htpps://collaborate.process...&ID="
Dim I As Long, J As Long
Dim rSrc As Range, C As Range
'This will be on active sheet
'Suggest you specify actual worksheet
Set rSrc = Range(Cells(1, 1), Cells(Rows.Count, "A").End(xlUp))
Set RE = CreateObject("vbscript.regexp")
With RE
.Pattern = sPat
.Global = True
End With
For Each C In rSrc
If RE.test(C.Text) = True Then
Set MC = RE.Execute(C.Text)
J = -1
For Each M In MC
J = J + 2
C.Offset(0, J) = M
C.Offset(0, J + 1) = sBaseURL & M
Next M
End If
Next C
End Sub
And here's the results of running this macro against data in column A:
Here is a formal explanation of the Regex, with links to more detail that hopefully still work:
\b\d+\b
\b\d+\b
Options: Case insensitive; ^$ match at line breaks
Assert position at a word boundary (position preceded or followed—but not both—by an ASCII letter, digit, or underscore) \b
Match a single character that is a “digit” (ASCII 0–9 only) \d+
Between one and unlimited times, as many times as possible, giving back as needed (greedy) +
Assert position at a word boundary (position preceded or followed—but not both—by an ASCII letter, digit, or underscore) \b
Created with RegexBuddy
I can help for the 1st part, to check if a value is numeric or not.
You did the split. Now, you can check if the variables you get are numeric or not. Example :
We want to check if the value in A1 is numeric :
isnum = isNumeric(range("A1"))
isnum is true if the value in A1 is numeric, else it is false.

Change a cell's format to boldface if the value is over 500

I am using Excel 2010 and trying to add a bunch of rows placing the sum of columns A and B in column C. If the sum is over 500 I would then like to boldface the number in column C. My code below works works mathematically but will not do the bold formatting. Can someone tell me what I am doing wrong? Thank you.
Public Sub addMyRows()
Dim row As Integer 'creates a variable called 'row'
row = 2 'sets row to 2 b/c first row is a title
Do
Cells(row, 3).Formula = "=A" & row & "+B" & row 'the 3 stands for column C.
If ActiveCell.Value > 500 Then Selection.Font.Bold = True
row = row + 1
'loops until it encounters an empty row
Loop Until Len(Cells(row, 1)) = 0
End Sub
Pure VBA approach:
Public Sub AddMyRows()
Dim LRow As Long
Dim Rng As Range, Cell As Range
LRow = Range("A" & Rows.Count).End(xlUp).Row
Set Rng = Range("C2:C" & LRow)
Rng.Formula = "=A2+B2"
For Each Cell In Rng
Cell.Font.Bold = (Cell.Value > 500)
Next Cell
End Sub
Screenshot:
An alternative is conditional formatting.
Hope this helps.
Note: The formula in the block has been edited to reflect #simoco's comment regarding a re-run of the code. This makes the code safer for the times when you need to re-run it. :)