I'm looking for help to get my Excel sheet into a specific format to import into Adwords. I have a sheet with data in this format:
I need to get it into this format:
What makes this tricky is getting 3 lines for each SKU. One line contains the Ad Group creation, the next is text ad creation and then the Keyword and bid is on the next line.
Can someone please help me achieve this? I would greatly appreciate it.
Thanks!
hope this helps...
(For all)
In a worksheet, set the range names that you can see the in code
ReportAddress
theSiteShort
theSiteLong
First
Second
And use it to reference inside the book. (This is just to not use $A$1)
Follow the comments...
'Take the report and store the address in the
'Range("ReportAddress") to use it later...
Sub takeReport()
'this is to take the reporte and store the address in Cells C6
'that has the name ReportAddress as you can see
Dim i
Dim FD As FileDialog 'to take files
Set FD = Application.FileDialog(msoFileDialogFilePicker)
With FD 'invoke the file dialog
If .Show = -1 Then 'if you take any file (1 or more)
For Each i In .SelectedItems 'for each file do this...
Range("ReportAddress").Value = i 'and as you can see, just take one file...
Next i
End If
End With
End Sub
'to create the report
Sub do_Report()
Dim dirReport As String
Dim wrkData As Workbook
Dim shtData As Worksheet
Dim tmpReport As Workbook
Dim shtReport As Worksheet
Dim skuNum() 'to store the data
Dim skuName()
Dim vendorPartNum()
Dim manufacturer()
Dim r 'var for rows
Dim c
Dim col
Dim fil
Dim i
Dim counter
Dim shortSite As String
Dim longSite As String
Dim first As String
Dim second As String
'this is to a better handle of the data...
shortSite = Range("theSiteShort").Value
longSite = Range("theSiteLong").Value
first = Range("First").Value
second = Range("Second").Value
Workbooks.Open Range("ReportAddress").Value 'open the workbook with the data
Set wrkData = ActiveWorkbook
Set shtData = ActiveSheet 'here we can fail, because if the xls has more than 1 sheet and that sheet
'is not the sheet we need, could fail...
Workbooks.Add 'create a new workbook (CTRL+N)
Set tmpReport = ActiveWorkbook 'Store it here
Set shtReport = ActiveSheet 'as well the active sheet
'headlines
Range("A1").FormulaR1C1 = "Ad Group"
Range("B1").FormulaR1C1 = "Bid"
Range("C1").FormulaR1C1 = "Headline"
Range("D1").FormulaR1C1 = "Desc 1"
Range("E1").FormulaR1C1 = "Desc 2"
Range("F1").FormulaR1C1 = "Display URL"
Range("G1").FormulaR1C1 = "Final URL"
Range("H1").FormulaR1C1 = "Keyword"
wrkData.Activate 'got to the data report
shtData.Activate 'activate the sheet with the data, remember the comment!
r = Range("A1").End(xlDown).Row 'find the last row of data
c = Range("A1").End(xlToRight).Column 'As well the last column
For col = 1 To c 'well may is always the same qty of columns, but i like to count!!!
For fil = 2 To r 'for every row
Select Case col 'depends in which column is...
Case 1 'the first one use SkuNum... and so on... (This are the columns of the data source)
ReDim Preserve skuNum(1 To fil - 1)
skuNum(fil - 1) = Cells(fil, col)
Case 2
ReDim Preserve skuName(1 To fil - 1)
skuName(fil - 1) = Cells(fil, col)
Case 3
ReDim Preserve vendorPartNum(1 To fil - 1)
vendorPartNum(fil - 1) = Cells(fil, col)
Case 4
ReDim Preserve manufacturer(1 To fil - 1)
manufacturer(fil - 1) = Cells(fil, col)
Case Else
'do nothing 'just in case...
End Select
Next fil
Next col
tmpReport.Activate 'go to the new book, that is our final report
shtReport.Activate 'go to the sheet... just in case again... 'This line could be deletec
counter = 0 'a counter (index) for the vars()
For i = 1 To (r * 3) Step 3 '
'i got r lines and i need to put every 3 lines,
'then, that why I use Step 3 = (every 3th line), and that 3 * r.
counter = counter + 1
If counter > UBound(skuName) Then 'if the counter is bigger that the
'qty of vars inside SkuName (or any other)
Exit For 'get out the for loop!
End If
'here is the magic... almost...
Cells(i + 1, 1).Value = manufacturer(counter) & " - " & vendorPartNum(counter)
Cells(i + 1, 2).Value = first
Cells(i + 2, 1).Value = manufacturer(counter) & " - " & vendorPartNum(counter)
Cells(i + 2, 3).Value = manufacturer(counter) & " - " & vendorPartNum(counter) & " On Sale"
Cells(i + 2, 4).Value = skuName(counter)
Cells(i + 2, 5).Value = "Shop " & manufacturer(counter) & " now."
Cells(i + 2, 6).Value = shortSite & manufacturer(counter)
Cells(i + 2, 7).Value = longSite & skuNum(counter)
Cells(i + 3, 1).Value = manufacturer(counter) & " - " & vendorPartNum(counter)
Cells(i + 3, 2).Value = second
Cells(i + 3, 8).Value = "+" & manufacturer(counter) & " +" & vendorPartNum(counter)
Next i
Cells.EntireColumn.AutoFit 'Autofit all columns...
MsgBox "Ready!" 'Finish!
End Sub
Related
I have written a simple VBA script (code below) that should inspect every cell in a certain column. Here I want to do some string manipulation ( i wante to search for "." in the string and then take the right side, but because I could not get it to work I always take the 4 digit as a start). I then copy the manipulated string into another cell and later back. The code works, but for some reason, it takes ages to run on only 35 cells!
I´m still a kook on VBA and wanted to get input what could be the reason for it and what I could improve to get a faster runtime. Is it because I take all strings froms 4 up to 50 ?
Sub EditStatus()
Application.DisplayAlerts = False
ActiveSheet.Name = "Backend"
myNum = Application.InputBox("Please enter the row number until which you would like to update the status column (only for new entries)")
For i = 2 To myNum
Cells(i, 20).Value = Mid(Cells(i, 10), 4, 50)
Cells(i, 10).Value = Cells(i, 20).Value
Next i
Range("T1:T200").Clear
Application.DisplayAlerts = True
End Sub
Thanks
No need for a loop. You can enter the formula in the entire range in 1 go and then convert them to values before putting the values back in Col J
Replace
For i = 2 To myNum
Cells(i, 20).Value = Mid(Cells(i, 10), 4, 50)
Cells(i, 10).Value = Cells(i, 20).Value
Next i
With
With Range("T2:T" & myNum)
.Formula = "=MID(J2, 4, 50)"
.Value = .Value
Range("J2:J" & myNum).Value = .Value
End With
Alternatively, you can directly perform the same action in Col J without the helper column T. For example you can do all that in 1 line as explained HERE as well
Simply replace
For i = 2 To myNum
Cells(i, 20).Value = Mid(Cells(i, 10), 4, 50)
Cells(i, 10).Value = Cells(i, 20).Value
Next i
Range("T1:T200").Clear
with
Range("J2:J" & myNum).Value = Evaluate("Index(MID(" & "J2:J" & myNum & ", 4, 50),)")
Replace Values In-Place
Adjust the values in the constants section.
This solution overwrites the data and doesn't use a helper column, but you can test it with one indicated near the end of the code.
Solve the renaming (Backend) part as needed.
The Code
Option Explicit
Sub EditStatus()
' Define constants.
Const sPrompt As String = "Please enter the row number until which you " _
& "would like to update the status column (only for new entries)"
Const sTitle As String = "Enter Number"
Const wsName As String = "Backend"
Const First As Long = 2
Const cCol As Long = 10 ' J
Const Delim As String = "."
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code.
' Require input.
Dim Last As Variant
Last = Application.InputBox( _
Prompt:=sPrompt, Title:=sTitle, Default:=First, Type:=1)
' Validate input.
If VarType(Last) = vbBoolean Then
MsgBox "You cancelled."
Exit Sub
End If
If Last < First Then
MsgBox "Enter a number greater than " & First - 1 & "."
Exit Sub
End If
If Int(Last) <> Last Then
MsgBox "Enter a WHOLE number greater than " & First - 1 & "."
Exit Sub
End If
' Define column range.
Dim rg As Range
Set rg = wb.Worksheets(wsName).Cells(First, cCol).Resize(Last - First + 1)
' Write values from column range to array.
Dim Data As Variant
If rg.Rows.Count > 1 Then
Data = rg.Value
Else
ReDim Data(1 To 1, 1 To 1): Data = rg.Value
End If
' Declare additional variables
Dim cValue As Variant ' Current Value
Dim i As Long ' Current Row (Array Row Counter)
Dim fPos As Long ' Current Delimiter Position
' Replace strings containing the delimiter, with the sub string
' to the right of it.
For i = 1 To UBound(Data)
cValue = Data(i, 1)
If Not IsError(cValue) Then
fPos = InStr(1, cValue, Delim)
If fPos > 0 Then
Data(i, 1) = Right(cValue, Len(cValue) - fPos)
End If
End If
Next i
' Maybe rather test with the following (writes to column 20 ("T")).
'rg.Offset(, 10).Value = Data
' Write values from array to column range.
rg.Value = Data
End Sub
I have an invoice from a service provider that I need to format so I can use the data in Excel. But, the formatting is not consistent.
There are three (3) columns:
ID
Description
Amount
Many ID#s on the invoice have a one line (row) description.
But just as many have 2-11 lines (rows) of description.
The ID# is only listed once with each set of description lines.
Up to this point, I have used Excel Formulas. But, all my formulas is making things go very slow.
VBA would be way faster.
What I have done is created an index system looking for new ID#s.
Then I have created a cascading concatenate formula based on the given index system.
The amount has been easy to pull out using a LEFT formula, since the amount lists USD.
I then have a second sheet that does a VLOOKUP off of the first sheet to pull the ID's, final concatenated descriptions, and Amounts.
Our last invoice had 17,427 lines of data with only 1,717 ID#s.
Here is an example of what I am working with:
I want it to look like this:
one of the possible solutions below:
'assume that Id in column `A`, Description in column `B`, Amount in `C` and header in row 1
Sub somecode()
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim sh As Worksheet: Set sh = wb.ActiveSheet
Dim lastRow&: lastRow = sh.Cells(Rows.Count, "B").End(xlUp).Row
Dim idColumn As Range: Set idColumn = sh.Range("A1:A" & lastRow)
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim cl As Range, keyID, valueDescription$, valueAmount$
For Each cl In idColumn
If cl.Value <> "" And Not dic.exists(cl.Value) Then
dic.Add cl.Value, sh.Cells(cl.Row, "B").Value & "|" & sh.Cells(cl.Row, "C").Value
keyID = cl.Value
valueDescription = sh.Cells(cl.Row, "B").Value
valueAmount = sh.Cells(cl.Row, "C").Value
ElseIf cl.Value = "" Then
valueDescription = valueDescription & " " & sh.Cells(cl.Row, "B").Value
dic(keyID) = valueDescription & "|" & valueAmount
End If
Next cl
Set sh = wb.Sheets.Add: sh.Name = "Result " & Date & " " & Replace(Time(), ":", "-")
Dim dkey, xRow&: xRow = 1
For Each dkey In dic
sh.Cells(xRow, "A").Value = dkey
sh.Cells(xRow, "B").Value = Split(dic(dkey), "|")(0)
sh.Cells(xRow, "C").Value = Split(dic(dkey), "|")(1)
xRow = xRow + 1
Next dkey
sh.Columns("A:C").AutoFit
End Sub
test:
I wrote code for you to do this job. Please install it in a standard code module. That is one that you have to insert. None of the existing is suitable.
Option Explicit
Enum Nws ' Worksheet setup (set values as required)
NwsFirstDataRow = 2
NwsNumColumns = 8 ' total number of columns in the sheet
NwsID = 1 ' Columns: 1 = column A
NwsDesc ' undefined = previous + 1
NwsAmt = 5 ' 5 = column E
End Enum
Sub MergeRows()
' Variatus #STO 24 Jan 2020
Dim Wb As Workbook
Dim Ws As Worksheet
Dim Rng As Range
Dim RowArr As Variant
Dim Desc As String, Amt As Double
Dim Tmp As Variant
Dim R As Long
' define workbook and worksheet as required
Set Wb = ActiveWorkbook ' this need not be ThisWorkbook
Set Ws = Wb.Worksheets("Invoice") ' change as appropriate
Application.ScreenUpdating = False
With Ws
R = .Cells(.Rows.Count, NwsDesc).End(xlUp).Row
For R = R To NwsFirstDataRow Step -1
If (R Mod 25) = 3 Then 'NwsFirstDataRow Then
Application.StatusBar = "Another " & R & " rows to process."
End If
Tmp = Trim(.Cells(R, NwsID).Value)
If Len(Tmp) Then
Set Rng = Range(.Cells(R, 1), .Cells(R, NwsNumColumns))
RowArr = Rng.Value
RowArr(1, NwsAmt) = TextToAmount(RowArr(1, NwsAmt))
If Len(Desc) Then
' if you want a comma instead of a line break
' replace Chr(10) with "," in the next line:-
RowArr(1, NwsDesc) = RowArr(1, NwsDesc) & Chr(10) & Desc
RowArr(1, NwsAmt) = RowArr(1, NwsAmt) + Amt
Desc = ""
Amt = 0
End If
With Rng
.Value = RowArr
.Cells.VerticalAlignment = xlTop
.Cells(NwsAmt).NumberFormat = "$#,##0.00"
End With
.Rows(R).AutoFit
Else
Tmp = Trim(.Cells(R, NwsDesc).Value)
If Len(Desc) Then Desc = Chr(10) & Desc
Desc = Tmp & Desc
Tmp = TextToAmount(.Cells(R, NwsAmt).Value)
If Tmp Then Amt = Amt + Tmp
.Rows(R).EntireRow.Delete
End If
Next R
End With
With Application
.ScreenUpdating = True
.StatusBar = "Done"
End With
End Sub
Private Function TextToAmount(ByVal Amt As Variant) As Double
Dim Tmp As Variant
Tmp = Trim(Amt)
If Len(Tmp) Then Tmp = Mid(Tmp, InStr(Tmp, "$") + 1)
TextToAmount = Val(Tmp)
End Function
Before you can run it you need to set the enumerations at the top to tell the code where your data and columns are. Toward the same end, please set the variables for workbook (Wb) and worksheet (Ws) in the procedure itself.
Note that the code adds the price, if any, in the rows that are deleted to the amount set against the remaining item.
Finally, you will see that I programmed the different rows to become lines in a single cell. That isn't what you asked for. If you want the items separate by commas look for the remark in the code where you can change this.
I am trying to copy named range in excel from one sheet to another, this works superb when I am using a static name. However now I would like to get the named range from a userform list box, and I am unsure how to do this. My copy function takes in the row number and I need to find this row number based on the string coming from the Listbox. If the listbox says Bolts the named range would be _OutputBolts which is refered to A123.
Call copyRows(ws, ThisWorkbook.Sheets("Templates").[_DrawingInputs].Row)
Call copyRows(ws, ThisWorkbook.Sheets("Templates").[_GeneralInputs].Row)
Call copyRows(ws, ThisWorkbook.Sheets("Templates").[_MaterialData].Row)
If GUI.ListBox_AdditionalComponents.ListCount > 0 Then
For i = 0 To GUI.ListBox_AdditionalComponents.ListCount - 1
namedRange = "[_Output" & GUI.ListBox_AdditionalComponents.List(i) & "]"
Call copyRows(ws, ThisWorkbook.Sheets("Templates").namedRange.Row)
Next i
End If
The copy procedure
Public Sub copyRows(ByRef shNew As Worksheet, startRow As Integer)
Dim i, j As Integer
Dim wsTemplates As Worksheet
Dim temp As Variant
Dim rowOverview As Integer
Dim lastCol As Integer
On Error Resume Next
Set wsTemplates = ThisWorkbook.Sheets("Templates")
i = startRow ' Where to copy from in templates
j = getLastRow(shNew, 1) 'Where to copy to, i.e append
If j > 2 Then
j = j + 2
End If
Do While wsTemplates.Cells(i, 1) <> ""
'copy the old range
wsTemplates.Rows(i).EntireRow.Copy
'paste it
shNew.Rows(j).EntireRow.Select
shNew.Paste
'format height
temp = wsTemplates.Rows(i).Height
shNew.Rows(j).RowHeight = CInt(temp)
' fill in the value from the GUI
temp = ""
temp = GUI.Controls("TextBox_" & Replace(shNew.Cells(j, 1).value, " ", "")).value
If temp = "" Then
temp = GUI.Controls("ComboBox_" & Replace(shNew.Cells(j, 1).value, " ", "")).value
End If
If temp <> "" Then
shNew.Cells(j, 4).value = temp
End If
'hyperlink drawing
If shNew.Cells(j, 1).value = "Drawing Name" Then
Call createHyperLink(shNew, j, 4, shNew.Cells(j, 4).value, GetFileNameWithOutExtension(getFilenameFromPath(shNew.Cells(j, 4).value)), shNew.Cells(j, 4).value)
End If
'update counters
i = i + 1
j = j + 1
Loop
' Format column widths, seems to be bug in this one...Maybe move out due to the fact we could do this once..
lastCol = getLastColumn(wsTemplates, 1)
For i = 1 To lastCol
temp = wsTemplates.Cells(1, i).Width
shNew.Columns(i).ColumnWidth = temp
Next i
End Sub
Solved by using Range(address), see comment
I have created a nested for loop to compare 3 different cell values within 2 sheets. The loop works fine when the data is small, but when I run on 5,000 rows its too slow and crashes excel. Any idea of how to run this more efficiently.
Sub RowMatch()
Dim x As Integer
' Make sure we are in the right sheet
Worksheets("Q416").Activate
' Set numrows = number of rows of data.
NumRows = Range("C2", Range("C2").End(xlDown)).Rows.count
' find the reference range
Worksheets("Q415").Activate
NumRows2 = Range("C5", Range("C5").End(xlDown)).Rows.count
Worksheets("Q416").Activate
MsgBox ("Total # of Rows on this sheet = " & NumRows & " and " & NumRows2 & " in Ref Range")
Range("A1").Select
' Establish "For" loop to loop "numrows" number of times.
For x = 1 To NumRows
'MsgBox NumRows2
For y = 1 To NumRows2
'MsgBox (ActiveCell.Offset(x, 0).Value & " & " & Worksheets("Q415").Cells(y + 1, 1))
If ActiveCell.Offset(x, 0).Value = Worksheets("Q415").Cells(y + 1, 1).Value _
And ActiveCell.Offset(x, 2).Value = Worksheets("Q415").Cells(y + 1, 3).Value Then
If ActiveCell.Offset(x, 5).Value = Worksheets("Q415").Cells(y + 1, 6).Value Then
'If NumRows(i).Value = ActiveCell.Offset(1, 0).Value Then
ActiveCell.Offset(x, 10).Value = "Same"
Else
ActiveCell.Offset(x, 10).Value = ActiveCell.Offset(x, 5).Value - Worksheets("Q415").Cells(y + 1, 6).Value
End If
End If
Next y
Next x
End Sub
Reading and writing to cells is one of the slowest operations you can do in Excel VBA. Instead, you should place the values contained in the worksheets into arrays and work with them there, Here is an excellent reference: http://www.cpearson.com/excel/ArraysAndRanges.aspx. Use your NumRows variables and either a column letter or number to define the ranges that will consitute the arrays e.g:
myRange = Range("A1:C" & NumRows)
myArray = myRange.value
From the link to Chip Pearsons site:
Dim Arr() As Variant
Arr = Range("A1:B10")
Dim R As Long
Dim C As Long
For R = 1 To UBound(Arr, 1) ' First array dimension is rows.
For C = 1 To UBound(Arr, 2) ' Second array dimension is columns.
Debug.Print Arr(R, C)
Next C
Next R
H,
i have an inventory sheet in this format.(provided image below, did not know how to add table over here). The company name and category are not in separate columns. instead the are listed in the product column. i want to add 2 additional columns, one for company and one for category, and add details to every row correspondingly.
original format
desired format
what would be the simplest way to do this?
This should do what you want, it assumes your data starts in column A:
Sub ChangeFormat()
Dim CompanyName As String, Catgory As String, LR As Long
LR = Range("A" & Rows.Count).End(xlUp).Row
For X = 2 To LR
If X > LR Then Exit For
If Left(UCase(Range("A" & X).text), 8) = "COMPANY:" Then
CompanyName = Trim(Right(Range("A" & X).text, Len(Range("A" & X).text) - 8))
Rows(X).Delete
LR = LR - 1
End If
If Left(UCase(Range("A" & X).text), 9) = "CATEGORY:" Then
Category = Trim(Right(Range("A" & X).text, Len(Range("A" & X).text) - 9))
Rows(X).Delete
LR = LR - 1
End If
Range("E" & X).Formula = CompanyName
Range("F" & X).Formula = Category
Next
End Sub
It scans through and when it finds one of the headings it assigns that value to the variable then deletes the row, if it finds neither then it assumes it is data and posts the Company and Category to columns E and F (I think Catagory is spelled wrong but I went with your spelling of it).
Make sure you back up your data before running this as it will modify what you have.
Here's a macro to do the task. You can change the name of the sheet as indicated in the code.
Sub InventoryReformat()
Dim ar
Dim i As Long
Dim wRow As Long
Dim sTxt As String, sCompany As String, sCategory As String
Dim wsS As Worksheet, wsD As Worksheet
Set wsS = Sheets("SOURCE_DATA") 'Change as required
Set wsD = Sheets("DESTINATION") 'Change as required
ar = wsS.Range("A1").CurrentRegion.Value 'Change start cell as required
wRow = 1 'Change first destination row as required
With wsD
.Cells(wRow, 1).Resize(1, 6) = Split("PRODUCT|COST PRICE|SALE PRICE|TAX|CATEGORY|COMPANY", "|")
wRow = wRow + 1
For i = 2 To UBound(ar, 1)
sTxt = ar(i, 1)
If InStr(1, sTxt, "Company") > 0 Then
sCompany = Trim(Split(sTxt, ":")(1))
Else
If InStr(1, sTxt, "Category") > 0 Then
sCategory = Trim(Split(sTxt, ":")(1))
Else
.Cells(wRow, 1) = ar(i, 1)
.Cells(wRow, 2) = ar(i, 2)
.Cells(wRow, 3) = ar(i, 3)
.Cells(wRow, 4) = ar(i, 4)
.Cells(wRow, 5) = sCategory
.Cells(wRow, 6) = sCompany
wRow = wRow + 1
End If
End If
Next i
.Cells(wRow - 1, 1).CurrentRegion.Columns.AutoFit
End With
End Sub
.
Hope this will help you.