Excel 2013 - Looking for a macro to write a sum for variable length sections - vba

I'm trying to find a macro that will write a sum formula for a variable number of rows.
I have a bunch of sections on a fairly extensive sheet. Each section has a column of costs, and each needs a sum at the end. However, the sections are variable in length, so I can't just write one sum and copy/paste it to each section.
Sections are stacked like this:
Section Headers
1 Data $
2 Data $
3 Data $
Sum?
Section Headers
1 Data $
2 Data $
3 Data $
4 Data $
5 Data $
6 Data $
7 Data $
Sum?
I have a few hundred of these to do, and I'd rather not manually write the sum formula each time. The lower boundary of the range I need summed is always the same relative to the formula cell, but the upper boundary is a random number of rows higher. Is there a way to have a macro auto detect how big the section is and make the sum only for that range?

This block of code assumes that each section heading starts with the word "Corrections" and that there are no blank rows between sections (so any blank cells will be filled with a sum formula). It also assumes the data is in column A. If your spreadsheet isn't setup like that, you could tweak this to work for your situation. Note that there isn't much in terms of error checking here, so you'll have to make sure your spreadsheet doesn't have any issues, or add error checks to the code (for example, if your spreadsheet doesn't start with a section heading, you may have an issue, and if you have sections with no values--that is a section header and then no data under it--you'll have an issue).
Dim wksh as worksheet
Dim last_row as long
Dim curr_start as long
set wksh = thisworkbook.worksheets("<your worksheet name>")
last_row = wksh.cells(wksh.Rows.Count, "A").End(xlUp).Row
for i = 1 to last_row + 1
if wksh.range("A"&i).value=0 or wksh.range("A"&i)="" then
wksh.range("A"&i).formula = "=SUM(A" & curr_start & ":A" & i-1 & ")"
elseif left(wksh.range("A"&i).value, 4)="Corr" then
curr_start = i + 1
endif
next i

Related

Concatenating data descriptions and aligning to data range

I have been trying to learn visual basic to write code to perform tasks when working with data in excel. I have been mostly copying snippets of code I find online and piecing them together. Currently, I have folders containing 10's of thousands of .csv files (data output from a CMM).
In each of these files column A consistently contains labels for the data and column B consistently contains the CMM data.
Currently, my program allows a user to select multiple .csv files and in a long roundabout way they all end up on one worksheet in excel with the data labels in the first column and the data in the next columns.
For example, if 10 CSV files are opened the data labels would be in the first column and the data would be in the next 10 columns.
The problem is that that the data labels are not aligned with the data and often each row of data has multiple labels.
I have been able to concatenate the data labels into one label for each row of data but cannot figure out how to align this label with the row of data.
At this point, I would be happy with a separate block of code that accomplishes this but... I suspect that my block of code that concatenates the labels could be easily modified to accomplish the task, I just haven't been able to figure it out.
So my code spits this out:
(Flatness) : Item (113)
Plane:RH_5_Mating_Surface
5.012 4.014 6.313 etc...
(Z) : Item (128) / (X) : Item (135)
Circle:Offset_Dowel_Hole
1.012 2.987 5.478 etc...
Circle:Cast_Hole_From_Offset_Dowel_Hole
2.147 7.895 4.258 etc...
Then this code concatenates the labels and spits them out in column B:
Dim rng1 As Range
Dim Lastrow As Long
Dim c As Range
Dim concat As String
Lastrow = .UsedRange.Rows(.UsedRange.Rows.Count).Row
Set rng1 = Range("A9:A" & Lastrow)
concat = ""
For Each c In rng1
If c > 0 Then
concat = concat & " " & c.Value
concat = Trim(concat)
Else
c.Offset(-1, 1).Value = concat
concat = ""
End If
Next c
The result is:
(Flatness) : Item (113) Plane:RH_5_Mating_Surface
5.012 4.014 6.313 etc...
(Z) : Item (128) / (X) : Item (135) Circle:Offset_Dowel_Hole
1.012 2.987 5.478 etc...
Circle:Cast_Hole_From_Offset_Dowel_Hole
2.147 7.895 4.258 etc...
What I need is:
I cant figure out how to show it here but...
I need the rows to match up, also note, here it shows that the data and labels are offset by the same amount but in reality they are not. So my thinking is that I need it to search for the next row containing data and put the label next to it.
I feel like I can just change this part...
Else
c.Offset(-1, 1).Value = concat
but I don't know how to do...
I tried nesting another "For Each" here instead similar to what its already doing but with a "For Each d In rng2" where "rng2" was the data column and it would look for the next row with data and place "concat" next to the data using "d.offset(-1, -1).Value = concat"
I couldn't figure out how to get it to work...
This is one possible way of doing it.
Delete the empty cells and shift up in the columns. If the data order is consistent, they should line up correctly.
For i=1 to Lastrow
If Range("A" & i).Value=""
Range("A" & i).Delete Shift:=xlUp
End If
If Range("B" & i).Value=""
Range("B" & i).Delete Shift:=xlUp
End If
Next
You can change the range according to your sheet. Also, modify the code to use another 'IF' condition to check blanks, if that works better for your case.

