I have a big file with data, updated weekly, from which a VBA script copies a lot of columns of various lengths and starting points, and then pastes these columns one by one into another file.
My question is how to best store the cell references that the script needs to be able to copy the correct columns? Currently there is a bunch of arrays storing the starting row number, starting column number, sheet number etc which are all indexed the same, and a loop function which does the actual copy paste work.
This (exceptionally bad?) solution would obviously be an absolute nightmare if the source file would change slightly at some point. So how should one do it better?
Excel is using enumerations for this purpose. Enums are the most efficient way to assign names to constants.
[Private] Enum WeeklyReport
FirstRow = 3
StartColumn = 1 ' Columns:
Text
Values
Totals = 17
Remarks
End Enum
The above declaration specifies the enum WeeklyReport with 5 values, one row and 4 columns. Text has the value of 2 because if the value is omitted the previous is incremented by 1. Therefore, Remarks = 18.
You can call up the values by their full name, like, WeeklyReport.Remarks or by their short name, like Remarks. That's why Excel gives unique names to its enumerations, like xlUp, which you might be using all the time without even knowing the enumeration's name. The names given above are possible but not very good.
Enumerations are a data type of their own which is interchangeable with Long. Declared before any code, at the top of a code sheet, they are available throughout that sheet if Private, otherwise throughout the project.
Related
How does decode_col, and encode_col, know which sheet to target if you never pass one in?
encode_col / decode_col converts between 0-indexed columns and column names.
If I give it a column name, like "foobar" and that column exists in different sheets, or wholly different files I'm processing how will these two functions know where that column is?
I have a relatively long subroutine that calls two external subs. In the sub, on one sheet=Start, VBA copies an account number and stores it under the variable name Acct (formatted as string). At the end of the sub, I would like VBA to activate another sheet=Stop, match Acct with a value in Column A (which is formatted as a number), then delete the entire row containing that number. I am just needing some assistance with finding and deleting the entire row containing that number.
It may also be worth mentioning that the numbers in column A are not in numerical order. Also, both sheets are located in the same workbook, we can call WB1.
Try Match function, which will return the row number as an integer variable. Then use Rows(row number).Delete
https://mysyntaxvba.wordpress.com/2016/08/16/what-is-the-syntax-to-return-the-row-of-a-found-value/
Could you please advise what would be the best way to create a union column for 12 separate columns (located in 12 different Excel sheets within a workbook) with or without VBA?
There are good manuals how to do it for two columns without VBA (using MATCH function) however I am not sure how to approach the case with multiple columns.
I think can be achieved with multiple consolidation ranges for a PivotTable. Would need labels for the columns and more than one column per sheet (could clone the existing ones). Should sort and remove duplicates from the list automatically (if cloned).
EDIT:
I'll assume your IDs are all numeric (otherwise, sorting would be very tricky if not impossible without VBA). You could modify the following array formula to meet your needs (select an area with enough rows to hold the full stack of IDs, enter the formula, then commit the formula with ctrl+shift+enter):
=SMALL(IFERROR(CHOOSE(COLUMN(INDIRECT("C1:C12",FALSE)),Sheet1!A1:A73,Sheet2!A1:A70,Sheet3!A1:A79,Sheet4!A1:A58,Sheet5!A1:A51,Sheet6!A1:A94,Sheet7!A1:A50,Sheet8!A1:A89,Sheet9!A1:A75,Sheet10!A1:A89,Sheet11!A1:A70,Sheet12!A1:A94),FALSE),ROW(INDIRECT("1:"&COUNT(Sheet1!A1:A73,Sheet2!A1:A70,Sheet3!A1:A79,Sheet4!A1:A58,Sheet5!A1:A51,Sheet6!A1:A94,Sheet7!A1:A50,Sheet8!A1:A89,Sheet9!A1:A75,Sheet10!A1:A89,Sheet11!A1:A70,Sheet12!A1:A94))))
I'll use a smaller version (2 columns) to explain how it works:
=SMALL(IFERROR(CHOOSE(COLUMN(A1:B1),A1:A73,C1:C70),FALSE),ROW(1:143))
First, COLUMN(A1:B1) returns a horizontal array of integers between 1 and 2. Passing this to the CHOOSE function with the two single-column ranges creates a single 73 x 2 array from both A1:A73 and C1:C70 (instead of creating a jagged array, the last three values of the second column will be filled in with #NA).
Wrap the result with IFERROR to convert the three #NA values to FALSE (otherwise, SMALL will return an error).
Next, ROW(1:143) returns a vertical array of integers between 1 and 143. Passing the 73 x 2 array and the array of integers between 1 and 143 to SMALL will return a single 143 x 1 array (vertical) of the sorted values (the three FALSE values are ignored).
Note on INDIRECT: Using INDIRECT in this way makes the formula stable even if rows/columns are deleted; however, it also makes the formula volatile, which will cause it to be recalculated every time there is a change in the workbook, which could slow things down considerably. Another option is INDEX (e.g., ROW(A1:INDEX(A:A,COUNT(...))), which can be affected by row/column deletions, but isn't volatile.
if you don't mind a bit of manual effort, this works for numeric and non numeric IDs:
Stack columns on top of each other manually using Ctrl-C + Ctrl-V
Go to Data tab --> Filter --> Advanced Filter --> tick unique records only --> choose your copy to location
This simple two step process would then give you unique union of two columns. Obviously the higher the number of columns, the more the utility of a VBA approach.
I have a very large array of data with many columns that display different outputs for the values presented. I would like to add a row above the data that will display the most common occurring value or word below.
Generally I would like to have each top of the column (right under the column label in row 1) have the most common value below. I will then use this value for various data analysis functions!
Is this possible, and if so, how? Preferably this will not require VBA, but simply a short code in the cell.
One caveat: The exact values may vary, so there is no set list where I can say "it will be one of these."
Any ideas appreciated!
Try a series of =COUNTIF(A:A,"VALUE TO SEARCH") functions if you want to stay away from VBA.
Otherwise, the best method would be to iterate through each column via VBA. With this method, you can even count the "varying" values and return the count and/or the value itself.
http://www.excel-easy.com/examples/most-frequently-occurring-word.html
This is a single formula you would write at the top of each column. Does not require VBA. You can replace the set range to an entire column, such as (A:A) instead of (A1:A7).
If you mean an array as in a data type, it could work differently but it depends what you're trying to do.
With data from A3 through A16, in A2 enter:
=INDEX($A$3:$A$16,MODE(MATCH($A$3:$A$16,$A$3:$A$16,0)))
This will work for text as well as numbers. Adjust this to match the column size.
I have a Worksheet with 10 columns and data range from A1:J55. Col A has the invoice # and rest of the columns have other demographic data. Goal is to type the invoice number on a cell and display all the rows matching the invoice number from col A.
Besides auto filter function, the only thing comes to my mind is VBA. Please advice what is the best way to get the data. Thanks for your help in advance.
Alright, I'm pretty proud of this one. Again avoiding VBA, this one uses the volatile formula OFFSET to keep moving its VLOOKUP search down the table until it's found all matches. Just make sure you paste enough rows of the formula that if there are many matches, there's room for all of them to appear. If you put a border around your match area then it would be clear if you ever ran out of room and needed to copy down the formula some more.
Again, in the main section, it's just a single formula (using index):
=IFERROR(INDEX($A$1:$J$200,$M3,MATCH(N$2,$A$1:$J$1,0)),"")
This gets to be so simple because the hard work of the lookup is done by an initial column which looks up the next row that matches the invoice number. It has the formula:
=IFERROR(MATCH($L$2,OFFSET($A$1:$A$200,M2,0),0)+M2," ")
Here is the working example that goes with those formulas:
Let me know if you need any further description of how it works, but it mostly uses the same rules as above so that it's robust in copying and moving around.
I've uploaded the Excel file so you can play with it, but everything you need to reproduce this feature should be in this solution.
Google Docs - Click link and hit Ctrl+S to download and open in Excel.
A popular solution to this problem is a simple VLookup. Lookup the invoice the user types in on the table A1:J55, and then return an adjascent column's data.
Here's an example of it working:
The formula in the highlighted cell is:
=VLOOKUP($L3,$A:$J,MATCH(N$2,$1:$1,0),FALSE)
What's nice about this formula is you only need to type it once and then you can copy it across and it'll automatically pick out the correct column of the table (that's the match part). The rest is very simple:
The first part says lookup value $L3 (the invoice number typed in),
The second part says look it up in range $A:$J (which is where your table is located). I've shown how you can select the entire columns $A:$J so that you can add and remove data without worrying about adjustin the range in your lookups. (Excel takes care of optimizing the formula so that unused cells aren't checked)
The third part picks the column from which the resulting data will be drawn once a matching row is found.
The FALSE part is an indication that the invoice number must match exactly (no approximate matching allowed)
The $ signs ensure that fixed ranges like the location of your source table ($A:$J) and your lookup value ($L3) don't get automatically changed as you copy the formula across for multiple columns.
The formula is pretty easy to adapt if you want to move around your table and the area where you do your lookup. Here's an example:
Bonus
If you want to add a little spiff, you can add a dropdown to the Invoice # field so that the user gets auto-completion and the option to browse existing values like so: