Apps Script to filter column in table based on values from list - vba

I'm looking for some help with a script to filter a specific column containing values from a list on a separate tab.
Basically I have 2 sheets in my workbook:
Sheet 1: contains a dynamic list of values that users need to copy/paste from a different source
Sheet 2: contains a table of data, with column E (5) that needs to be filtered for all values contained in the list on sheet 1. This means exact matches and partial matches.
E.g. User pastes the below list of countries on Sheet 1:
Country
Belgium
Netherlands
France
Column E(5) is filtered for exact and partial matches on Sheet 2:
| Country |
| -------- |
| Belgium |
| Belgium Luxembourg|
| Netherlands |
| France Spain |
| Italy France |
Any ideas how I can get this to work?
Thanks
I have a VBA script that works (see below), but converting it to Apps Script seems impossible as it uses the .exists construct which is not supported by Apps Script.
Sub AutoFltr()
Dim dIn As Object, dOut As Object
Dim a As Variant, b As Variant
Dim i As Long, j As Long
Dim bFound As Boolean
Set dIn = CreateObject("Scripting.Dictionary")
dIn.CompareMode = 1
Set dOut = CreateObject("Scripting.Dictionary")
dOut.CompareMode = 1
With Sheets("Start Here >>")
a = .Range("B17", .Range("B" & Rows.count).End(xlUp)).Value
End With
With Sheets("FA Items")
b = .Range("E2", .Range("E" & Rows.count).End(xlUp)).Value
For i = 1 To UBound(b)
If Not dIn.exists(b(i, 1)) Then
If Not dOut.exists(b(i, 1)) Then
j = 0
bFound = False
Do
j = j + 1
If InStr(1, b(i, 1), a(j, 1), vbTextCompare) > 0 Then
dIn(b(i, 1)) = 1
bFound = True
End If
Loop Until bFound Or j = UBound(a)
If Not bFound Then dOut(b(i, 1)) = 1
End If
End If
Next i
If dIn.count > 0 Then
.Range("A1").CurrentRegion.Autofilter Field:=5, Criteria1:=dIn.Keys, Operator:=xlFilterValues
End If
End With
End Sub

Related

Macro - list rows number of the cells which contains value "Code: "

I have a sheet in which Column "A" contains values. I want to collect the row numbers in a new sheet where string "Code: " is found.
I want to write VBA code to achieve this.
For example :
Sheet1 :
RowNo | Column A
------|---------
1 | Hello World
2 | Good morning
3 | Code: 46904A65
4 | Excuse
5 | Code: 4523S45
Output Sheet :
RowNo | Column A
------|---------
1 | 3
2 | 5
Explanation : String "Code: " found in 3rd and 4th row of sheet 1. So output sheet contains those row numbers in it.
The following code is not working :
Set stTempData = Sheets("Output Sheet")
stTempData.Select
Set mainsheet = Sheets("Sheet1")
mainsheet.Select
k = Range("a65536").End(xlUp).Row
i = 1
Do While i < k
Set r = stTempData.Range("a65536").End(xlUp).Offset(1, 0)
If InStr(ActiveSheet.Cells(i, 0).Value, "Code:") > 0 Then
r.Offset(i, 0).Value = ActiveCell.Row - 1
j = i
r.Offset(j + 1, 1).Value = ActiveCell.Row - 2
End If
i = i + 1
Loop
Put this in A1 in the output sheet:
=IFERROR(AGGREGATE(15,6,ROW(Sheet1!$A$1:$A$5)/(ISNUMBER(SEARCH("Code",Sheet1!$A$1:$A$5))),ROW(1:1)),"")
And copy down till you get blanks.
As to your code:
There is no column 0 So your Cells() needs to change to 1. Also you do not want to offset r. One more thing you had activesheet and activecell which was throwing it off.:
Set stTempData = Sheets("Output Sheet")
stTempData.Select
Set mainsheet = Sheets("Sheet1")
mainsheet.Select
k = Range("a65536").End(xlUp).Row
i = 1
Do While i <= k
Set r = stTempData.Range("a65536").End(xlUp).Offset(1, 0)
If InStr(mainsheet.Cells(i, 1).Value, "Code:") > 0 Then
r.Value = mainsheet.Cells(i, 1).Row
End If
i = i + 1
Loop

VBA, Advanced Filter & Remove Duplicates

I have a list full of different paths in col A.
I have a list of details in B and C.
How can I on a new sheet: 1) pull each unique path, 2) for each path compile the values from B * C and remove the duplicates. 3) repeat the next path after these are done in the latest row.
I do have a faulty macro, but for the sake of being concise and accurate I will not post. Unless someone wants to read it, please reques
Any help would be greatly appreciated.
Here is what I have(I understand its long, i'll take try to clean it up abit) :
Sub FileDetail()
'Does not fill down, go to bottom to unleased fill down
'Skips unreadable files
'This Macro retrieves data from files picked. The data is based on header. Data is also filtered for unique values.
'You must make sure headers are in the first row and delimted.
Dim wb As Workbook, fileNames As Object, errCheck As Boolean
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
Dim lngLastNode As Long, lngLastScen As Long
Dim intColinstrument As Integer, lngLastinstrument As Long
'Skipped worksheet for file names
Dim wksSkipped As Worksheet
Set wksSkipped = ThisWorkbook.Worksheets("Skipped")
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
.Range("A1:E1").Value = Array("File Name", "Sheet Name", "Node", "Book", "Instrument")
End With
'get user input for files to search
Set fileNames = CreateObject("Scripting.Dictionary")
errCheck = UserInput.FileDialogDictionary(fileNames)
If errCheck Then
Exit Sub
End If
'''
For Each Key In fileNames 'loop through the dictionary
On Error Resume Next
Set wb = Workbooks.Open(fileNames(Key))
If Err.Number <> 0 Then
Set wb = Nothing ' or set a boolean error flag
End If
On Error GoTo 0 ' or your custom error handler
If wb Is Nothing Then
wksSkipped.Cells(wksSkipped.Cells(wksSkipped.Rows.Count, "A").End(xlUp).Row + 1, 1) = fileNames(Key)
Else
Debug.Print "Successfully loaded " & fileNames(Key)
wb.Application.Visible = False 'make it not visible
' more working with wb
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
''''''''''''''''''testing additional column..trouble here
' Find the Anchor Date
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("instrument.instrumentType", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColScenario), .Cells(lr, intColScenario)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
''''''''''''''''''''''''''''''''''''below is working'''''''''''''''''''''''
' Find the Desk column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("book.reportingLine.pathName", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -1).Value = ws.Name
y.Offset(0, -2).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
End If
' Find the Intrument
intColinstrument = 0
On Error Resume Next
intColinstrument = WorksheetFunction.Match("instrument.instrumentType", .Rows(1), 0)
On Error GoTo 0
If intColinstrument > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColinstrument)) > 1 Then
lr = .Cells(.Rows.Count, intColinstrument).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColinstrument), .Cells(lr, intColinstrument)).AdvancedFilter xlFilterCopy, , z, True
If Not boolWritten Then
z.Offset(0, -3).Value = ws.Name
z.Offset(0, -4).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
z.Delete Shift:=xlUp
End If
End If
' Identify the next row, based on the most rows used in columns C & D
lngLastNode = wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row
lngLastScen = wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row
lngLastinstrument = wksSummary.Cells(wksSummary.Rows.Count, 5).End(xlUp).Row
lngNextRow = WorksheetFunction.Max(lngLastNode, lngLastScen) + 1
If (lngNextRow - lngStartRow) > 1 Then
' Fill down the workbook and sheet names
z.Resize(lngNextRow - lngStartRow, 2).FillDown
''''''''Optional if you want headers to be filled down.
'If (lngNextRow - lngLastNode) > 1 Then
' Fill down the last Node value
'wksSummary.Range(wksSummary.Cells(lngLastNode, 3), wksSummary.Cells(lngNextRow - 1, 3)).FillDown
'End If
'If (lngNextRow - lngLastScen) > 1 Then
' Fill down the last Scenario value
'wksSummary.Range(wksSummary.Cells(lngLastScen, 4), wksSummary.Cells(lngNextRow - 1, 4)).FillDown
'End If
End If
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
End If
End With
Next ws
wb.Close savechanges:=False 'close the workbook do not save
Set wb = Nothing 'release the object
End If
Next 'End of the fileNames loop
Set fileNames = Nothing
' Autofit column widths of the report
wksSummary.Range("A1:E1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
.Visible = True
End With
End Sub
So this code gets file name, sheet name, and columns I specify's data.
1) However I am having trouble adding additional columns to this. (I currently get 2 extracted columns), and also
2) I am having trouble putting it in a format where it columns are based upon each other. ex It will give me unique value for each path, but then not the unique values per sport.
Edit to include data ( I also would like to include a 4th and 5th column but kept it to 3 for simplicity):
+-------------------------------+------------+--------------+
| path | sport | Teams |
+-------------------------------+------------+--------------+
| stack/over/flow/larrybird | basketball | celtics |
+-------------------------------+------------+--------------+
| stack/over/flow/michaeljordan | basketball | bulls |
+-------------------------------+------------+--------------+
| stack/over/flow/tigerwoods | golf | pga |
+-------------------------------+------------+--------------+
| stack/over/flow/josebautista | baseball | bluejays |
+-------------------------------+------------+--------------+
| stack/over/flow/jordanspeith | golf | pga |
+-------------------------------+------------+--------------+
| stack/over/flow/kevinlove | basketball | timberwolves |
+-------------------------------+------------+--------------+
| stack/over/flow/lebronjames | basketball | cavs |
+-------------------------------+------------+--------------+
| stack/over/flow/stephencurry | basketball | warriors |
+-------------------------------+------------+--------------+
| stack/over/flow/larrybird | baseball | redsox |
+-------------------------------+------------+--------------+
| stack/over/flow/michaeljordan | baseball | whitesox |
+-------------------------------+------------+--------------+
| stack/over/flow/michaeljordan | chess | knight |
+-------------------------------+------------+--------------+
| stack/over/flow/michaeljordan | basketball | hornets |
+-------------------------------+------------+--------------+
| stack/over/flow/kevinlove | basketball | cavs |
+-------------------------------+------------+--------------+
| stack/over/flow/tigerwoods | golf | pga |
+-------------------------------+------------+--------------+
And expected result (I included fill down in this)
+-------------------------------+------------+--------------+
| path | sport | teams |
+-------------------------------+------------+--------------+
| stack/over/flow/larrybird | basketball | celtics |
+-------------------------------+------------+--------------+
| | baseball | red sox |
+-------------------------------+------------+--------------+
| stack/over/flow/tigerwoods | golf | pga |
+-------------------------------+------------+--------------+
| stack/over/flow/michaeljordan | basketball | bulls |
+-------------------------------+------------+--------------+
| | | hornets |
+-------------------------------+------------+--------------+
| | baseball | whitesox |
+-------------------------------+------------+--------------+
| | chess | knight |
+-------------------------------+------------+--------------+
| stack/over/flow/kevinlove | basketball | timberwolves |
+-------------------------------+------------+--------------+
| | | cavs |
+-------------------------------+------------+--------------+
| stack/over/flow/josebautista | baseball | bluejays |
+-------------------------------+------------+--------------+
It seems to be an issue for the 3rd (4th and 5th also) columns with getting unique values.
The easy way would be, to copy the whole range, sort it and then run some calculations:
Sub Macro1()
Application.ScreenUpdating = False
Dim str As String
With Sheet1
str = .Range(.Cells(.Rows.Count, 1).End(xlUp), .Cells(1, 3)).Address
.Range(str).Copy Sheet2.Cells(1, 1)
End With
Application.CutCopyMode = False
With Sheet2
.Activate
Dim str2 As String
str2 = .Range(str).Offset(1).Resize(.Range(str).Rows.Count - 1).Address
.Range(str2).Value = Evaluate("if(" & str2 & "="""",-1E+99," & str2 & ")")
.Sort.SortFields.Clear
.Sort.SortFields.Add .Range(str).Offset(1).Resize(, 1), 0, 1, , 0
.Sort.SortFields.Add .Range(str).Offset(1, 1).Resize(, 1), 0, 1, , 0
.Sort.SortFields.Add .Range(str).Offset(1, 2).Resize(, 1), 0, 1, , 0
.Sort.SetRange .Range(str).Offset(1)
.Sort.Header = 2
.Sort.Apply
.Range(str2).Value = Evaluate("if(" & str2 & "=-1E+99,""""," & str2 & ")")
Dim val As Variant, i As Long, rng2 As Range
val = .Range(str).Value
Set rng2 = .Range(str).Offset(.Range(str).Rows.Count).Resize(1)
For i = 3 To UBound(val)
If val(i - 1, 1) = val(i, 1) And val(i - 1, 2) = val(i, 2) And val(i - 1, 3) = val(i, 3) Then Set rng2 = Union(rng2, .Range(str).Rows(i))
Next
i = .Range(str).Rows.Count - rng2.Rows.Count
rng2.EntireRow.Delete xlShiftUp
With .Range(str).Offset(1).Resize(i - 1, 1)
.Value = Evaluate("if(" & .Address & "=" & .Offset(-1).Address & ",""""," & .Address & ")")
With .Offset(, 1)
.Value = Evaluate("if((" & .Address & "=" & .Offset(-1).Address & ")*(" & .Offset(, -1).Address & "=""""),""""," & .Address & ")")
End With
End With
End With
End Sub
Done by phone, may contain errors!
Changed a lot now, please copy the whole code and test it again.
EDIT
Ok, a completely different solution. Should be fast, but may not be very clear in the way it works :P
Sub Macro2()
Dim inVal As Variant, outVal() As Variant, orderArr() As Variant
Dim startRng As Range
Dim i As Long, j As Long, k As Long, iCount As Long
Set startRng = Sheet1.Range("A2:C2") 'upmost row-range of the range to be copied (exclude headers!)
With startRng.Parent
inVal = .Range(startRng, .Cells(.Rows.Count, startRng.Column).End(xlUp)).Value
End With
ReDim orderArr(1 To UBound(inVal))
For i = 1 To UBound(inVal)
iCount = 1
For j = 1 To UBound(inVal)
For k = 1 To UBound(inVal, 2)
If StrComp(inVal(i, k), inVal(j, k), 1) = 1 Then iCount = iCount + 1
If StrComp(inVal(i, k), inVal(j, k), 1) <> 0 Then Exit For
Next
Next
orderArr(i) = iCount
Next
k = 1
ReDim outVal(1 To UBound(inVal, 2), 1 To UBound(inVal))
For i = 0 To Application.Max(orderArr)
If IsNumeric(Application.Match(i, orderArr, 0)) Then
iCount = Application.Match(i, orderArr, 0)
For j = 1 To UBound(inVal, 2)
outVal(j, k) = inVal(iCount, j)
Next
k = k + 1
End If
Next
ReDim Preserve outVal(1 To UBound(inVal, 2), 1 To k - 1)
For i = 1 To UBound(outVal)
For j = UBound(outVal, 2) To 2 Step -1
If outVal(i, j - 1) = outVal(i, j) Then
If i = 1 Then
outVal(i, j) = ""
ElseIf outVal(i - 1, j) = "" Then
outVal(i, j) = ""
End If
End If
Next
Next
'upper left cell of the output-range
Sheet2.Range("A2").Resize(UBound(outVal, 2), UBound(outVal)).Value = Application.Transpose(outVal)
End Sub
Feel free to set the starting range (Sheet1.Range("A2:C2")) to Selection and then simply select the range and start the macro. Does work with any size (while VERY big ranges may freeze excel for some time).
As always: if you have any questions, just ask :)
One efficient solution would be to:
Fisrt copy the values with Range.Copy
Then sort the rows with Range.Sort
Then remove the duplicated rows with Range.RemoveDuplicates
Finally remove the duplicated branches with a loop
This procedure removes the duplicated rows and format as a tree view:
Sub RemoveDuplicates()
Dim rgSource As Range, rgTarget As Range, data(), r&, c&
' define the source, the target and the number of columns
Const columnCount = 3
Set rgSource = Range("Sheet1!A3")
Set rgTarget = Range("Sheet1!F3")
' copy the values to the targeted range
Set rgSource = rgSource.Resize(rgSource.End(xlDown).Row - rgSource.Row + 1, columnCount)
Set rgTarget = rgTarget.Resize(rgSource.Rows.Count, columnCount)
rgSource.Copy rgTarget
' sort the rows on each column
For c = columnCount To 1 Step -1
rgTarget.Sort rgTarget.Columns(c)
Next
' build the array of columns for RemoveDuplicates
Dim rdColumns(0 To columnCount - 1)
For c = 1 To columnCount: rdColumns(c - 1) = c: Next
' remove the duplicated rows
rgTarget.RemoveDuplicates rdColumns
Set rgTarget = rgTarget.Resize(rgTarget.End(xlDown).Row - rgTarget.Row + 1, columnCount)
' format as a tree view by removing the duplicated branches
data = rgTarget.Value
For r = UBound(data) To 2 Step -1
For c = 1 To columnCount - 1
If data(r, c) <> data(r - 1, c) Then Exit For
data(r, c) = Empty
Next
Next
rgTarget.Value = data
End Sub
If you don't mind having the results sorted, instead of in the original order, the following code will do that. It should "auto-adapt" to any number of columns.
(If you need the results in the original order, I would use Collections or Dictionaries and User Defined Object approach)
Your data should start in A1 (with Row 1 being the column labels) and you can see where, in the code, you define the Worksheets for your Source and Results data.
Since most of the "work" is done within a VBA array, rather than on the worksheet, it should run quite rapidly.
Option Explicit
Sub SortFormat()
Dim wsSrc As Worksheet, wsRes As Worksheet, rRes As Range
Dim vRes As Variant
Dim R As Range, C As Range
Dim V As Variant
Dim I As Long, J As Long
'Set source and results worksheets, ranges
Set wsSrc = Worksheets("Sheet1")
Set wsRes = Worksheets("Sheet2")
wsRes.Cells.Clear
Set rRes = wsRes.Cells(1, 1)
Application.ScreenUpdating = False
'Copy source data to results worksheet
Dim LastRow As Long, LastCol As Long
With wsSrc
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set R = .Range(.Cells(1, 1), .Cells(LastRow, LastCol))
R.Copy rRes
Application.CutCopyMode = False
End With
'Go to Results sheet
With wsRes
.Select
.UsedRange.EntireColumn.AutoFit
End With
rRes.Select
'Sort the data
With wsRes.Sort.SortFields
.Clear
Set R = wsRes.UsedRange.Columns
For Each C In R
.Add Key:=C, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
Next C
End With
With wsRes.Sort
.SetRange R
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.Apply
End With
'Remove any completely duplicated rows
'Create array of columns
ReDim V(0 To R.Columns.Count - 1)
For I = 0 To UBound(V)
V(I) = I + 1
Next I
R.RemoveDuplicates Columns:=(V), Header:=xlYes
'Remove Duplicated items in each row
'Work in VBA array for more speed
vRes = R
For I = UBound(vRes, 1) To 3 Step -1
If vRes(I, 1) = vRes(I - 1, 1) Then vRes(I, 1) = ""
For J = 2 To UBound(vRes, 2)
If vRes(I, J) = vRes(I - 1, J) And _
vRes(I, J - 1) = "" Then vRes(I, J) = ""
Next J
Next I
R = vRes
Application.ScreenUpdating = True
End Sub
If you want to make a unique list of anything, use a Dictionary object.
Make sure to add a reference to the Scripting Runtime controls! Just some quick and dirty code (as in completely untested) based on your sample data:
Sub GetUniques()
Dim oDic as New Dictionary
Dim r as Integer
Dim strKey as String
Dim varValue(2) as Variant
'Get a unique list of Column A values
r = 3 'Your data starts on row 3
Do While Cells(r,1).value <> "" 'Run until the first blank line
strKey = Cells(r,1).value
varValue(0) = Cells(r,2).Value
varValue(1) = Cells(r,3).Value
If Not oDic.Exists(strKey) Then
oDic.Add strKey, varValue
End If
r = r +1
Loop
'Now display your list of unique values
Dim K as Variant
Dim myArray as Variant
r = 3 'We'll start on row 3 again but move over to column I (9)
For Each K in oDic.Keys
Cells(r,9).Value = K
myArray = oDic.Item(K)
Cells(r,10).Value = myArray(0)
Cells(r,11).Value = myArray(1)
r = r + 1
Next K
End Sub

i need to move a subset's subheader from one cell place its value adjacent to its respective row

The request sounded simple enough: "I need you to create some code that would create a column, move the property codes to the same row as the units...". I thought "Cool, I'll send the code via email - after I make this project my bitch...". That was two days ago...
Below is a snippet of the report and the finished output. Thanks in advance for your help. Needless to say, this project was humbling. Oh yeah, I noticed the last MultiFamily unit "112" s/b "112". I'll correct that.
Report before/after
The actual report is over 5K records but the format is the same. Below is the actual data:
Fig 1.
(A) | (B)
(01) Property | Tenant
(02) Unit | Code
(03) 118 - MultiFamily Facility 1 |
(04)        0118     | t0103001
(05)        0121     | t0077028
(06)        0124     | t0099589
(07)        Total 118 - MultiFamily Facility 1 |
(08) 119 - MultiFamily Facility 2 |
(09)        001     | t0103128
(10)        002     | t0101985
(11)        003     | t0102938
(12)        Total 119 - MultiFamily Facility 2 |
(13) 121 - MultiFamily Facility 3 |
(14)        001     | t0099507
(15)        002     | t0101773
(16)        003     | t0103123
(17)        004     | t0099821
(18)        005     | t0077281
(19)        Total 121- MultiFamily Facility 3 |
fig.2
(A) | (B) | (C)
(01) Property | Unit | Tenant Code
(02) 118    |    0118 |   t0103001
(03) 118    |    0121 |   t0077028
(04) 118    |    0124 |   t0099589
(05)    |     Total 118 - MultiFamily Facility 1
(06) 119    |    001 |   t0103128
(07) 119    |    002 |   t0101985
(08) 119    |    003 |   t0102938
(09)    |     Total 119 - MultiFamily Facility 2
(10) 121    |    001 |   t0099507
(11) 121    |    002 |   t0101773
(12) 121    |    003 |   t0103123
(13) 121    |    004 |   t0099821
(14) 121    |    005 |   t0077281
(15)    |     Total 121 - MultiFamily Facility 3
There are better ways to code the following but this will do what you require based on the information you supplied. It will not do the formatting. You can record a separate macro for that yourself, or just format manually.
If you are doing this operation multiple times, there are ways of automating last row, header row, and column numbers. I have essentially hard coded them, but you could also tweak this to work with selected range, but I was not that bored, nor are my skills that advanced.
Option Explicit
Sub MakeReport()
Dim HeaderRow, FirstRow, LastRow, sPropertyCol, sTenantCol, dPropertyCol, dUnitCol, dTenantCol, CounterX, CounterY As Long
Dim wsSource, wsDest As Worksheet
Dim PropertyNumber As String
'This chunk of code defines where the source information is and
'were destination information goes in terms of column and row numbers
HeaderRow = 2
FirstRow = 3
LastRow = 19
sPropertyCol = 1
sTenantCol = 2
dPropertyCol = 1
dUnitCol = 2
dTenantCol = 3
'This is the first row of Data on the destination sheet
CounterY = 2
'rename the sheets as required to suit your sheet names
Set wsSource = Worksheets("Sheet1")
Set wsDest = Worksheets("Sheet2")
'Taking care of the rearranged header inofrmation
wsDest.Range("A1") = wsSource.Range("A1")
wsDest.Range("B1") = wsSource.Range("A2")
wsDest.Range("C1") = wsSource.Range("B1") & " " & wsSource.Range("B2")
'Loop through data check if its a total row then
'Check if its a property row
'otherwise treat it as a unit row
'Does not eliminate blank lines, just repeats them
For CounterX = FirstRow To LastRow
If InStr(wsSource.Cells(CounterX, sPropertyCol).Value, "Total") = 0 Then
If InStr(wsSource.Cells(CounterX, sPropertyCol).Value, "-") <> 0 Then
PropertyNumber = Left(wsSource.Cells(CounterX, sPropertyCol).Value, InStr(wsSource.Cells(CounterX, sPropertyCol).Value, "-") - 2)
Else
wsDest.Cells(CounterY, dPropertyCol).Value = PropertyNumber
wsDest.Cells(CounterY, dUnitCol).Value = wsSource.Cells(CounterX, sPropertyCol).Value
wsDest.Cells(CounterY, dTenantCol).Value = wsSource.Cells(CounterX, sTenantCol).Value
'increase the row you are going to write to next
CounterY = CounterY + 1
End If
Else
wsDest.Cells(CounterY, dUnitCol).Value = wsSource.Cells(CounterX, sPropertyCol).Value
'increase the row you are going to write to next
CounterY = CounterY + 1
End If
Next CounterX
End Sub
Same answer, some different techniques ...
Option Explicit
Sub test()
Dim srcSht As Worksheet, tarSht As Worksheet
Dim srcRng As Range, tarRange As Range
Dim myCell As Range, myStr As String, ZeroStr As String
Dim myFacility As Long, nZeros As Long
Dim srcFirstRow As Long, srcLastRow As Long, tarLastRow As Long
Dim iLoop As Long, jLoop As Long, iCount As Long
' initialize
Set srcSht = Worksheets("Sheet1") '<~~ pick the sheet names you need
Set tarSht = Worksheets("Sheet2")
srcFirstRow = 3
srcLastRow = srcSht.Range("A" & srcSht.Rows.Count).End(xlUp).Row
Set srcRng = srcSht.Range(srcSht.Cells(1, 1), srcSht.Cells(srcLastRow, 3))
myFacility = -1
iCount = 1
' prepare the target sheet
tarLastRow = tarSht.Range("B" & tarSht.Rows.Count).End(xlUp).Row
tarSht.Range(tarSht.Cells(1, 1), tarSht.Cells(tarLastRow, 3)).Delete (xlUp)
tarSht.Range("A1").Value = "Property"
tarSht.Range("B1").Value = "Unit"
tarSht.Range("C1").Value = "Tenant Code"
' you may want to add some formatting of the target sheet at this point
For iLoop = srcFirstRow To srcLastRow
myStr = ""
If InStr(srcRng.Range("A" & iLoop).Value, "-") Then
' find the facility heading, the number goes in myFacility
myStr = Trim(Split(srcRng.Range("A" & iLoop), "-")(0))
myFacility = -1
On Error Resume Next
If Len(myStr) > 0 Then myFacility = CLng(myStr)
On Error GoTo 0
If myFacility = -1 Then
iCount = iCount + 1
tarSht.Cells(iCount, 2).Value = srcRng.Cells(iLoop, 1).Value
End If
Else
' put values in target sheet
iCount = iCount + 1
tarSht.Cells(iCount, 1).Value = myFacility
tarSht.Cells(iCount, 2).Value = srcRng.Range("A" & iLoop).Value
nZeros = Len(Trim(srcRng.Range("A" & iLoop).Value))
ZeroStr = ""
For jLoop = 1 To nZeros
ZeroStr = ZeroStr & "0"
Next jLoop
tarSht.Range("B" & iCount).NumberFormat = ZeroStr '<~~ set this as needed
tarSht.Cells(iCount, 3).Value = srcRng.Range("B" & iLoop).Value
End If
Next iLoop
End Sub

Creating a unique entry for each line item in Excel

I need help in creating a macro in Excel wherein it grabs a certain cell and copies the entire row x number of times depending on the cell's contents.
To make it clear, let's say I have 2 rows:
| Order # | Item | Qty |
| 30001 | bag | 3 |
| 30002 | pen | 1 |
What I want the macro to do is grab the number under the Qty column and copy the entire row and insert a new line with the exact same contents under it. The number of times it does this depends on the number in the Qty cell. Also, it appends a three digit number in the Order # cell to make it a unique reference point. What the end-result should be:
| Order # | Item | Qty |
| 30001-001 | bag | 1 |
| 30001-002 | bag | 1 |
| 30001-003 | bag | 1 |
| 30002-001 | pen | 1 |
It's hard to explain it here but I hope you get the point. Thanks in advance, gurus!
The following code supports blank lines in the middle of the data.
If Qty = 0, it won't write the Item in the output table.
Please insert at least 1 row of data, because it won't work if there is no data :)
Option Explicit
Sub caller()
' Header at Row 1:
' "A1" = Order
' "B1" = Item
' "C1" = Qty
'
' Input Data starts at Row 2, in "Sheet1"
'
' Output Data starts at Row 2, in "Sheet2"
'
' Sheets must be manually created prior to running this program
Call makeTheThing(2, "Sheet1", "Sheet2")
End Sub
Sub makeTheThing(lStartRow As Long, sSheetSource As String, sSheetDestination As String)
Dim c As Range
Dim rOrder As Range
Dim sOrder() As String
Dim sItem() As String
Dim vQty As Variant
Dim sResult() As String
Dim i As Long
' Reads
With ThisWorkbook.Sheets(sSheetSource)
Set rOrder = .Range(.Cells(lStartRow, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1)) ' It will work if there are blank lines in the middle!
i = rOrder.Rows.Count
ReDim sOrder(1 To i)
ReDim sItem(1 To i)
ReDim vQty(1 To i)
i = 1
For Each c In rOrder
sOrder(i) = Trim(c.Text)
sItem(i) = Trim(c.Offset(0, 1).Text)
vQty(i) = c.Offset(0, 2).Value
i = i + 1
Next c
End With
' Processes
sResult = processData(sOrder, sItem, vQty)
' Writes
ThisWorkbook.Sheets(sSheetDestination).Range("A" & lStartRow).Resize(UBound(sResult, 1), UBound(sResult, 2)).Value = sResult
End Sub
Function processData(sOrder() As String, sItem() As String, vQty As Variant) As String()
Dim i As Long
Dim j As Long
Dim k As Long
Dim sResult() As String
j = WorksheetFunction.Sum(vQty) ' That's why vQty had to be Variant!
ReDim sResult(0 To j, 1 To 3)
k = 0
For i = 1 To UBound(sOrder)
For j = 1 To vQty(i)
sResult(k, 1) = sOrder(i) & "-" & Format(j, "000")
sResult(k, 2) = sItem(i)
sResult(k, 3) = "1"
k = k + 1
Next j
Next i
processData = sResult
End Function
I hope it helps you. I had fun making it!
One way: Walk down the qty column inserting as needed then jumping to the next original row;
Sub unwind()
Dim rowCount As Long, cell As Range, order As String, i As Long, r As Long
Set cell = Range("C1")
rowCount = Range("C" & rows.Count).End(xlUp).Row
For i = 1 To rowCount
order = cell.Offset(0, -2).Value
For r = 0 To cell.Value - 1
If (r > 0) Then cell.Offset(r).EntireRow.Insert
cell.Offset(r, 0).Value = 1
cell.Offset(r, -1).Value = cell.Offset(0, -1).Value
cell.Offset(r, -2).Value = order & "-" & Format$(r + 1, "000")
Next
Set cell = cell.Offset(r, 0)
Next
End Sub

Excel lookup based on a condition

sheet1 sheet2 sheet3
---------
| |
V V * V-----
123 | A 123 | 456 C | |
* | B 123 | 789 D | |
| C 123 | 345 E | |
^ |
|-----------------
Can I look up 123 from sheet 1 to sheet 2 to return a letter (but that letter must appear in sheet 3 (C), look up the letter that is in sheet 3 and return 456? the problem is there are multiple 123's in sheet 2; I'm only used to dealing with unique numbers. Can it go A is not in sheet 3 so go to next letter until hits C. then lookup value to the left which is 456.
Thanks
Using VBA, inside a Module, write this new function:
Public Function LookFx(Sh1 As Range, Sh2 As Range, Sh3 As Range) As String
Dim BaseVal As String
Dim FoundV As Boolean
Dim SecVal As String
Application.Volatile
BaseVal = Sh1.Value
FoundV = False
For Each xx In Sh2
If xx.Value = BaseVal Then
SecVal = xx.Offset(0, -1).Value
For Each yy In Sh3
If yy.Value = SecVal Then
LookFx = yy.Offset(0, -1).Value
End If
Next
End If
Next
End Function
the value to be add in the function are:
Lets this is your data:
Sheet1:
Sheet2 :
Sheet 3:
The code below will loop through the values in sheet2 if a match is found it will loop through the values in sheet3. If a match is found it will be displayed, else it will c continue its loop in sheet.
Sub main()
Dim intValue As Integer
Dim i As Integer
Dim j As Integer
Dim strChar As String
intValue = Sheet1.Cells(1, 1)
For i = 1 To 3
If intValue = Sheet2.Cells(i, 2) Then
strChar = Sheet2.Cells(i, 1)
For j = 1 To 3
If strChar = Sheet3.Cells(j, 2) Then
MsgBox (Sheet3.Cells(j, 1))
Exit Sub
End If
Next j
End If
Next i
End Sub