Find cell value, match, cut, move, ...vba

I am a beginner in VBA.
I have components which always consist from 2 parts. (Rotor and a stator, each has its own number). When work is with them it can be damaging some of these parts, however it is necessary to keep a list of damaged parts, where the result is inventory e.g. 200 rotors, stators 150 with different numbers. Before I could scrap it, I need to complete them as proper sets. I.e. rotor "a" stator "a", "b" with "b", etc. It's crazy to work with many numbers to compare them, copy …to find the result of sets qty.
It is possible to solve it with Macro, what I try to do, but I was stuck.
What is the task: In the column "A" I have a list of all damaged parts (mix of rotors, stators different numbers). In the column "C" an information only with help of VlookUP, what should be a counterpart number.
What do I need to solve: In row 5, column. „A“ I have component number , but I know that in the same column, somewhere from row 6 to xx I have a counterpart. What I need is … according to information from column C, same row(5) where is info about the counterpart num. to find counerpart in column A, when found, took it out and put into cell B5. Thus,I get a complete set. Then the next row (6), same action. Macro reading num. in „C“,searching in „A“, when found, cut, and put to „B“ next row 7,8,9,… The result should be a certain qty of pairs + some single numbers if not second part found.
The problem I have is that cycle is working until always found relared counterpart. If the counterpart in row A is not available (no match betwen C-A), the code will stop on that row.
What I need help with is, that if code did not find the counerpart based to info from C just skip this row, make it red and continue with next row till end, it means stop on first empty cell in C. Thanks a lot to everybody who is helping me.
Dim pn As Range,
Dim a
Dim x
x = 5
Dim i As Long, Dim radek As Long
a = Cells(x, 3)
For i = 1 To 500
Range("A:A").Select
Set pn = Selection.Find(What:=a)
If Not pn Is Nothing Then
pn.Select
End If
Selection.Cut
Cells(x, 2).Select
ActiveSheet.Paste
x = x + 1
Next
End Sub

Copying Row Info from one sheet to another based on match

