How to get the address of selected multi rows?
When I press CTRL and multi select the specific rows, the result is will selected all the rows start from 1st specific row to last specific row, the rows I didn't select will be selected together if they are in 1st row to last row range.
Dim rng As Range: Set rng = Selection
MsgBox (rng.Address)
Picture added
Thank you.
On excel under developper step, u can save a macro.
Range("1:12,19:19,23:23,26:26").Select
Range("A26").Activate
t = Range("1:12,19:19,23:23,26:26").Address
t="$1:$12,$19:$19,$23:$23,$26:$26"
This is the result after select multi rows
$1:$12 => lines 1 to 12
$19:$19=> one other line selected
split t by , with a for each
in the loop split by :
...
Not 100% sure what you want to achive but I think the answer will be in here:
Option Explicit
Sub split_multi_address()
' note because the question asks about a selection on a sheet,
' fully qualified range names are not used
' as safe to assume we only want to operate on activesheet
Dim s As String
s = Selection.Address
Dim arrSplitStrings() As String
' array of range addresses as string
arrSplitStrings = Split(s, ",")
Dim v As Variant
For Each v In arrSplitStrings
Debug.Print v
Next v
' array of ranges
Dim arrSplitRanges() As Range
ReDim arrSplitRanges(UBound(arrSplitStrings) - LBound(arrSplitStrings))
Dim i As Long
i = 0
' create array of ranges
For Each v In Split(s, ",")
Set arrSplitRanges(i) = Range(v)
i = i + 1
Next v
' print array of ranges
For i = 0 To UBound(arrSplitRanges) - LBound(arrSplitRanges)
Debug.Print arrSplitRanges(i).Address
Next i
' array of ranges rows
' redim to remove previous values
ReDim arrSplitRanges(UBound(arrSplitStrings) - LBound(arrSplitStrings))
i = 0
' create array of range rows
For Each v In Split(s, ",")
Set arrSplitRanges(i) = Range(v).EntireRow
i = i + 1
Next v
' print array of ranges
For i = 0 To UBound(arrSplitRanges) - LBound(arrSplitRanges)
Debug.Print arrSplitRanges(i).Address
Next i
End Sub
Here is my Answer:
For DelRowAddr = 0 To i - 1
For RngProcAddr= 0 To j - 1 'The range only can delete the row
If ArrDelRow(DelRowAddr) = ArrProcRow(RngProcAddr) Then
DelTargetMatch = DelTargetMatch + 1
Else: Dismatch = Dismatch + 1
End If
If Dismatch = j Then 'Out of range
MsgBox DataFSC.Range("B5").Value & ArrDelRow(DelRowAddr), vbOKOnly + vbInformation, DataFSC.Range("A5") 'Error message in other sheet because some language can't show in vba
Exit Sub
End If
Next RngProcAddr
Dismatch = 0
Next DelRowAddr
Dim StrArrDelRow As String
StrArrDelRow = ""
If DelTargetMatch = i Then 'Maybe it is only the way to delete the row that I selected only
For DelRowAddr = 0 To i - 1
If DelRowAddr = i - 1 Then
StrArrDelRow = StrArrDelRow & Range(ArrDelRow(DelRowAddr)).Address
Else: StrArrDelRow = StrArrDelRow & Range(ArrDelRow(DelRowAddr)).Address & ","
End If
Next DelRowAddr
If Not StrArrDelRow = vbNullString Then
If Range(StrArrDelRow).Row = DeleteProcessOther.TopLeftCell.Row Then
MsgBox DataFSC.Range("B5").Value & ArrDelRow(DelRowAddr - 1), vbOKOnly + vbInformation, DataFSC.Range("A5")
Exit Sub
Else: Range(StrArrDelRow).EntireRow.Delete 'If I use this way to delete, it wont delete row5,7 as picture show
End If
Else: MsgBox DataFSC.Range("B5").Value & ArrDelRow(DelRowAddr - 1), vbOKOnly + vbInformation, DataFSC.Range("A5")
Exit Sub
End If
MsgBox ("¡Ì Deleted!") 'some special symbol or character can't show in vba
End If
Related
Alright, this is a very specific question. I have an excel macro written that takes a web URL, delimits it, transposes it, and then adds adjacent columns that describe the information in the originally transposed columns. Now, I need to add something to my macro that will loop through and check if the first character of one cell matches one of the first 4 characters of another cell. If it does, I need to concatenate strings from the descriptive columns to new cells. I'll illustrate this below:
3,435,201,0.5,%22type%25202%2520diabetes%22,0 Node type 2 diabetes
4,165,97,0.5,%22diet%22,0 Node diet
5,149,248,0.5,%22lack%2520of%2520exercise%22,2 Node lack of exercise
6,289,329,0.5,%22genetics%22,3 Node genetics
7,300,71,0.5,%22blood%2520pressure%2520%22,5 Node blood pressure
7,3,-7,1,0 Arrow +
4,3,-21,1,0 Arrow +
5,3,-22,1,0 Arrow +
6,3,-34,1,0 Arrow +
,7%5D Tail
I added color to make the concept of the problem more easily visualized. In row one of the first column, we see a red 3 that corresponds to 'type 2 diabetes'. In the fifth row of the first column, we see a blue 7 that corresponds to 'blood pressure'. These are both node objects, as the adjacent column signifies. In the sixth cell of the first column we see a blue 7 and a red 3. This indicates that an arrow (also signified by adjacent column) is connecting blood pressure to diabetes. In the next column over, we see an orange plus sign, which indicates this is a positive relationship.
The goal is to populate the next column over with "blood pressure + type diabetes", as I demonstrated in the image. So, I need some code to check the first characters in each node cell, and then compare them to the first 4 characters of each arrow cell. When an arrow that matches two of the nodes is found, I need the code to populate the row next to the + signs with a concatenated string comprised of the names of the nodes pertaining to that arrow, as well as the + sign between them (it's possible that it could also be a minus sign, but one isn't present in this example). Any pointers? I can't wrap my head around this. Edited to add Data
Here is the code of my current macro:
Sub Delimit_Transpose()
Cells.Replace What:="],[", Replacement:="#", LookAt:=xlPart, SearchOrder _
:=xlByRows, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False
ActiveCell.FormulaR1C1 = "=RIGHT(R[-1]C,LEN(R[-1]C)-36)"
Dim i As Long, strTxt As String
Dim startP As Range
Dim xRg As Range, yRg As Range
On Error Resume Next
Set xRg = Application.InputBox _
(Prompt:="Range Selection...", _
Title:="Delimit Transpose", Type:=8)
i = 1
Application.ScreenUpdating = False
For Each yRg In xRg
If i = 1 Then
strTxt = yRg.Text
i = 2
Else
strTxt = strTxt & "," & yRg.Text
End If
Next
Application.ScreenUpdating = True
Set startP = Application.InputBox _
(Prompt:="Paste Range...", _
Title:="Delimit Transpose", Type:=8)
ary = Split(strTxt, "#")
i = 1
Application.ScreenUpdating = False
For Each a In ary
startP(i, 1).Value = Replace(Replace(a, "[", ""), "]", "")
i = i + 1
Next a
i = 1
For Each a In ary
If Len(a) > 13 Then
startP.Offset(i - 1, 1).Value = "Node"
ElseIf Len(a) < 13 And Len(a) > 6 Then
startP.Offset(i - 1, 1).Value = "Arrow"
Else
startP.Offset(i - 1, 1).Value = "Tail"
End If
i = i + 1
Next a
Dim openPos As Integer
Dim closePos As Integer
Dim midBit As String
i = 1
n = 5
For Each a In ary
openPos = InStr(a, ",%22")
On Error Resume Next
closePos = InStr(a, "%22,")
On Error Resume Next
midBit = Mid(a, openPos + 1, closePos - openPos - 1)
On Error Resume Next
If openPos <> 0 And Len(midBit) > 0 Then
startP.Offset(i - 1, 2).Value = Replace(Replace(midBit, "%22", ""), "%2520", " ")
ElseIf Len(a) < 13 And InStr(a, "-") = 4 Then
startP.Offset(i - 1, 2).Value = "'-"
ElseIf Len(a) < 7 Then
startP.Offset(i - 1, 2).Value = " "
Else
startP.Offset(i - 1, 2).Value = "+"
End If
i = i + 1
n = n + 1
Next a
Application.ScreenUpdating = True
End Sub
This is my approach.
There's room for a lot of improvements, but is a rough code that should get you started.
Read the code's comments and adapt it to fit your needs.
EDIT: I updated the code to match the sample worksheet you uploaded, build the first column range dinamically, validate if commas appear in the first column cell so no error is raised.
As I said in the comments, it's better easier to debug if you call one procedure from the other, instead of merging them.
Code:
Option Explicit
Public Sub StoreConcatenate()
' Basic error handling
On Error GoTo CleanFail
' Define general parameters
Dim targetSheetName As String
targetSheetName = "Test space" ' Sheet holding the data
Dim firstColumnLetter As String
firstColumnLetter = "C" ' First column holding the numbers
Dim firstColumnStartRow As Long
firstColumnStartRow = 7
' With these three parameters we'll build the range address holding the first column dynamically
' Set reference to worksheet
Dim targetSheet As Worksheet
Set targetSheet = ThisWorkbook.Worksheets(targetSheetName)
' Find last row in column (Modify on what column)
Dim firstColumnlastRow As Long
firstColumnlastRow = targetSheet.Cells(targetSheet.Rows.Count, firstColumnLetter).End(xlUp).Row
' Build range of first column dinamically
Dim firstColumnRange As Range
Set firstColumnRange = targetSheet.Range(firstColumnLetter & firstColumnStartRow & ":" & firstColumnLetter & firstColumnlastRow)
' Loop through first column range cells
Dim valueCell As Range
For Each valueCell In firstColumnRange
' Check if cell contains "," in the second position in string
If InStr(valueCell.Value, ",") = 2 Then
' Store first digit of cell before ","
Dim firstDigit As Integer
firstDigit = Split(valueCell.Value, ",")(0)
' Check if cell contains "," in the fourth position in string
If InStr(3, valueCell.Value, ",") = 4 Then
' Store second digit of cell after ","
Dim secondDigit As Integer
secondDigit = Split(valueCell.Value, ",")(1)
End If
' Store second colum type
Dim secondColumnType As String
secondColumnType = valueCell.Offset(, 1).Value
' Store third column value
Dim thirdColumnValue As String
thirdColumnValue = valueCell.Offset(, 2).Value
' Store nodes values (first digit and second column type)
Select Case secondColumnType
Case "Node"
Dim nodeValues() As Variant
Dim nodeCounter As Long
ReDim Preserve nodeValues(nodeCounter)
nodeValues(nodeCounter) = Array(firstDigit, thirdColumnValue)
nodeCounter = nodeCounter + 1
Case "Arrow"
Dim matchedNodeFirstValue As String
Dim matchedNodeSecondValue As String
matchedNodeFirstValue = IsInArrayReturnItem(firstDigit, nodeValues)(1)
matchedNodeSecondValue = IsInArrayReturnItem(secondDigit, nodeValues)(1)
If matchedNodeFirstValue <> vbNullString And matchedNodeSecondValue <> vbNullString Then
valueCell.Offset(, 3).Value = matchedNodeFirstValue & Space(1) & thirdColumnValue & Space(1) & matchedNodeSecondValue
End If
End Select
End If
Next valueCell
CleanExit:
Exit Sub
CleanFail:
Debug.Print "Something went wrong: " & Err.Description
Resume CleanExit
End Sub
' Credits: https://stackoverflow.com/a/38268261/1521579
Public Function IsInArrayReturnItem(stringToBeFound As Integer, arr As Variant) As Variant
Dim i
For i = LBound(arr) To UBound(arr)
If arr(i)(0) = stringToBeFound Then
IsInArrayReturnItem = arr(i)
Exit Function
End If
Next i
IsInArrayReturnItem = Array(vbNullString, vbNullString)
End Function
Let me know if it works
It appears that you are concatenating the lookups based on the
first and second integers,
where the second column = "Arrow"
If that is the case, I suggest:
Read the data table into a VBA array for faster processing
I am assuming your data is ordered as you show it, with all the Node entries at the start.
if that is not the case, then loop twice -- once to find the Nodes, and second time to concatenate the Arrow data.
Read the diagnoses into a dictionary for fact lookup.
if column2 = "Arrow" then concatenate the lookups of the first and second integers
Write back the data
Note: As written, this will overwrite the original table destroying any formulas that might be there. If needed, you could easily modify it to only overwrite the necessary area.
Note2 Be sure to set a reference (under Tools/References) to Microsoft Scripting Runtime, or change the Dictionary declaration to late-binding.
Regular Module
'set reference to Microsoft Scripting Runtime
Option Explicit
Sub Dx()
Dim WS As Worksheet
Dim rngData As Range, c As Range, vData As Variant
Dim dDx As Dictionary
Dim I As Long, sKey As String, dxKeys As Variant
'Get the data range
Set WS = ThisWorkbook.Worksheets("sheet1")
With WS
'assume table starts in A1 and is three columns wide
Set rngData = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp)).Resize(columnsize:=3)
'read into variant array for faster processing
vData = rngData
End With
'create dictionsry for dx lookups
Set dDx = New Dictionary
For I = 2 To UBound(vData, 1)
Select Case vData(I, 2)
Case "Node"
sKey = Split(vData(I, 1), ",")(0) 'first comma-separated number
If dDx.Exists(sKey) Then
MsgBox "duplicate diagnostic key. Please correct the data"
Exit Sub
End If
dDx.Add Key:=sKey, Item:=vData(I, 3)
Case "Arrow"
dxKeys = Split(vData(I, 1), ",")
vData(I, 3) = dDx(dxKeys(0)) & " + " & dDx(dxKeys(1))
End Select
Next I
'reWrite the table
Application.ScreenUpdating = False
rngData = vData
End Sub
Hi I'm trying to build a macro that can search for cells with any value in and increase the numbers inside them by one.
all my cells have a text and numbers for e.g. ( Movie 1 , Movie 2 , Car )
each cell contains a name and a number .. the name might be one or two words or more.. the number is not always at the end and it's usually from 0 to 200 but not all of the cells have numbers.
Those cells are all over the sheet and I want the macro to search for anything that has value in it and separate the numbers from texts then increase the numbers by one.
after hours of trial and error I reached to this code :
Sub IncreaseCellValue()
Dim value As Variant
'Add 1 to the existing cell value
If IsNumeric(Range("A1").value) Then
Range("A1").value = Range("A1") + 1
Else
value = Split(Range("A1").value, " ")
Range("A1").value = value(0) & " " & (CInt(value(1)) + 1)
End If
End Sub
The problem now is this code can only be applied to one specified cell.
Since working directly with excel cells would slow down your executing time (when there are large number of cells to check), working with an array would be the key:
Option Explicit
Sub IncreaseCellValue()
Dim arr As Variant
'This will hold your whole worksheet. Change the sheet name
arr = ThisWorkbook.Sheets("SheetName").UsedRange.Value
Dim i As Long, j As Long
For i = 1 To UBound(arr) 'for every row
For j = 1 To UBound(arr, 2) 'for every column
Select Case True
Case arr(i, j) = vbNullString
Case arr(i, j) Like "*MyWord*" 'beware Like is Case Sensitive
Case Else
arr(i, j) = AddOne(arr(i, j))
End Select
Next j
Next i
'Paste you array back to the worksheet
ThisWorkbook.Sheets("SheetName").UsedRange.Value = arr
'Note this will paste only values, so if you have formulas they will disappear
End Sub
Private Function AddOne(Value As Variant) As Variant
Dim MySplit As Variant
MySplit = Split(Value, " ")
Dim i As Long
For i = LBound(MySplit) To UBound(MySplit)
If IsNumeric(MySplit(i)) Then
AddOne = AddOne & " " & MySplit(i) + 1
Else
AddOne = AddOne & " " & MySplit(i)
End If
Next i
AddOne = Trim(AddOne)
End Function
I have the following list on Sheet1:
COLUMN A COLUMNB COLUMN C
1 ADDRESS Services(s) USED VEHICLE(S) USED
2 Address1 Service4 Vehicle1, Vehicle3, Vehicle4
3 Address1 Service3 Vehicle1, Vehicle3, Vehicle4
4 Address2 Service5 Vehicle1, Vehicle2, Vehicle5
5 Address2 Service2 Vehicle1, Vehicle6
6 Address2 Service1, Service2, Service3, Service4, Service5, Service6 Vehicle2, Vehicle5, Vehicle6
7 Address1 Service1, Service2, Service3, Service4, Service5, Service6 Vehicle2, Vehicle3
On Sheet2, I would like the following output in Column B when I enter "Address1" in cell B4
COLUMN A COLUMN B
4 Address1
12 Service1
13 Service2
14 Service3
15 Service4
16 Service5
17 Service6
50 Vehicle1
51 Vehicle2
52 Vehicle3
53 Vehicle4
54 Vehicle5
56 Vehicle6
Worksheet_Change Code ("Sheet2" module)
Private Sub Worksheet_Change(ByVal Target As Range)
' call Function only if modifed cell is in Column "B"
If Not Intersect(Target, Range("B4")) Is Nothing Then
Application.EnableEvents = False
Call FilterAddress(Target.Value)
End If
Application.EnableEvents = True
End Sub
Sub FilterAddress Code (Regular module)
Option Explicit
Sub FilterAddress(FilterVal As String)
Dim LastRow As Long
Dim FilterRng As Range, cell As Range
Dim Dict As Object
'Dim ID
Dim Vehicle As Variant
Dim VehicleArr As Variant
Dim i As Long, j As Long
Dim Service As Variant
Dim ServiceArr As Variant
Dim x As Long, y As Long
Dim My_Range As Range
With Sheets("Sheet1")
' find last row with data in column "A" (Adress)
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set FilterRng = .Range("A1:C" & LastRow)
.Range("A1").AutoFilter
' AutoFilter "Sheet1" according to value in "Sheet2" in Column B
FilterRng.AutoFilter Field:=1, Criteria1:=FilterVal
Set Dict = CreateObject("Scripting.Dictionary")
' create an array with size up to number of rows >> will resize it later
ReDim ServiceArr(1 To LastRow)
j = 1 ' init array counter
For Each cell In .Range("B2:B" & LastRow).SpecialCells(xlCellTypeVisible)
' read values from cell to array using the Split function
Service = Split(cell.Value, ",")
For i = LBound(Service) To UBound(Service)
Service(i) = Trim(Service(i)) ' remove extra spaces from string
If Not Dict.exists(Service(i)) Then
Dict.Add Service(i), Service(i)
' save Service Name to array >> will use it later for "Bubble-sort" and paste in "Sheet2"
ServiceArr(j) = Service(i)
j = j + 1 ' increment ServiceArr counter
End If
Next i
Next cell
' resize array up to number of actual Service
ReDim Preserve ServiceArr(1 To j - 1)
End With
Dim ServiceTmp As Variant
' Bubble-sort Service Array >> sorts the Service array from smallest to largest
For i = 1 To UBound(ServiceArr) - 1
For j = i + 1 To UBound(ServiceArr)
If ServiceArr(j) < ServiceArr(i) Then
ServiceTmp = ServiceArr(j)
ServiceArr(j) = ServiceArr(i)
ServiceArr(i) = ServiceTmp
End If
Next j
Next i
' now the "fun" part >> paste to "Sheet2"
With Sheets("Sheet2")
.Range("A1").Value = "ADDRESS"
.Range("B4").Value = FilterVal
.Range("C1").Value = "VEHICLE(S) USED"
' clear contents from previous run
.Range("B12:B17").ClearContents
.Range("B12:B" & UBound(ServiceArr) + 11) = WorksheetFunction.Transpose(ServiceArr)
End With
FilterRng.Parent.AutoFilterMode = False
With Sheets("Sheet1")
' find last row with data in column "A" (Adress)
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set FilterRng = .Range("A1:C" & LastRow)
.Range("A1").AutoFilter
' AutoFilter "Sheet1" according to value in "Sheet2" in Column B
FilterRng.AutoFilter Field:=1, Criteria1:=FilterVal
Set Dict = CreateObject("Scripting.Dictionary")
' create an array with size up to number of rows >> will resize it later
ReDim VehicleArr(1 To LastRow)
y = 1 ' init array counter
For Each cell In .Range("C2:C" & LastRow).SpecialCells(xlCellTypeVisible)
' read values from cell to array using the Split function
Vehicle = Split(cell.Value, ",")
For x = LBound(Vehicle) To UBound(Vehicle)
Vehicle(x) = Trim(Vehicle(x)) ' remove extra spaces from string
If Not Dict.exists(Vehicle(x)) Then
Dict.Add Vehicle(x), Vehicle(x)
' save Vehicle Name to array >> will use it later for "Bubble-sort" and paste in "Sheet2"
VehicleArr(y) = Vehicle(x)
y = y + 1 ' increment VehicleArr counter
End If
Next x
Next cell
' resize array up to number of actual Vehicle
ReDim Preserve VehicleArr(1 To y - 1)
End With
Dim VehicleTmp As Variant
' Bubble-sort Vehicle Array >> sorts the Vehicle array from smallest to largest
For x = 1 To UBound(VehicleArr) - 1
For y = x + 1 To UBound(VehicleArr)
If VehicleArr(y) < VehicleArr(x) Then
VehicleTmp = VehicleArr(y)
VehicleArr(y) = VehicleArr(x)
VehicleArr(x) = VehicleTmp
End If
Next y
Next x
' now the "fun" part >> paste to "Sheet2"
With Sheets("Sheet2")
.Range("A1").Value = "ADDRESS"
.Range("B4").Value = FilterVal
.Range("C1").Value = "VEHICLE(S) USED"
' clear contents from previous run
.Range("B50:B55").ClearContents
.Range("B50:B" & UBound(VehicleArr) + 49) = WorksheetFunction.Transpose(VehicleArr)
End With
FilterRng.Parent.AutoFilterMode = False
End Sub
When I enter "Address1" in cell B4 on Sheet2, I receive the following error:
Runtime error '9':
Subscript out of range
However, if I save the file with B4 populated and close it, then re open the file, I am able to get the macro to work properly when I edit the cell contents to say either Address1 or Address2.
What is causing the "Subscript out of range" message to appear, and how can I change the code to avoid it? Do I need to update the code in Worksheet_Change Code?
I've also noticed that if I delete the contents of cell B4 on Sheet2 I get the following error:
Run-time error'1004':
No cells were found.
Are these two errors related?
The maximum 'j' isn't bounded by the number of rows on the sheet - it's bounded by the number of elements that you can split out of those rows. There's no way to determine before your code executes what size ServiceArr needs to be dimensioned to. That means depending on the data, you'll get intermittent subscript errors in this section:
ReDim ServiceArr(1 To LastRow) '<-- This is only a guess.
j = 1
For Each cell In .Range("B2:B" & LastRow).SpecialCells(xlCellTypeVisible)
Service = Split(cell.Value, ",")
For i = LBound(Service) To UBound(Service)
Service(i) = Trim(Service(i))
If Not Dict.exists(Service(i)) Then
Dict.Add Service(i), Service(i)
ServiceArr(j) = Service(i) '<--Subscript error here if unique elements > LastRow
j = j + 1
End If
Next i
Next cell
The solution is ridiculously easy - get rid of ServiceArr completely. It will always be exactly the same thing as both Dict.Keys and Dict.Values because you're basically keeping a 3rd identical copy of the same data here:
Dict.Add Service(i), Service(i)
ServiceArr(j) = Service(i)
This does almost exactly the same thing as your code, except it gives you a 0 based array instead of a 1 based array:
For Each cell In .Range("B2:B" & LastRow).SpecialCells(xlCellTypeVisible)
Service = Split(cell.Value, ",")
For i = LBound(Service) To UBound(Service)
Service(i) = Trim(Service(i))
If Not Dict.exists(Service(i)) Then
Dict.Add Service(i), Empty
End If
Next i
Next cell
ServiceArr = Dict.Keys
'...
'Adjust this to 0 based.
For i = LBound(ServiceArr) To UBound(ServiceArr)
See #YowE3K's comment for why you get the other error.
Well, just wildly guessing but can you try the following:
Option 1
In stead of:
For i = 1 To UBound(ServiceArr) - 1
For j = i + 1 To UBound(ServiceArr)
Write:
For i = 0 To UBound(ServiceArr) - 1
For j = i + 1 To UBound(ServiceArr)
Option 2
In stead of:
j = 1 ' init array counter
Write:
j = 0 ' init array counter
If nothing works, give information about the line of the error. E.g. once you see the error message, press debug and see on which line is colored in yellow.
This is an Excel problem.
I have an Excel Rows with the following values:
cell value 1, 2, x, ,1=,2=, ,x=,2
cell address a1,b1,c1,d1,e1,f1,g1,h1,i1
I want to get all non-empty cell addresses from the above row.
i.e.
a1,b1,c1,e1,f1,h1,i1
Is it possible using vba/vbs to do the job?
thank you very much
You can use .SpecialCells(xlCellTypeConstants)
To demonstrate, run this, and observe result in the Imediate window
Sub demo()
Dim rng As Range, rNonEmpty As Range
Set rng = [A1:I1]
Set rNonEmpty = rng.SpecialCells(xlCellTypeConstants)
Debug.Print rNonEmpty.Address
End Sub
The following code will check an area of 10 rows by 1000 columns and finally will show the addresses of the non blank cells in cell A20:
Sub no_blank_cells()
Dim wks As Worksheet
Set wks = ActiveSheet
m = ""
maxrows = 10
maxcolumns = 1000
For i = 1 To maxrows
For j = 1 To maxcolumns
a = Cells(i, j)
If a <> "" Then
m = m & Cells(i, j).Address(RowAbsolute:=False, ColumnAbsolute:=False) & ", "
End If
Next j
Next i
msg = MsgBox(m, vbInformation)
wks.Cells(20, 1) = m
End Sub
If you want to change the area to be searched, modify the value of the variables maxrowsand maxcolumns.
Sub WriteNonNulValue()
'/*SELECT SHEET TO ANALIZE*/
Worksheets("Sheet1").Select
'/*SELECT ROW TO READ*/
Row_to_read = 1
'/*SELECT ROW IN WHICH WRITE*/
Row_to_write = 2
'/*NUMBER OF COLUMNS TO ANALIZE*/
Columns_to_analize = 11
'/*COUNTER WRITE*/
Columns_to_write = 1
For i = 1 To Columns_to_analize
If Trim(Cells(Row_to_read, i)) <> "" Then
'/*WRITE ADDRESS NO EMPTY CELLS*/
Cells(Row_to_write, Columns_to_write) = Cells(Row_to_read,i).Address
'/*INCREMENT COUNTER WRITE*/
Columns_to_write = Columns_to_write + 1
End If
Next i
End Sub
So I want to run through A1-C200 and paste everything into a Word document. The trouble is, I have two ways of pasting it into Word, but each one has its downfall.
Goal: Copy A1-C200 into Word and keep the column layout, without copying blancs.
Example 1:
The code below copies everything into Word, but runs from A1 -> A200, B1 -> B200, C1 -> C200. Because it reads through my file this way, I lose my column layout. I would prefer a solution for this example, because this code looks clearer to me.
iMaxRow = 200
" Loop through columns and rows"
For iCol = 1 To 3
For iRow = 1 To iMaxRow
With Worksheets("GreatIdea").Cells(iRow, iCol)
" Check that cell is not empty."
If .Value = "" Then
"Nothing in this cell."
"Do nothing."
Else
" Copy the cell to the destination"
.Copy
appWD.Selection.PasteSpecial
End If
End With
Next iRow
Next iCol
Example 2:
The code below copies the correct column layout, but also inserts blancs. So if A1-A5 and A80-A90 are filled in, I will have 75 blancs in my Word document.
a1 = Range("A1").End(xlDown).Address
lastcell = Range("C1").Address
Range(a1, lastcell).Copy
With Range("A1")
Range(.Cells(1, 1), .End(xlDown).Cells(2, 3)).Copy
End With
Range("A1:C50").Copy
appWD.Selection.PasteSpecial
There's multiple ways to do this, don't know which is the quickest but here's some code I threw together real quick for you. Getting the range all at once in a variant is the fastest way to grab data out of excel.
Sub test()
Dim i As Long, j As Long
Dim wd As Word.Document
Dim wdTable As Word.Table
Dim wks As Excel.Worksheet
Dim v1 As Variant
Set wd = GetObject("C:\Documents and Settings\Jon\Desktop\New Microsoft Word Document.doc")
'Get data in array
Set wks = ActiveSheet
v1 = wks.UsedRange
'Create table
Set wdTable = wd.Tables.Add(Range:=wd.Application.Selection.Range, NumRows:=1, NumColumns:= _
ubound(v1,2), DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:= _
wdAutoFitFixed)
'Place data
For i = 1 To UBound(v1)
For j = 1 To UBound(v1, 2)
If Len(v1(i, j)) > 0 Then
'Add row if not enough rows, this can be done before the j loop if
'you know the first column is always filled.
'You can also do an advanced filter in excel if you know that the first
'column is filled always and filter for filled cells then just
'do a straight copy and paste using r1.specialcells(xlCellTypeVisible).copy
'If you know the rows ahead of time when you create the table you can create all the rows at once,
'which should save time.
wd.application.selection
If wdTable.Rows.Count < i Then wdTable.Rows.Add
wdTable.Cell(i, j).Range.Text = v1(i, j)
End If
Next j
Next i
Set wks = Nothing: Set wd = Nothing: Set v1 = Nothing
End Sub
not quite sure I understand the prob ... but here's a stab at it:
dim rg200x3 as range: set rg200x3 = range("a1:c200")
dim Col1 as new collection
dim Col2 as new collection
dim Col3 as new collection
dim rgRow as new range
dim sText as string
for each rgRow in rg200x3
sText = trim(rgRow.cells(1,1)): if (sText <> "") call Col1.Add(sText)
sText = trim(rgRow.cells(1,2)): if (sText <> "") call Col2.Add(sText)
sText = trim(rgRow.cells(1,3)): if (sText <> "") call Col3.Add(sText)
next rgRow
at this point Col1, Col2, and Col3 contain your text w the blank cells factored out, so now loop over these to print out
dim i as long
for i = 1 to 200
on error resume next ' (cheap way to avoid checking if index > collection sz)
debug.print Col1(i) + " | " Col2(i) + " | " + Col3(i)
on error goto 0
next i
(note: code typed in freehand with no checking ... )
How about this to sub for your first solution:
iMaxRow = 200
" Loop through columns and rows"
For iRow = 1 To iMaxRow
For iCol = 1 To 3
With Worksheets("GreatIdea").Cells(iRow, iCol)
" Check that cell is not empty."
If .Value = "" Then
"Nothing in this cell."
"Do nothing."
Else
"Copy the cell to the destination"
.Copy appWD.Selection.PasteSpecial
End If
End With
Next iCol
Next iRow