Sorting rows of the data into different spreadsheets based on row data Excel VBA - vba

Basically I want to copy a row into the spreadsheet that has the same name as one of the data entries in the row.
For m = 1 To TCD.Rows.count
If Not IsEmpty(TCD.Cells(m, Mix_Design_Colmn_Number).Value) Then
TCD.Rows(m).Copy _
Worksheets(CStr(TCD.Cells(m, Mix_Design_Colmn_Number).Value)).Rows((Worksheets(CStr(TCD.Cells(m, Mix_Design_Colmn_Number).Value)).Rows.count) + 1)
End If
Next m
However, the following line give me an object defined or application defined error:
TCD.Rows(m).Copy _
Worksheets(CStr(TCD.Cells(m, Mix_Design_Colmn_Number).Value)).Rows((Worksheets(CStr(TCD.Cells(m, Mix_Design_Colmn_Number).Value)).Rows.count) + 1)
If anyone knows of a better way of doing this or can figure out the error that would be great!

Problem is with .rows.count
Look this:
debug.Print Worksheets("Sheet1").Rows.Count
Result:
1048576
if you add 1 and we got rows(1048576+1) vba a little crazy
Try different find the last row. Maybe you will find something there -> http://www.thespreadsheetguru.com/blog/2014/7/7/5-different-ways-to-find-the-last-row-or-last-column-using-vba

Related

How to output an excel array without knowing how many items it has

Scenario: I have a code that connects to an API (in this case Factset) and retrieves some data. The data output of the formula is an array of dates, which can have any number of dates inside.
Issue: Currently, my code puts the formula into the sheets and updates. The problem is, for an excel array to work (as far as I understand), I have to select as many cells as output items and press "ctrl+shift+enter". The problem is, for each item in my list, I don't know precisely how many output values will be, so I cannot pre-select that range, therefore my output just get the first data row of the array.
For example, the output should be:
15.01.2018
15.02.2018
15.03.2018
15.04.2018
15.05.2018
but since I don't know there will be 5 dates for that item, my current output is only:
15.01.2018
Question: Is there a possible way to do this, without having the final number of data rows that will be in the output? If so, how could I do it?
Code so far:
For i = 2 To numberofitems + 1
If wb.Worksheets("Dates").Cells(1, i).Value <> "" Then
wb.Worksheets("Call Prices").Cells(2, i).Formula = "=FDS(" & Cells(1, i).Address(0, 0) & ",""FI_(CALL,,DATE,,DATE)"")"
Application.Goto ActiveWorkbook.Sheets("Dates").Cells(2, i)
Application.SendKeys("^+{ENTER}")
End If
Next i
Obs: I also read some other posts with the .FormulaArray property, but could not understand how to use it without preselecting the range.
Obs2: The data is retrieved form the API (factset), so it is neither on the sheet nor in a VBA array. Since the data will come from the API, I don't know beforehand how many items it will have.
You can pull the dates into an array and then loop through each of the items in the array.
Sub datesArray()
vDates As Variant
vDates = Sheets("Sheets1").Range("A1:A5")
For i = 1 To UBound(vDates)
'your code
Next i
End Sub

Comparing two lists in excel and extracting values missing from 2nd list - cannot be duplicated (also over two sheets)