I have an excel book that has two sheets: 1) Import 2) Pricing Rules.
Pricing Rules Sheet
The A column is what I need to match on. Example values include STA_PNP4, STA_PST.. and others. There are potentially around 50 different rows in the sheet, and it will continue to grow over time. Then for each row, there are pricing values in columns B to CF.
Import Sheet
This sheet has the same number of columns, but only Column A is filled out. Example values include STA_PNP4_001_00, STA_PNP4_007_00, STA_PST_010_00.. and many more.
What I need to do:
If the text in Import Sheet Column A before the second "_" matches the column identifer in Pricing Rules Sheet Column A, copy the rest of B to CF of Pricing Rules sheet for that row into the Import sheet for the row it matched on.
Any idea on where to begin with this one?
Why don't you do it using formulas only?
Assuming :
1.) Data in Import Sheet is
(col A)
STA_PNP4_007_00
STA_PNP4_001_00
STA_PNP4_001_00
.
.
2.) Data in Pricing Rules Sheet
(Col A) (col B) (ColC) (Col D) .......
STA_PNP4 1 2 3 .....
STA_PST 4 5 6 .....
STA_ASA2 7 8 9 .....
Then write this formula in B1 cell of Import Sheet
=IFERROR(VLOOKUP(LEFT(A1,FIND("",A1,FIND("",A1)+1)-1),PricingRules!$A$1:$CF$100,2,0),"")
Drag it down in column B
and For Column C , D just change index num from 2 to (3 for C) , (4 for D) and like that.
Because it will continue to grow over time you may be best using VBA. However, even with code I would start by applying the ‘groups’ via formula, so as not to have a spreadsheet overburdened with formulae and hence potentially slow and easy to corrupt. Something like part of #xtremeExcel’s solution which I repeat because the underscores have been treated as formatting commands in that answer:
=LEFT(A1,FIND("_",A1,1+FIND("_",A1))-1)
I’d envisage this (copied down) as an additional column in your Import Sheet - to serve as a key field to link to your Pricing Rules Sheet. Say on the extreme left so available for use by VLOOKUP across the entire sheet.
With that as a key field then either:
Write the code to populate Pricing Rules Sheet as frequently as run/desired. Either populating ‘from scratch’ each time (perhaps best for low volumes) or incrementally (likely advisable for high volumes).
Use VLOOKUP (as suggested). However with at least 84 columns and, presumably, many more than 50 rows that is a lot of formulae, though may be viable as a temporary ‘once off’ solution (ie after population Copy/Paste Special/Values).
A compromise. As 2. But preserve a row or a cell with the appropriate formulae/a and copy that to populate the other columns for your additions to your ColumnA and/or ColumnA:B.
Thanks for the input guys.
I got it implemented via a method like this:
{=VLOOKUP(LEFT($A4,7),PricingRules!A3:CF112,{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84},FALSE)}
That is my ugly function, applied across a whole row, to look up and copy from my pricing rules every column when it finds a match.
Below is the function that I have created for above scenario. Its working as per the requirement that you have mentioned.
Sub CopyData()
Dim wb As Workbook
Dim importws As Worksheet
Dim PricingRulesws As Worksheet
Dim Pricingrowcount As Integer
Dim importRowCount As Integer
Dim FindValue As String
Dim textvalue As String
Dim columncount As Integer
Dim stringarray() As String
'Enter full address of your file ex: "C:\newfolder\datafile.xlsx"
Set wb = Workbooks.Open("C:\newfolder\datafile.xlsx")
'Enter the name of your "import" sheet
Set importws = Sheets("Import")
'Enter the name of your "Pricing" sheet
Set PricingRulesws = Sheets("PricingRules")
For Pricingrowcount = 1 To PricingRulesws.UsedRange.Rows.Count
FindValue = PricingRulesws.Cells(Pricingrowcount, 1)
For importRowCount = 1 To importws.UsedRange.Rows.Count
textvalue = importws.Cells(importRowCount, 1)
stringarray = Split(textvalue, "_")
textvalue = stringarray(0) & "_" & stringarray(1)
If FindValue = textvalue Then
For columncount = 2 To PricingRulesws.UsedRange.Columns.Count
importws.Cells(importRowCount, columncount) = PricingRulesws.Cells(Pricingrowcount, columncount)
Next columncount
End If
Next importRowCount
Next Pricingrowcount
End Sub

VBA - Search and remove duplicates

