How to adjust VBA for formula use? - excel

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

Related

Ignoring specific rows with VBA Excel

I have a table in Excel like such, where the number of rows will vary each day:
Column A
Column B
Column C
Cell 1
Cell 2
Show
Cell 3
Cell 4
Show
Cell 5
Cell 6
Ignore
I am using vba to convert the range to a html table, and then email it.
I have a helper column (Column C), and I want to use a formula there to filter out certain rows.
However, that filter is not excluding hidden cells from being displayed in the html table.
I currently use this
Dim LastRow As Long LastRow = rInput.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
to find the last row of my table. This works great in projects where you want all of the table included.
I tried to change it to Find("Ignore", which gets me Object variable or With block variable not set
I tried including 'SpecialCells(xlCellTypeVisible)' in my
ConvertRangeToHTMLTable(Sheet2.Range("$A:$J").Rows("5:" & LastRow), 5)
and using a filter to hide the 'Ignore' cells. But that did not stop them showing in the emailed html table.
You probably have some sort of loop which goes over the rows right? It will not automatically skip the hidden rows just because they are filtered out, you need to specifically tell it to skip them. You can do something like:
For Each r In myRange.Rows
If Not r.EntireRow.Hidden Then
doSomething
End If
Next r
Ended up adjusting the table (and thus the range I cared about) to start at row 1 rather than row 5, and using
strBody = dsaEmailHeader & ConvertRangeToHTMLTable(Sheet2.Range("$A:$H").Rows("1:" & LastRow).SpecialCells(xlCellTypeVisible))
worked, where it didn't previously.

VBA paste range with dynamic selection

I read multiple Q&A's, however it's difficult for me to really understand all the long scripts with the usage of different objects.
I am trying to select a range from one worksheet to an other.
I want to keep the range dynamic, because the range can vary every time. By doing so I have used the following script:
Dim range As Long
For range = ActiveCell To ActiveCell.End(xlDown)
Sheets("Stock Report").range("A4" & range).Value =
Sheets("Unique File").range("Y8" & range).Value
However it doesn't do anything.
This script does work, but I would like to keep the last cell dynamic:
Sheets("Stock Report").Range("A4:A9000").Value =
Sheets("Unique File").Range("Y8:Y90004").Value
I have to do this for multiple columns which are calculated by using other files in a different worksheets and need to sort them finally without all the calculations in an other one.
Any suggestions?
OK try.
Sheets("Unique File").Range("Y8:Y" & Sheets("Unique File").Range("A4").End(xlDown).Row + 4).Copy
Sheets("Stock Report").Range("A4").PasteSpecial Paste:=xlPasteValues
Range("A4").End(xlDown).Row gets the last row of the data on the Stock Report sheet and is used to copy down to the same row on the Unique File sheet. I added 4 because your range starts on 8 on the Unique File sheet.
I think your code didn't work because you were trying to use the "range" you created from the Stock Report sheet on the Unique values sheet.

VBA: Macro to loop through rows and Autofill

EDIT: If I instead wanted to autofill these cells, would the following code work?
Sub BC_Edit()
' Define width and height of table
Dim datasetWidth, datasetHeight As Integer
' Find values for width and height of table
datasetWidth = Range("A1").End(xlToRight).Column
datasetHeight = Range("A1").End(xlDown).Row
' Loop over each column
For x = 1 To datasetWidth
Set sourceRange = Cells(2,x)
Set fillRange = Range(Cells(3, x), Cells(datasetHeight, x))
sourceRange.AutoFill Destination:=fillRange
Next
End Sub
I'm working with a couple of extremely large datasets - each approximately 3000 rows by 4000 columns. While Excel may not be the best tool for the job, I have already built a significant amount of infrastructure around the data and cannot move to a different framework. I'm using Excel 2007.
In a particular worksheet, when I try to Autofill using a formula I have inputted for the entire second column B (3000 x 1) via copy and paste of this column into the remaining 3000 by 3998 selection, or some part of this selection, Excel gives me a memory/resources error. So I would like to instead loop through each row and Autofill across all the columns. (In other words, I'd like to use A2 to autofill A3:A4000, B2 to autofill B3:B4000, and so on.) Perhaps this would help with the memory issue. How would I go about writing a macro to accomplish this?
I'd appreciate any advice on this issue, and perhaps some help with the appropriate VBA code, if possible.
Here is a pretty basic example of a macro to set columns below 2 to the formula of column 2.
It would be best to have this attached to a button or something similar rather than running every time you open the sheet.
Sub Button_Click()
' Define width and height of table
Dim datasetWidth, datasetHeight As Integer
' Find values for width and height of table
datasetWidth = Range("A1").End(xlToRight).Column
datasetHeight = Range("A1").End(xlDown).Row
' Loop over each column
For x = 1 To datasetWidth
' From row 3 to the height of data, set the formula of the cells to
' the formula contained in row 2 of that column
Range(Cells(3, x), Cells(datasetHeight, x)).Formula = Cells(2, x).Formula
Next
End Sub

Excel function to search a string for a multiple keywords

I have two tables. One of them has server names. The other has timestamps (first table, column A below) and text strings (first table, column B below). I want to search those strings for a keywords specified in the server table (second table below). If the match is found function writes to the cell name from the header of the column where the keyword is.
Example
I want to complete column System in Blue table. So for example C2 should show GreenSys and C8 - RedSys.
I have tried using SEARCH function but it looks like it tries to match whole table to the string if I pass it as an argument. VLOOKUP doesnt work too as I am using two tables. What's the best way for me to get this working?
If you change the way you have the data setup so that it is a bit more Excel-friendly, this can be rather easily accomplished.
The lookup sheet should look like this (the formula below has this as 'Sheet2'):
Then on your main data sheet, in cell C2 and copied down:
=IF(SUMPRODUCT(COUNTIF(B2,"*"&Sheet2!$A$2:$A$7&"*")),INDEX(Sheet2!B:B,SUMPRODUCT(COUNTIF(B2,"*"&Sheet2!$A$2:$A$7&"*")*ROW(Sheet2!$A$2:$A$7))),"")
The results look like this:
With the assumption that all Servers start with "Serv".. this should work without using vba.
=MID(B1,SEARCH("Serv",B1,1),IF(ISERROR(SEARCH(" ",B1,SEARCH("Serv",B1,1))),LEN(B1)-SEARCH("Serv",B1,1),SEARCH(" ",B1,SEARCH("Serv",B1,1))-SEARCH("Serv",B1,1)))
Essentially the formulas searches for the keyword serv and then attempts to parse until the end of the word to return the full name.
As someone else mentioned, it would be easier to do with vba but then again there is a benefit of not having macros.
Can you try this formula to cellC2?
=IF(SUMPRODUCT((B2=Sheet2!$A$2:$D$4)*COLUMN(Sheet2!$A$1:$D$1))>0,
INDEX(Sheet2!$A$1:$D$1,SUMPRODUCT((B2=Sheet2!$A$2:$D$4)*COLUMN(Sheet2!$A$1:$D$1)))
,"")
I have assumed that the second table is at Sheet2 and that data is upto column D, starting with the headers at A1, with the format you describe.
EDIT:
I can see you have amended the original post, and my answer no longer meets the specifications. Therefore I think it is best that I delete it.
EDIT2:
Added VBA solution. Assumptions:
Orignal data table in Sheet1
Destination table in Sheet2
Headers of Sheet1 in 1st row
The below code was tested, it should be OK but needs error handling:
Sub moveData()
Dim rngDestination As Range
Dim lRowCounter As Long, lColCounter As Long, lValueCounter As Long, lLastRow As Long
Dim vOriginArray As Variant, vValuesArray As Variant, vDestinationArray As Variant
' Database table in Sheet2
vOriginArray = Sheet2.UsedRange.Value
' Destination table in Sheet1
With Sheet1
lLastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
' Put the values we need to compare into an array
vValuesArray = .Range(.Cells(2, 2), .Cells(lLastRow, 2)).Value
Set rngDestination = .Range(.Cells(2, 3), .Cells(lLastRow, 3))
End With
' We will store the values to an array first and then
' back to the sheet, it is faster this way
ReDim vDestinationArray(1 To rngDestination.Rows.Count, 1 To 1)
' Loop through all rows and columns, exclude header row
For lRowCounter = 2 To UBound(vOriginArray, 1)
For lColCounter = LBound(vOriginArray, 2) To UBound(vOriginArray, 2)
' For each entry, find which values match and store them
For lValueCounter = 1 To UBound(vValuesArray, 1)
If InStr(1, vValuesArray(lValueCounter, 1), vOriginArray(lRowCounter, lColCounter), vbTextCompare) Then
vDestinationArray(lValueCounter, 1) = vOriginArray(1, lColCounter)
End If
Next lValueCounter
Next lColCounter
Next lRowCounter
' Put the data back to excel
With rngDestination
.ClearContents
.Value = vDestinationArray
End With
End Sub

Copying an array to a filtered range gives irrational results

Copying the values of a filtered range to an array seems to work without a problem: the array then contains values from both filtered and unfiltered cells. However, when I copy the array's contents back to the filtered range, the results are incomprehensible to me.
Here's my code:
Sub test()
Dim rangecopy() As Variant
rangecopy() = Range(Cells(2, 1), Cells(14, 3)).Value
For c = LBound(rangecopy, 1) To UBound(rangecopy, 1)
rangecopy(c, 1) = c
rangecopy(c, 2) = c * 10
rangecopy(c, 3) = c * 100
Next
Range(Cells(2, 1), Cells(14, 3)).Value = rangecopy()
End Sub
It is supposed to give the following result. Here, the range was unfiltered when the macro copied the array to it.
If the range is filtered by column D ("NO" is filtered out), the result looks like this:
First, the filtered cells aren't updated. Then, most cells from column B get values from the array's first column (4, 5, 6), while a few others get values from the array's second column correctly (10). The last two rows are filled with #N/A error. Is this supposed to work that way? I'm using Office 2010.
I really hope someone with knowledge of the internal workings of VBA can provide more insight on your question. I can share the following:
First, it is working as intended. However, I don't know why this is the design, nor what exactly is happening in the assignment process.
There are many cases that create a similar issue. For instance, if you have the filter on (some rows are hidden) and try to fill (drag) a formula down, you will see similar results, in that hidden rows aren't populated, but they do affect the (relative) references in the formula. On the other hand, if you manually copy and paste into a filtered range, the data is pasted into the hidden rows (as you intend).
It seems that any range referenced that is part of the Autofilter range is actually non-contiguous*. Using Range.Address does not always reveal this, nor does looping through Range.Areas. If we modify your example, we can see where the "real" error is:
Dim r1 as range
Dim r2 as range
Set r1 = Sheet1.Range("A1:B5") 'some numbers in a range
Set r2 = Sheet2.Range("A2:B6") 'same-size range underneath a filtered header
r1.Copy Destination:=r2
It works when all the rows are visible. When the filter on Sheet2 creates hidden rows, the result is "Run-time error '1004': ...the Copy area and the paste area are not the same size and shape." On the other hand, using the "manual" / clipboard method works for hidden rows:
r1.Copy
r2.PasteSpecial (xlPasteValues)
Since assigning an array to a range bypasses the clipboard (as in the 1st block), we ought to receive an error (instead you just end up with erroneous results).
The only solutions I'm aware of are to either loop through the range and assign a value to each cell:
For i = 1 to LastRow
For j = 1 to LastCol
Sheet1.Cells(i,j).Value = myArr(i,j)
Next
Next
OR (better) remove the Autofilter, assign the array to the range, then reapply the filter.
*technically it's contiguous, so it may be better to say that the range is composed of several ranges/areas, although using .Address doesn't indicate this and there is only one area when you try to loop through Range.Areas

Resources