Write String() array to excel excluding blank values - vb.net

Is there any functionality that would allow me to write a two dimensional string array to an Excel worksheet, but only "overwrite" the cells in Excel if the corresponding value isn't an empty string?
I have an application that does this...it pulls data from our database, puts it in a two dimensional array, and writes the whole array to an excel template. The columns written to are A through S, but in column Q there is a formula which is currently being overwritten, as it isn't pulled from the database.
I'd hate to hardcode the formula in the program, so I'm hopeful that there is a way to accomplish what I'm trying to do. The only idea I've had is to attempt to read the range from the worksheet first, but I'm curious to know if there is some kind of functionality that will handle this already.
EDIT: To be clear, this is a VB.Net program writing to Excel.

edit:written on the assumption this was VBA?
You could do it like this, which uses INDEX to extact the columns of the array after column Q.
Sub Sample()
Dim X
'create 2D array matching your size
X = Range("A1:S10").Value2
Range("A1:P10").Value2 = X
'add 2nd last column to R
Range("R1:R10").Value2 = Application.Index(X, 0, UBound(X, 2) - 1)
'add last column to S
Range("S1:S10").Value2 = Application.Index(X, 0, UBound(X, 2))
End Sub

Related

Excel VBA: Create a stacked column chart with named table

I'm trying to use VBA to create a stacked column graph using 3 columns of data out of a large ~30 column named table in excel. The desired outcome would be a stacked column graph, where the columns are based on the column "Program" (there are 5 distinct values across ~200 rows) and the numbers that make up the columns are the "SAVINGS - USE THIS" with the corresponding "Project Number"s as those chunks labels. Each row is a distinct project.
For instance, if I have 5 projects in "Program 1," I would want the "SAVINGS - USE THIS" values stacked on top of each other and when a run my mouse over the portions of the column the Project Number would show.
I am fairly new to VBA and am currently editing my previous code to make the project numbers and their savings into a pie graph (originally I didn't care about the program), so if there is a better way to do any of this please let me know.
Sub CreateChart()
Dim labelRng As Range
Dim dataRng As Range
Dim progRng As Range
Dim chtRng As Range
Dim cht As Object
Dim mySeries As Series
Dim vntValues As Variant
Dim i As Integer
Set chtRng = Union(Sheets("CMF").ListObjects("CMF").ListColumns("Program").Range, _
Sheets("CMF").ListObjects("CMF").ListColumns("Project Number").Range, _
Sheets("CMF").ListObjects("CMF").ListColumns("SAVINGS - USE THIS").Range)
'Set progRng = Sheets("CMF").ListObjects("CMF").ListColumns("Program").Range
'Set labelRng = Sheets("CMF").ListObjects("CMF").ListColumns("Project Number").Range
'Set dataRng = Sheets("CMF").ListObjects("CMF").ListColumns("SAVINGS - USE THIS").Range
'Set chtRng = Union(progRng, dataRng, labelRng) 'Sets range for pie
Set cht = Sheets("CMF").Shapes.AddChart2 'Creates chart
For j = cht.Chart.SeriesCollection.Count To 1 Step -1 'Had to be added to avoid errors
cht.Chart.SeriesCollection(j).Delete
Next j
cht.Chart.SetSourceData chtRng 'Sets data range for chart
cht.Chart.ChartType = xlColumnStacked
If this is the best way to do this, my current issue is defining the data range for the graph. You can see I am trying a couple different things, but the problem I'm having is that instead of pulling the "Program" column it is pulling the column that is next to it.
For reference, Project Number is in Column A, Program is in Column C and SAVINGS is in column X. It is pulling Columns A, B and X. Even if I specify the column number as "3" or pull them in a different order, I always have the same issue. The only way I don't have the issue is if I stop pulling in the Project Number and just pull in Program and Savings, which it gets right.
What am I doing that is causing it to pull back the wrong column of data, and once I get the right data in how can I make the stacked columns be organized by Program?
Thanks for reading all of that!
Since you are just assigning SetSourceData at the end, excel is going to do what it thinks is best...which isn't what you want in this case.
Try creating the different Series one at a time and assigning the ranges for each individually after you delete them. Just use the Macro Recorder to go over all the steps that you need to do to create the chart from scratch.

Excel macro - how to break wrapped text into rows for merged columns?

I have to import data from PDF to SAS, and one step involves converting the PDF data to excel spreadsheet before converting to text for simpler SAS import. Usually the PDF data converts fine into excel, with few errors. As I am trying to import older data, it is getting quite messy and some of the rows get wrapped in a single cell. I am trying to figure out if there is a macro possible which can help me fix this error in sheets without too much manual manipulation. I have never programmed in VBA before so I am quite new to excel macros.
Here is the example of messy data:
Here is the example of normal data:
(*Note the data values in the two images are different, just for example formatting)
I have tried working on a macro. For this, I copy the messy data into another sheet, and run the macro which outputs corrected data on a separate sheet, and then i copy the corrected data over the messy one in the original spreadsheet.
After trying to code the macro, I was unable to figure out how to tell excel to take the data in columns C,D,E,F which are all merged into one cell and break that wrapped text, and so on for other merged columns (as shown in messy data image).
Here is my current code that I got after watching some tutorials:
Sub Split_Text_to_Rows()
Dim splitVals1 As Variant
Dim splitVals2 As Variant
Dim totalVals As Long
Set sh1 = ThisWorkbook.Sheets(2)
Set sh2 = ThisWorkbook.Sheets(3)
sh2.Cells.Clear
lrow1 = sh1.Range("A65356").End(xlUp).Row
For j = 1 To lrow1
splitVals1 = Split(sh1.Cells(j, 1), Chr(10))
splitVals2 = Split(sh1.Cells(j, 2), Chr(10))
For i = LBound(splitVals1) To UBound(splitVals1)
lrow2 = sh2.Range("A65356").End(xlUp).Row
sh2.Cells(lrow2 + 1, 1) = splitVals1(i)
Next i
For k = LBound(splitVals2) To UBound(splitVals2)
lrow3 = sh2.Range("B65356").End(xlUp).Row
sh2.Cells(lrow3 + 1, 2) = splitVals2(k)
Next k
Next j
End Sub
As you can see, my code is also quite messy. Although, I got the code to work for columns A and B, when I get to column C - "Motor Vehicle Theft" and so on, I am not sure how to separate that wrapped text since they are merged in columns C,D,E,F. I would also like to keep the columns I to Q as two merged rows even after macro splits 1 row into 2 (shown in normal data image) and then continue splitting cells till column Z.
Any tips would be helpful! Please let me know if more information or clarification is needed.
I often find that the best approach is to first paste the data into Word, do some clean-up there, format it as a Word table, and then transfer it into Excel. The reason is that Word has very powerful find/replace features which allow you to quickly convert a mess into something sensible. Since you didn't provide example data I could paste in, I randomly found a pdf on the web to show one approach. The key in this case was noticing that each column begins with a space followed by a digit. So I did a search for " ^#" (a space followed by 'any digit') and replaced it by "^t" (tab character). Next, I used Word's 'Convert to Table' feature, and after that the data table is ready for pasting into Excel.

Random 8 digit Values generating with either VBA or Formula BUT copy and special paste over it to keep the value

i'm quite new to Excel VBA and formulas but i need to have a cell with random values on the form that i had created to create like a unique id (I know that there might be chances of getting duplicate numbers but it is slim) to send it to people to fill up a form but the thing is that if im using the RandBetween(00000000,99999999), hiding and unhiding just keep activating the formula which is hard for me to keep track if i were to send this template for others to fill up. So is there any way where when i have the formula and when the user opens the Excel form, it will randomize a 8 digit number and copy and special pasting the value overwriting it to freeze the value.
(Other ways are also gladly welcomed, thanks in advance guys)
I'm not sure what you're actually trying to do here, but if you wanted to generate a random number and store its value in a cell, you can use this code in 'ThisWorkbook' in VBA:
Option Explicit
Const MySheetName As String = "Sheet1" ' Change this to your sheet name
Private Sub Workbook_Open()
' Called every time you open the excel document
Dim myRandNumber As Long
Randomize
myRandNumber = CLng(Rnd() * 999999999) ' CLng converts the floating point number to a long integer
Dim rowNum As Integer
Dim colNum As Integer
rowNum = 3
colNum = 5
ThisWorkbook.Sheets(MySheetName).Cells(rowNum, colNum).Value = myRandNumber
End Sub
If you wanted to make sure this number was unique, which is probably what you really want to do, you can store these values in a separate hidden sheet and check these values each time you want to generate a new number.
You could also just timestamp the number using this line of code instead of the random number:
ThisWorkbook.Sheets(MySheetName).Cells(rowNum, colNum).Value = CDbl(Now() * 100000)
This will create a new unique number every 1 second, based on the system time.
If I understand correctly, you want to generate this number once. There are a couple ways I can think of to do this:
Generate the number only if the cell is blank
Set a flag in a hidden sheet
In both cases, if you wanted to generate the number again, you just need to clear the cell containing the number (option 1) or clear the flag in the sheet (option 2)
Option 1 can be done by replacing this part of the above code:
ThisWorkbook.Sheets(MySheetName).Cells(rowNum, colNum).Value = myRandNumber
with this:
With ThisWorkbook.Sheets(MySheetName).Cells(rowNum, colNum)
If (.Value = "") Then
.Value = myRandNumber
End If
End With
Hope this helps

Outputting rows into another sheet

I have two sets of data stored in two different sheets. I need to run an analysis which prints out the non-duplicate rows (i.e. row is present in one and not the other) found in the sheets and print them in a new sheet.
I can do the comparison fine - it is relatively simple with ranges and the For Next method. I currently store the non-duplicates in two different collections, each representing the non-duplicates in each sheet. However I am having trouble deciding how to proceed with pasting the duplicate rows on the new sheet.
I thought about storing the entire row into a collection but printing the row out of the collection in the new sheet seems non-trivial: I would have to determine the size of the collection, set the appropriate range and then iterate through the collection and print them out. I would also like to truncate this data which would add another layer of complexity.
The other method I thought was simply storing the row number and using Range.Select.Copy and PasteSpecial. The advantage of this is that I can truncate however much I wish, however this seems incredibly hacky to me (essentially using VBA to simulate user input) and I am not sure on performance hits.
What are the relative merits or is there a better way?
I have been tackling a similar problem at work this week. I have come up with two methods:
First you could simply iterate through each collection one row at a time, and copy the values to the new sheet:
Function PasteRows1(ByRef srcRows As Collection, ByRef dst As Worksheet)
Dim row As Range
Dim curRow As Integer
curRow = 1
For Each row In srcRows
dst.rows(curRow).Value = row.Value
curRow = curRow + 1
Next
End Function
This has the benefit of not using the Range.Copy method and so the user's clipboard is preserved. If you are not copying an entire row then you will have to create a range that starts at the first cell of the row and then resize it using Range.Resize. So the code inside the for loop would roughly be:
Dim firstCellInRow as Range
Set firstCellInRow = dst.Cells(curRow,1)
firstCellInRow.Resize(1,Row.columns.Count).Value = row.Value
curRow = curRow + 1
The second method I thought of uses the Range.Copy. Like so:
Function PasteRows2(ByRef srcRows As Collection, ByRef dst As Worksheet)
Dim row As Range
Dim disjointRange As Range
For Each row In srcRows
If disjointRange is Nothing Then
Set disjointRange = row
Else
Set disjointRange = Union(disjointRange, row)
End If
Next
disjointRange.Copy
dst.Paste
End Function
While this does use the .Copy method it also will allow you to copy all of the rows in one shot which is nice because you will avoid partial copies if excel ever crashes in the middle of your macro.
Let me know if either of these methods satisfy your needs :)

How to paste part of two dimensional array into worksheet range without using loop?

How to paste part of two dimensional array into worksheet range without using loop ?
Pasting whole array is straightforward :
Sub TestPasteArrayIntoRange()
Dim MyArray As Variant
MyArray = [{1,2;3,4;5,101}]
selection.Resize(UBound(MyArray, 1), UBound(MyArray, 2)) = MyArray
End Sub
But what about pasting only second row (first row we could past in by simple reducing selection size to one row) ? I've seen answer for case of one dimensional array which use Index Excel function, but it doesn't apply here.
My first idea was to copy array into range and then copy row from it, but it seems, that I cannot create range object which is not part of some worksheet object (expecially cannot create virtual range without virtual worksheet).
I think you will just have to create another function like e.g. GetRow() or GetColumn() to return a reduced vector (or array) with the select values you need. There are some functions here that you can use for your purpose: http://www.cpearson.com/excel/vbaarrays.htm