Im working on a project report for work and I'm trying to find a way to compare two lists of project codes i.e "123456" and see whether the 2nd list is missing any new values that would've been entered into the first list. The lists are thousands of records long and so far people have been doing it manually (it hurts me knowing this) so I'm trying to make it automatic.
What I have tried is using an Array with a Index(Match(CountIF))) formula but I just cant seem to get it working.
My problem is that when I get the array to fill with what i want I then can't get it to not duplicate values (I need it to check the masterlist so it doesnt output something more than once into the output list).
I've also tried to give it a go with other formulas - but the lists can be thousands of records long so I cant do a cell for cell match as the list would be huge (that or my excel knowledge isnt good enough to know the easy solution).
Any help would be hugely appreciated.
Array might not be the best solution
I've checked quite a few other solutions but they don't quite deal with my issue and I don't have the skill to adapt them.
Here is one approach using VBA and arrays which is quicker than doing via the sheet. It checks each item in H to see it is present in J (and not the other way round). I assume that's what you want.
Sub x()
Dim v1, v2, v3(), i As Long, j As Long
v1 = Range("H2", Range("H" & Rows.Count).End(xlUp)).Value
v2 = Range("J2", Range("J" & Rows.Count).End(xlUp)).Value
ReDim v3(1 To UBound(v1, 1))
For i = LBound(v1) To UBound(v1)
If IsError(Application.Match(v1(i, 1), v2, 0)) Then
j = j + 1
v3(j) = v1(i, 1)
End If
Next i
Range("K2").Resize(j) = Application.Transpose(v3)
End Sub
Using an input box
Sub x()
Dim v1, v2, v3(), i As Long, j As Long
v1 = Application.InputBox("First list", Type:=8)
v2 = Application.InputBox("Second list", Type:=8)
ReDim v3(1 To UBound(v1, 1))
For i = LBound(v1) To UBound(v1)
If IsError(Application.Match(v1(i, 1), v2, 0)) Then
j = j + 1
v3(j) = v1(i, 1)
End If
Next i
Range("K2").Resize(j) = Application.Transpose(v3)
End Sub
A formula solution.
Note that I turned the first two ranges into Tables and changed the names. The formula is using structured references. This enables the formula to auto update if you add rows in the future.
=IFERROR(INDEX(ProjList1[#Data],AGGREGATE(15,6,1/ISNA(MATCH(ProjList1[#Data],ProjList2[#Data],0))*ROW(ProjList1[#Data]),ROWS($1:1))-ROW(ProjList1[#Headers])),"")
How does it work? Briefly:
MATCH generates an array of #NA! errors or a number.
ISNA turns that into an array of TRUE/FALSE where TRUE indicates an entry in table 1 that is NOT in table 2
Multiplying that array by the array of project list rows returns an array of error message vs row number
AGGREGATE small function ignores the error returns to give an ascending list of row numbers
INDEX then returns the appropriate entry from Table 1
ROW(ProjList1[#Headers]) is a correction so that the table may be located anyplace on the worksheet, and still return the correct row.
Not sure if you're trying to set this up so it will autoupdate in future, but as a stopgap:
Countif column next to list 1 that checks whether they appear in list 2...
... Feeding into a pivot that only shows those where the countif value is 0, in the "row" field to remove duplication?

Excel VBA: Copy the Data up to the last column that has value

The spreadsheet has multiple values in a cell.
What I'm trying to do is get that value found in a cell and paste it to another sheet and copy the other fields(columns) that belong to that value. How do I set the range in order copy the other fields(columns) up to the last column that has value? Thanks in advance.
For iRowGetProdCode = 0 To UBound(sSplitProdCode)
Sheets("Output").Cells(iRowCountOutput, 1).Value = sSplitProdCode(iRowGetProdCode)
iRowCountOutput = iRowCountOutput + 1
Next iRowGetProdCode
here is an idea how to discover an un-empty columns in the same row,
maybe you will find it useful and manipulate it for your needs:
Function LoopUntilLastColumn(ByVal Row As Integer)
Dim i As Integer
i = 1
Do While Cells(Row, i) <> ""
' do somthing
MsgBox (" I AM ALIVE COLUMN!")
i = i + 1
Loop
' you can also use the return value of the function.
LoopUntilLastColumn = i
End Function
I'm not exactly sure about what you're asking, but here are my three best guesses.
1.) Splitting delimited data from a single cell to columns
Without VBA: Use the "Text to Columns" function (Excel Ribbon:
Data|Data Tools).
With VBA: Use the split function MSDN (Related Post), then assign array values to target cells. Or parse your string manually with a loop.
2.) Finding the end of a continuous range
Without VBA: Use ctrl + arrow key
With VBA: Use the Range.End Property
3.) Looping through columns and rows
Used a nested loop:
For c = 1 to 5
For r = 1 to 20
Cells(r,c) = "Row = " & r & ", Column = " & C
Next
Next
Editing Suggestions (I don't have enough reputation to directly comment or edit)
This question as worded may be too specific for StackOverflow. Consider re-wording so that the problem can be understood in a general context and your question can be more useful to others.
Also, the wording is a little confusing. For example, use of the term "value" seems to change from referring to delimited data to referring to cell content in VBA. Likewise, it can be confusing to use "fields" or "columns" to describe the data if it's actually delimited text, so clarity on the data's state of existence would help.
It also seems to me that you've parsed the string on it's delimiter to an array, and that you're looping through this array to write the data in rows. I still can't see how exactly your question about setting a range fits in.

VBA Word table with unknown number of fused rows/columns

I'm currently trying to work with complex tables in Microsoft Word. My problem is, those tables have fused cells and rows, and I'm not sure of how many rows or columns i'll have.
Here is a (stupid) example how the kind of tables i'll have
I get my table thanks to a bookmark, and then proceed to stock the table in a Dim for easier access
Sub SetTable()
Dim tb as Table
Selection.GoTo What:=wdGoToBookmark, Name:="MyTable"
Selection.MoveDown
Set tb = Selection.Tables(1)
End Sub
Now, I'd use that table to write in several tables of a database.
Let's say, I have a table "Destinations", a table "Ways" and a table "Time"
I'm kinda blocked there.
With fused rows and columns, i cannot access a whole column or row. But as i don't know how many rows and columns i have (i could have, for example, 5 different ways for "Destination 1", or several distances in "Way 1")
I am a little lost on how i should try to work.
Cell(x,y).Row doesn't work because several rows are fused, and it is the same with Column, so we get errors extremely easily
I was thinking of putting tables in cells that might get an unknown number of rows/columns, a bit like this
The Problem with this method is that the person that'll write in the document won't be me. Meaning, if he has to create a table each time there is a new line/column that requires it, chance is that it'll become a problem quickly.
(I haven't found yet a method to put something in a given cell of a table at the creation of a new line, I'm also open on that point)
I was wondering if there are best practices to apply in this kind of case, and I am looking for advices too.
If you already had to treat something similar to this, how did you do?
Thanks in advance for your answers
Cordially,
Zawarudio
Note : The example of table here is insanely stupid, and even I don't even know what it's talking about. It was just to put informations in the tables, and have absolutely no link with what I'm trying to do.
If you were lost by the distances/times/whatever, sorry about that
I had some vacations so I didn't work on that question before now.
I just found a way that I felt was relevant, so I come here to share my answer
Note that I only worked on an unknown number of merged rows at the moment, so this answer will only be about that, though I believe it is the same. Also note that I'm on Word 2010. I don't know if rows/column behavior changed in 2013 or will change in the future. (well, obviously)
The big problem was that a merged row cell will only have a value of the first row of the merged row. Let's take a simple example
This table has 2 rows and 2 columns. We fused the rows of the 1st column.
table.Rows.Count will return 2, so will table.Columns.count.
table.cell(1,1).Range.text will return the content of the merged rows.
We would like table.cell(2,1).Range.text to return the value of the merged row, but VBA tells us here that this value doesn't exist.
There is no problem with table.cell(1,2).Range.text and table.cell(2,2).Range.text.
With values, that means that our table with merged rows is pretty equals to that
Where each empty cell would generate an error 5941.
How to resolve the problem?
Sub ReadAllRows()
Dim NbRows As Integer
Dim NbColumns As Integer
Dim i, j As Integer
Dim SplitStr() As String
Dim col1 as String
Dim col2 as String
Dim col3 as String
Dim col4 as String
'note : my table here is a public value that i get thanks to bookmarks
NbRows = table.Rows.count
NbColumns = table.Columns.count
For i = 3 To NbRows
'We put each value of each columns in a dim
'We do that to remember previously entered row value if the application encounters an error
'Because of merged rows, some cells on each row will not exist and return an error
'When the application encounters an error, it just proceeds to next column
'As previous existing value of this column was stocked in a Dim, we can get the full row at the end of the column loop
For j = 1 To NbColumns
On Error GoTo ErrorHandler
SplitStr = Split(table.Cell(i, j).Range.Text, Chr(13))
Select Case j
Case 1:
col1 = SplitStr(0)
Case 2:
col2 = SplitStr(0)
Case 3:
col3 = SplitStr(0)
Case 4:
col4 = SplitStr(0)
'ect...
End Select
NextRow:
Next j
'We have here all the values of the line
MsgBox "col1: " & col1 & Chr(10) & _
"col2: " & col2 & Chr(10) & _
"col3: " & col3 & Chr(10) & _
"col4: " & col4 & Chr(10)
Next i
'This Error handler will skip the whole Select Case and thus will proceed towards next cell
ErrorHandler:
If Err.Number = 5941 Then
Err.Clear
Resume NextRow
End If
End Sub
That way, when a cell doesn't exist, that mean the row if merged. Meaning we want the last known value of the row. Since we skip the whole select when row is unknown, the value of the Dim isn't changed while we do get right the value of not merged rows.
This isn't rocket science, but I first began with a simple On Error Resume Next, and with that, non-existing rows simply had the value of last existing row, so I also had to work on a function that would try to get the good value for each cell of each row...
Note that I did things the ugly way here, but you can use a one dimensionnal arrays to stock an entire row the way Word is supposed to understand it, or you can even get a two dimensionnal array stocking your whole table in it a way Word understands
Well, I hope it helps someone, someday!
Cordially,
Zawarudio
I think there must be an existing Q/A about this but I didn't find it using a quick search, so for now...
One thing you can do is iterate through the cells of the range of the table. Like this:
Sub iterTable()
Dim r As Range
Set r = ActiveDocument.Tables(1).Range
For i = 1 To r.Cells.Count
Debug.Print r.Cells(i).RowIndex, r.Cells(i).ColumnIndex, r.Cells(i).Range.Text
Next
End Sub
As long as you have predefined texts that will allow you to detect your "Destination" groups, that should be enough for you to make progress...

VBA - draw in numbers from one sheet and get data from another

I am trying to write a VBA to draw data from one sheet to another, but am stuck on something.
I only need some of the data in the original sheet (let's call it s1), in particular, I need data between two rows.
I have these rows written down in another sheet (s2), so I know exactly between which rows I need the data from. As you may expect there are multiple rows between which I need the data.
The problem is now that I am trying to write a VBA that is able to look up these rows in my row sheet (s2), and then goes to the sheet in which all my original data is contained (s1), and then draws out the data between the two rows into a third sheet (s3).
I have not been able to make it draw in the numbers from s2 (can't seem to work out how to tell it that it is these two rows between which I need the data, but from another sheet), and currently have to input the row numbers myself, which is really tedious, since the dataset is large!
Any help would be much appreciated!
Thank you!
Have you tried the .find option.
You could use something like this:
findrow = w1.Cells.Find(what:="Content", MatchCase:=False).Row
An more extensive example would help
Just break down your problem into smaller steps.
You can assign a value on a sheet using cell references:
Sheets("S1").Cells(iRow, iCol) = Value
'or
Sheets("S3").Cells(2, 10) = Sheets("S2").Cells(2, 10)
You can use Sheets("S1").Range("J" & iRow) or Range("J2").
If there are conditions that need to be met for something to happen, use If statements.
If (Value 1 < Value 2) Then
Sheets("S3").Cells(2, 10) = Sheets("S2").Cells(2, 10)
Else
Sheets("S3").Cells(2, 10) = Sheets("S1").Cells(2, 10)
End If
Without more information about the specific choices you are using to determine which rows to copy and from which sheet, it's hard to say. But you can do things in smaller batches or copy whole ranges, by looping.
Dim iRow As Integer
'This would copy all the data from Sheet("S1").Range("J1:J20) to Sheet "S3".
For iRow = 1 to 20
Sheets("S3").Cells(iRow, 10) = Sheets("S1").Cells(iRow, 10)
Next iRow
You could insert an If statement into each row, inside the loop to see if a certain criteria is met, and then decide which sheet in which to copy the data.