I'm looking for an algorithm for which I do not have the VBA knowledge to script myself. So I'm stuck. It isn't through lack of effort trying because I have given it a go (plus, this bit of code is the last remaining piece of my bigger VBA code) I simply lack the knowledge/experience/skill...
Basically, I have an Excel file. In this file is a sheet, "sheet1". Sheet1 contains many rows of data. The number of rows contained in sheet1 can vary from 1 to n. Sometimes, I may have 50 while other times I may have 30, etc. What is consistent is the layout of the book, i.e. I have codes in column A which identify a product in my database.
What I want to do is this:
1. Scan the sheet for empty rows (due to the way the workbook is generated, I sometimes have blank rows) and remove them. These blank rows are sometimes in-between rows with data while at other times may be trailing at the end of the sheet.
2. After removing the blank rows find the last used row. Store that to a variable. I have found this piece of code useful for doing that:
mylastrow = myBook.Sheets("Results").Cells.Find(what:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
3. Starting from the row determined in (2), I want to take the product code in A(x where x = mylastrow) and find any other occurrences of it (in column A). If any are found, delete that entire row corresponding to it. Importantly, this loop must go in reverse. For example let's say mylastrow = 40, the loop will need to begin at A40 and on the next iteration do A39 (or 38 if a row has been removed?). This is because with any of the product numbers the corresponding data in the row contains more data further down the column (because of the way the sheet was generated). Essentially the entry closest to the last row is the most recent.
Hopefully I've been able to explain the situ properly. But if not and you're willing to take the challenge (my burden?) off me I would be very grateful.
QF
The only way to develop that knowledge and skill is to get in there and code! I'm sure someone may come in and write you the entire procedure, but in the meantime these resources should give you the tools to do it yourself.
First, check out the method here to delete blank rows. It relies on "Selection" for the range, so you can either manually select all the cells of the sheet, then run the macro, or replace it with the following:
Dim r as range
set r = Sheet1.Cells 'now use r instead of Selection
OR (even better) use your code for finding the last used row and set the range from row 1 to "mylastrow".
Next, beginning from "mylastrow", start adding the values in Column A to a Dictionary object (example here). You can use a row counter to decrement from "mylastrow" to 1. Here's an example of how it would work. The key is assumed to be in the 1st column ("A").
Dim dict As Object
Dim rowCount As Long
Dim strVal As String
Set dict = CreateObject("Scripting.Dictionary")
rowCount = Sheet1.Range("A1").CurrentRegion.Rows.Count
Do While rowCount > 1
strVal = Sheet1.Cells(rowCount, 1).Value2
If dict.exists(strVal) Then
Sheet1.Rows(rowCount).EntireRow.Delete
Else
dict.Add strVal, 0
End If
rowCount = rowCount - 1
Loop
Set dict = Nothing
Before:
After:
Note that the 1st row hasn't been touched since we stopped when rowCount is 1 (assumes there's a header).

Excel pulling data from certain cells

I have a file that I only want to extract cells B9, B19, B29, etc etc etc in a pattern throughout the entire file. I would preferably like it to be extracted to a different excel file or someway so that I can do stuff with only those cells in another excel worksheet.
Potentially, I may have several excel files that I may need to do this sort of thing so if there were a way where I had the same format throughout a lot of files that I could always extract cells B9, B19, B29 that would be great. any help appreciated
I looking for syntax if possible
EDIT
Was thinking if I could somehow make an excel IF statement saying if Row has a 9 in it and the row is B then print it somewhere but I want it printed in a column
EDIT 2
I just want column B not A like I mentioned before.
B9, B19,B29,B39 through the whole file
Just in case you want to do it with code:
Sub Test()
'Assumes Sheet1 has your values and Sheet2 will be the data extracted from every row ending in 9
Dim iCounter As Long
Dim newSheetRow As Long
Dim aValue As String
Dim bValue As String
newSheetRow = 1
'Start and nine and increment by 10 till you reach end of sheet
For iCounter = 9 To Sheet1.Rows.Count - 1 Step 10 'NOTE: You may not want to do it by RowCount, but just showing you could
aValue = Sheet1.Range("A" & iCounter)
bValue = Sheet1.Range("B" & iCounter)
Sheet2.Range("A" & newSheetRow).Value = "We were on row: " & iCounter
Sheet2.Range("B" & newSheetRow).Value = aValue
Sheet2.Range("C" & newSheetRow).Value = bValue
newSheetRow = newSheetRow + 1
Next iCounter
MsgBox "Done"
End Sub
You could use the INDIRECT function. It takes a cell reference as a text string and returns the value in that cell. So instead of using
=data!a9
to get the value in sheet "data" in cell a9, you use
=indirect("data!a9")
You can also use r1c1 notation, like this:
=indirect("data!r9c1",false)
From there you can use the ROW and COLUMN functions to go in steps of 10:
=INDIRECT("data!r"&-1+10*ROW()&"c"&COLUMN(),FALSE)
If you put this formula in A1 of your output sheet and then copy and paste it down and across, it will give you the values in data!A9, data!A19, data!A29,... in cells A1, A2, A3... Depending on how you want your output arranged, you might have to modify the cell reference string.
Depending on how often you want to do this depends on how you need to do it, if it's a one of them some simple excel commands might help.
e.g.
In Cell C1 put the following:
=MOD(ROW(),10)
then replicate this down to the bottom of your data. the command will return the numbers 1 through to 0. You can then filter the data on column C where value is 9 then select the visible rows and copy the data to a new sheet.
ROW() ' this returns the ROW number of cell the command is in.
MOD(number, divisor) ' this basically divides one number by the other and returns the remainder. so row 9 / 10 = 0 remainder of 9, row 19 / 10 = 1 remainder of 9.
Hope this helps.