I run a small retail store and I've been tasked with creating inventory labels for our items. I export a list of items from an inventory management software (AMan Pro) into Excel taking the items' Description, Quantity, Condition, SKU#, and Platform (video game platform). I've got two Macros currently. One will trim up the SKU to a usable format (gets rid of leading letters) and the other separates multiple quantity items out to separate rows. (i.e. item #1 with a Quantity of 5 will be copied to 5 rows all with a quantity of 1) That stuff works, but I think I'm doing some unnecessary steps that could be handled with a macro.
My AMan program spits out an Excel workbook with the items. I then copy those items into my 'macro-enabled' workbook on sheet one and then run my macros. Sheet two has the properly formatted data for the labels. For example; it has formulas that trim the Description field to only use the first 60 characters. (some of the descriptions are pretty long)
I'm feeling like that second sheet isn't really necessary. I would like to copy the items into Sheet 1 and run a macro that does all of that formatting for me on sheet 1.
Separate items with multiple quanities onto separate rows (I do have
a working macro for this.)
Remove leading letters from SKU (working macro) and then put SKU in seven digit number format (#######)
Reduce the Description to only the first 60 characters.
Reduce the Condition to only the first 2 characters.
Reduce the Platform to only the first 15 characters.
Here's my current macro code and a link to my spreadsheet. Thanks in advance, guys.
SKU_LABEL_FINAL.xlsm
Sub ExpandRows()
Dim dat As Variant
Dim i As Long
Dim rw As Range
Dim rng As Range
Set rng = Sheets(1).UsedRange
dat = rng
' Loop thru your data, starting at the last row
For i = UBound(dat, 1) To 2 Step -1
' If Quantity > 1
If dat(i, 2) > 1 Then
' Insert rows to make space
Set rw = rng.Rows(i).EntireRow
rw.Offset(1, 0).Resize(dat(i, 2) - 1).Insert
' copy row data down
rw.Copy rw.Offset(1, 0).Resize(dat(i, 2) - 1)
' set Quantity to 1
rw.Cells(1, 2).Resize(dat(i, 2), 1) = 1
End If
Next
Columns("D:D").Select
Selection.Replace What:="AManPro-", Replacement:="", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End Sub
To format your SKUs into a 7 digit number format is pretty simple with what you have. You would just need to insert this line of code before the End Sub line
Selection.NumberFormat = "0000000"
You could trim the length of each cell when you parse through the data.
First, I would use some Const statements to note which columns are what, and what length they need to be. You could just use the numbers in the code, but this makes it easier to update things in the future if they change. Add below the final Dim
Const DESCRIPTIONLENGTH = 60
Const DESCRIPTIONCOLUMN = 2
Then as you loop through each row, you would then update the value of the cell - add below the For i =...
'Format each column's data
dat(i, DESCRIPTIONCOLUMN).Value = Left(dat(i, DESCRIPTIONCOLUMN), DESCRIPTIONLENGTH)
I'm hoping the code for the description column will help you to build what you need for the other columns as well.
Related
My apologies up front if this post contains too much information. I am new to VBA and this site.
After some difficulties trying to run a Macro I recorded, I have tried to break it up into smaller portions. One of those portions includes appending two columns of data, which will then be used to create a table. The data in these columns is coming from two other worksheets in the same workbook.
When the data is transferred directly by me, the shortened Macro works fine. However, when I use a formula to transfer the data, the Macro does not work. I would appreciate suggestions on how to edit the shortened Macro to either adjust for the formula or edit the VBA to transfer the data and then append, so I might then proceed with attempting to create the table. The code I have been using is:
Dim col As Range, _
found As Range
Dim currRow As Integer
currRow = ActiveSheet.Range("A:A").Find("", after:=ActiveSheet.Range("A1"), lookat:=xlWhole, searchdirection:=xlNext).Row
For Each col In ActiveSheet.UsedRange.Columns
If col.Column <> 1 Then
Set found = col.EntireColumn.Find("", after:=col.Cells(1, 1), lookat:=xlWhole, searchdirection:=xlNext)
Set found = ActiveSheet.Range(col.Cells(1, 1), found)
found.Copy
ActiveSheet.Cells(currRow, 1).PasteSpecial
currRow = currRow + found.Cells.Count - 1
End If
Next col
End Sub
I should have mentioned the columns of data being copied to the worksheet will have headers and vary in the number of rows from workbook to workbook. I will try to attach images of the start and desired endpoints.
The formulas are applied to 200 rows in each column. When I use the Macro on the data copied using the formula, it seems to append the 200 cells of formula from column 2 to the 200 cells of formula in column 1. The data stays in each column, and after my last data point in column 1 I now have blank cells down to row 400 that have the formula instead of data.
BEginning View
Possible Append Result 1
Possible Append result 2
I'm trying to automate the following problem: I have an excel workbook with a large number of quotes (each quote tab is in the format of "Q-#####"). Each quote tab has a series of calculations. I updated cell "L6" on every tab. Starting on row 6, there are a number of SKUs - each quote tab has a different number of SKUs.
The below is trying to do the following:
Determine if the worksheet starts with "Q-"
Determine the row of the last SKU (None go past row 100 so I put that to hopefully save time) then I remove 5 because I want to count the number of rows past L6
I want to then set the range as L6 to the last row by using the integer I determined in step 2
Then I want to copy the formula in L6 and paste it into every cell in my range (it's a very long formula that uses relative references so I wanted paste formulas)
I fixed my previous error (thanks BigBen) and now the copy/paste function is only working on the first identified Q- tab. On the subsequent Q tabs, I can see it selecting the right ranges but the paste doesn't work.
Accountant here, so I'm trying my best but a complete novice!
For Each ws In Worksheets
If ws.Name Like "Q-*" Then
Dim nRows As Integer
nRows = ws.Range("L6:L100").Find(What:="*", LookIn:=xlValues, SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
nRows = nRows - 5
Range("L6").Copy
Dim cell As Range
Set cell = Range("L6")
cell.Resize(nRows, 1).Select
cell.PasteSpecial (xlPasteFormulas)
End If
Next ws
End Sub```
I have a text based system with people's names, and I need to copy and paste the first 4 cells (not a problem) and the last 5 cells to a different worksheet for analysis.
The problem arises with the transfer from the text based system the data is presented in, to the spreadsheet when it comes to people with spaces in their surname (ie, Da Silva). I use text to columns, which will give me a variant number columns, depending on the number of spaces in the name which is an issue.
I already have a crude solution, but the time it takes and the jumping about between screens while the macro is running looks very unprofessional. I don't get much spare time at work, and I don't have the tools to test this properly at home.
Can anyone help me get this to loop until the last empty cell, and basically neaten it up a little?
The code, which repeats 300 times, is as following:
Sheets("Late list Sorting").Select
Range("A2").End(xlToRight).Select
Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, -5)).Select
Selection.Copy
Sheets("Late list").Select
Range("D4").Select
ActiveSheet.Paste
(...Repeat until...)
Range("A300").End(xlToRight).Select
Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, -5)).Select
Selection.Copy
Sheets("Late list").Select
Range("D302").Select
ActiveSheet.Paste
Sheets("Late list Sorting").Select
If you only want the values (not formulae or formatting) then you can simply use Value=Value...
Dim i As Long, sWs As Excel.Worksheet, tWs As Excel.Worksheet
Set sWs = ThisWorkbook.Worksheets("Late list Sorting")
Set tWs = ThisWorkbook.Worksheets("Late list")
For i = 2 To 300
tWs.Range(tWs.Cells(i + 2, 4), tWs.Cells(i + 2, 8)).Value = sWs.Range(sWs.Cells(i, 1).End(xlToRight), sWs.Cells(i, 1).End(xlToRight).Offset(0, -5)).Value
Next
it is inefficient to use .Select, here's a great resource: How to avoid using Select in Excel VBA
To eliminate the screen flashes, put this at the beginning of your code. Remember to turn it back to true at the end.
Application.ScreenUpdating = False
You want to use a loop, where the variable i becomes the row number you will reference like so:
Range("A" & i).Value...
There are many ways to loop, here is one example:
For i = 1 to 300
//All your code here
Next i
Hoping you can help an basic excel user please!
I have a file of around 2000 rows and I need to add a line/break after every third one. Is there a simple way of doing this please?
Your help and advice would be much appreciated.
Thanks
Quick way without VBA
In a empty column add this formula, =IF(MOD(ROW(),3)=0,NA(),"") and copy down
Press F5, Goto .... Special, Formulas Errors (selects every third row)
Insert Rows
step 2 shown below
If you want to try some VBA here is a button click event that will do the insert on every third row. Let me know if you have any questions.
Private Sub CommandButton1_Click()
Dim ws As Excel.Worksheet
Dim lRow As Long
Dim lastRow As Long
'Set the worksheet object to the sheet by name
Set ws = Application.Sheets("Sheet1")
'Set the row to start looping(inserting) rows at
lRow = 4
'Find the last row with a value in column A
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'Account for the amount of rows that will be inserted.
lastRow = lastRow + (lastRow * 0.33)
'Loop through the worksheet from the start row to the last row
Do While lRow <= lastRow
'Insert a row
ws.Rows(lRow).EntireRow.Insert
'Increment the row to insert at on the next pass of the loop
lRow = lRow + 4
Loop
End Sub
a non vba way is create a new column and insert numbering
1
2
3
4
5
6
......
then for empty row, number as 3,6,9,.... (let say u have 2k records, duplicate it 2k)
then sort by the number column, then remove the column
If your content is the same, or repeating, for each row, you could open the file in any basic text editor and do the following:
Highlight and copy the first three rows (including the third line break).
Using find-and-replace (Opt-Cmd-F in TextEdit on Macs), copy that content into the 'find' field as well as the 'replace' field.
Add a line break at the end of the content you pasted in the 'replace' field.
Execute the find-and-replace action.
This should turn something like this:
item
item
item
item
item
item
item
...into this:
item
item
item
item
item
item
item
...and so on.
This definitely isn't the most elegant solution, but is one of the quickest/simplest I've seen without resorting to a text parsing script in bash, etc.
Assuming data starts in A1, in B1 and copied down to suit (i.e. past the end of the cells populated in ColumnA):
=IF(MOD(ROW(),4)=0,"",OFFSET(A$1,3*INT((ROW()-1)/4)+MOD(ROW(),4)-1,))
Here is a VBA equivalent to the solution proposed by Eric K. above. The orientation assumes column header labels in row 1 that should be left alone.
Sub insBlankFourthRow()
Debug.Print Timer
With Worksheets("Sheet3")
.Columns(1).Insert
With .Cells(1, 1).CurrentRegion '<~~ original CurrentRegion
With .Resize(.Rows.Count - 1, 1).Offset(1, 0)
.Cells(1, 1) = 1
.Cells.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Step:=1
End With
With .Resize(Int(.Rows.Count / 3) + 1, 1).Offset(.Rows.Count, 0)
.Cells(1, 1) = 3.5
.Cells.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Step:=3
End With
End With
'
With .Cells(1, 1).CurrentRegion '<~~ new expanded CurrentRegion
.Cells.Sort Key1:=.Columns(1), Order1:=xlAscending, _
Orientation:=xlTopToBottom, Header:=xlYes
End With
.Columns(1).Delete
End With
Debug.Print Timer
End Sub
tbh, ~2000 rows of data isn't that much to be worried about but 10× or 100× that amount of data will start to lag significantly when inserting rows individually or in a bulk non-contiguous orientation. A 'helper' column populated with a Range.DataSeries method (fastest way I know of populating a sequence) can be readily discarded once its purpose has been fulfilled.
Running the above against 2500 rows of random data typical of the image took ⁸⁄₁₀₀ of a second. That time might be moderately improved with disabling the Application.ScreenUpdating property and similar overhead.
I am attempting to write a fairly intelligent macro for Excel. In this instance, I have two seperate workbooks that I am transferring information between. The first workbook has a listing of dates in column A followed by corresponding values in the following columns. Like so:
As you can see, the dates and values are not consistently entered. They are only available certain days. The current entries stop at the end of 2012. The point of this macro is to add new numbers each month.
an excerpt from Workbook 2 looks like this:
My current macro currently locates the correct values in workbook 2 and copies them to Workbook 1.
Which is great except for the possibility of inconsistent dates. Where there could be 15 days worth of values in the "CMP (3 YEARS)"section, but only 14 days worth in the "CMP (4 YEARS)" section. If I were to blindly copy those values over to sheet one they would not line up correctly. I would rather there be a calculated average if a date is missing
So to remedy this problem, I am attempting the current solution:
'Grab 1 Month
Call Import("CMA/FIXED (4 WEEKS)", "B1", DestinationWorkbook, ExportedWorkbook, 3, 2, False, DestinationDateRange)
The above method is called for each column of numbers to be located as well as for extracting the dates.
Allow me to explain what is supposed to be happening below. First the String to search for in the second sheet is passed in. this is used in conjunction with the x and y offset integers to locate the correct values. The DestinationSheetName is Workbook1 and ExportedDataSheetName is Workbook2. the isDateImport boolean is there to tell the method that this is the first import and all we are grabbing is the first set of dates that will be added to column A of Workbook1. The ByRef DateRange is a pointer to those dates after they have been copied and pasted to column A of Workbook1. So obviously when this is used for dates initially this is null.
from there we check to see what kind of import we are doing. If it is for dates we just do a simple locate, copy and past. If not, we attempt an inteligent import of the located values. The idea is to create a pointer to both sets of dates, The dates in Column A in workbook1 and the dates that correspond to the values we are copying. We want the paste location to be in the correct row for each date. I create both pointers and point them at the start of both date columns. In place of the copy paste code I am just printing to the debug window. if the pointers values match each other. After comparing the dates I attempt to increment the pointers to the next values in the columns of dates.
This works great for 2 dates and then I get an error when I try to increment the pointers.
What should I be doing to fix this error? or is there an easier way to do this?... I know pointers are a little overkill for Excel spreadsheets...
Sub Import(SearchString As String, PasteLocation As String _
, DestinationSheetName As Variant _
, ExportedDataSheetName As Variant, xOffset As Integer, yOffset As Integer _
, isDateImport As Boolean, ByRef DateRange)
Windows(DestinationSheetName).Activate
Set newSpot = Range(PasteLocation).End(xlDown)
newSpot.Select 'remove
Set newSpot = newSpot.Item(2, 1)
Windows(ExportedDataSheetName).Activate
Cells.Find(What:=SearchString, After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.Select
If Not isDateImport Then
'intelligent import
Set datesColumn = ActiveCell.Item(xOffset, yOffset)
datesColumn.Select 'remove
Set valuesColumn = datesColumn.Item(1, 2)
valuesColumn.Select 'remove
Set datesColumn = Range(datesColumn, datesColumn.End(xlDown))
datesColumn.Select 'remove
Set valuesColumn = Range(valuesColumn, valuesColumn.End(xlDown))
valuesColumn.Select 'remove
Set DateColumnPointer = datesColumn.Item(1, 1)
DateColumnPointer.Select 'remove
Set DateRangePointer = DateRange.Item(1, 1)
Windows(DestinationSheetName).Activate
DateRangePointer.Select 'remove
For Each cell In valuesColumn
If (DateColumnPointer = DateRangePointer) Then
Debug.Print "Same"
Else
Debug.Print "Different"
End If
'increment Pointers
Windows(ExportedDataSheetName).Activate
DateColumnPointer = DateColumnPointer.Item(2, 1)
Windows(DestinationSheetName).Activate
DateRangePointer = DateRangePointer.Item(2, 1)
Next
Else
'primitive import
Set cell1 = ActiveCell.Item(xOffset, yOffset)
If isDateImport Then
Set cell2 = cell1.End(xlDown)
Else
Set cell2 = cell1.End(xlDown).End(xlToRight)
End If
Set rng = Range(cell1, cell2)
rng.Select
rng.Copy
Windows(DestinationSheetName).Activate
If isDateImport Then
Range("W1").Select
Else
Range("V1").Select
End If
ActiveSheet.Paste
'Add grabbed values
Set numbers = Range(Range("W1"), Range("W1").End(xlDown))
numbers.Copy
newSpot.PasteSpecial xlValues
End If
Windows(ExportedDataSheetName).Activate
Range("A1").Select
End Sub
You'll need to use set in your increment pointers section to increase the range incrementally otherwise your only copying the values from the next cell up to the current one.
'increment Pointers
Windows(ExportedDataSheetName).Activate
Set DateColumnPointer = DateColumnPointer.Item(2, 1)
Windows(DestinationSheetName).Activate
Set DateRangePointer = DateRangePointer.Item(2, 1)
Unless I've missed the point of your question?