When the "Not" in "And Not rng.Characters(iEnd, 1).Font.Italic" is left out the code is able to pull italicized and underlined words but when I place a "Not" in the code to pull out the underlined and not italicized words it doesn't work. I'm not exactly sure why the "Not" wouldnt work. The pictures below are the source sheet and the new sheet with the extracted data. Im able to use the code to get underlined and italicized but I'm adding the Not so I can get underlined but not italicized. The data that is supposed to come out is line 5 from the picture labeled 1.
Sub extract()
Dim dataRng As Range, cl As Range
Dim marr As Variant
Set dataRng = Worksheets("Sheet1").Range("C1:C10")
With Worksheets("Sheet2")
For Each cl In dataRng
marr = GetUnderlines(cl)
If IsArray(marr) Then .Cells(.Rows.Count, 4).End(xlUp).Offset(1).Resize(UBound(marr) + 1) = Application.Transpose(marr)
Next
End With
End Sub
Function GetUnderlines(rng As Range) As Variant
Dim strng As String
Dim iEnd As Long, iIni As Long, strngLen As Long
strngLen = Len(rng.Value2)
iIni = 1
Do While iEnd <= strngLen
Do While rng.Characters(iEnd, 1).Font.Underline And Not rng.Characters(iEnd, 1).Font.Italic
If iEnd = strngLen Then Exit Do
iEnd = iEnd + 1
Loop
If iEnd > iIni Then strng = strng & Mid(rng.Value2, iIni, iEnd - iIni) & "|"
iEnd = iEnd + 1
iIni = iEnd
Loop
If strng <> "" Then GetUnderlines = Split(Left(strng, Len(strng) - 1), "|")
End Function
.Font.Underline doesn't return a Boolean(True/False) ;) It returns an Long
If you add a Watch you can see it for yourself.
Change your code to
Do While rng.Characters(iEnd, 1).Font.Underline = xlUnderlineStyleSingle _
And Not rng.Characters(iEnd, 1).Font.Italic
Change while condition to this :
Underline is not boolean.
Do While (rng.Characters(iEnd, 1).Font.Underline = -4142 Or _
(rng.Characters(iEnd, 1).Font.Underline = 2 And _
Not rng.Characters(iEnd, 1).Font.Italic))
Your code is truncating the last character.
Function GetUnderlines(rng As Range) As Variant
Dim strng As String
Dim iEnd As Long, iIni As Long, strngLen As Long
strngLen = Len(rng.Value2)
iIni = 1
Do While iEnd <= strngLen
Do While rng.Characters(iEnd, 1).Font.Underline = xlUnderlineStyleSingle And Not rng.Characters(iEnd, 1).Font.Italic And rng.Characters(iEnd, 1).Text <> " "
iEnd = iEnd + 1
If iEnd > strngLen Then Exit Do
Loop
If iEnd > iIni Then
strng = strng & Mid(rng.Value2, iIni, iEnd - iIni) & "|"
End If
iEnd = iEnd + 1
iIni = iEnd
Loop
If strng <> "" Then GetUnderlines = Split(Left(strng, Len(strng) - 1), "|")
End Function
Related
I want to compare two set of rows, data from sheet "Calculated Structure" is compared against data from sheet "MAP" and if it is a match return the value in last column.
Based on other responses I have the following code which joins the rows into a string and then leverages dictionary to perform the compare. I am using dictionary to improve performance as I am comparing MAP and DATA that are both 50,000+ records.
Sub CheckRows()
Dim cl As Range
Dim Ws1 As Worksheet
Dim Ws2 As Worksheet
Dim Vlu As String
Dim VluD As String
Dim Lc As Long
Dim x As Long
Dim i As Long
Dim ArrRslt() As Variant
Dim iRw As Long
Dim LastRow As Long
Dim RngTrgt As Range
Dim dict As New Scripting.Dictionary
Set Ws1 = Sheets("Calculated Structure")
Set Ws2 = Sheets("Map")
Lc = Ws2.Cells(2, Columns.Count).End(xlToLeft).Column - 1
RsltCol = Ws1.Cells.Find("*", LookIn:=xlFormulas, SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
For icntr = RsltCol To 1 Step -1
If Cells(1, icntr).value = "Calced Result" Then
Columns(icntr).Delete
End If
Next
LastCol = Ws1.Cells.Find("*", LookIn:=xlFormulas, SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
Cells(1, LastCol + 1).value = "Calced Result"
i = 1
x = 1
ReDim ArrRslt(0)
With CreateObject("scripting.dictionary")
dict.CompareMode = 1
For Each cl In Ws2.Range("A1", Ws2.Range("A" & Rows.Count).End(xlUp))
Vlu = Join(Application.Index(cl.RESIZE(, Lc).value, 1, 0), "|")
dict.Add key:=Vlu, Item:=(Ws2.Cells(i, Lc + 1))
i = i + 1
Next cl
For Each cl In Ws1.Range("B2", Ws1.Range("B" & Rows.Count).End(xlUp))
VluD = Join(Application.Index(cl.RESIZE(, Lc).value, 1, 0), "|")
If dict.Exists(VluD) Then
CResult = dict(VluD)
ArrRslt(x - 1) = CResult
Else
ArrRslt(x - 1) = "?"
End If
ReDim Preserve ArrRslt(0 To x)
x = x + 1
Next cl
End With
LastRow = Ws1.Cells.Find("*", LookIn:=xlFormulas, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
Set RngTrgt = Ws1.Range(Cells(2, Lc + 2), Cells(LastRow, Lc + 2))
RngTrgt = Application.WorksheetFunction.Transpose(ArrRslt)
MsgBox "Mapping Complete!"
End Sub
Some instances row on "MAP" have wildcard("*") designated as the cell value.
For example MAP looks like:
Check 1
Check 2
Check 3
Result
ABC
DEF
123
R1
ABC
*
123
R2
And my data looks like:
Field 1
Field 2
Field 3
Expected Result
ABC
DEF
123
R1
ABC
GHI
123
R2
For my second row of data I expect to return "R2" because in the MAP check 2 is wildcarded, so any value for Field 2 should pass. Instead the "?" is returned indicating no match found. My understanding is that this is because string "ABC|GHI|123" is not defined in the map.
What can I do to account for the wildcard values?
I feel I need to evaluate each "Check/Field" individually. Meaning first see if match found for Check 1, if so search for match for Check 2, so on till all matches are found.
I tried nested dictionaries. I believe that I am able to assign values appropriate but hitting a Run-time Error '450' when trying to retrieve information from the dictionaries. Here is my code:
Sub theDictionary()
Dim cl As Range
Dim WsRslt As Worksheet
Dim WsMap As Worksheet
Dim Vlu As String
Dim VluD As String
Dim Lc As Long
Dim MapVl As Long
Dim RsltCol As Long
Dim icntr As Long
Dim LastCol As Long
Dim rCount As Long
Dim mCount As Long
Dim x As Long
Dim i As Long
Dim dictCount As Long
Dim ArrRslt() As Variant
Dim iRw As Long
Dim LastRow As Long
Dim previousCell As String
Dim cResult As String
Dim RngTrgt As Range
Dim dict As New Scripting.Dictionary
Dim subDict() As Object
Set WsRslt = Sheets("Calculated Structure")
Set WsMap = Sheets("Map")
MapVl = WsMap.Cells(2, Columns.Count).End(xlToLeft).Column
Lc = MapVl - 1
MsgBox "Map value=" & MapVl & "and LC=" & Lc
RsltCol = WsRslt.Cells.Find("*", LookIn:=xlFormulas, SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
For icntr = RsltCol To 1 Step -1
If Cells(1, icntr).value = "Calced Result" Then
Columns(icntr).Delete
End If
Next
LastCol = WsRslt.Cells.Find("*", LookIn:=xlFormulas, SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
Cells(1, LastCol + 1).value = "Calced Result"
i = 1
x = 1
dictCount = 1
rCount = 1
ReDim ArrRslt(0)
previousCell = ""
ReDim subDict(1 To MapVl - 1)
For dictCount = 1 To MapVl - 1
Set subDict(dictCount) = New Scripting.Dictionary
subDict(dictCount).CompareMode = vbTextCompare
Next dictCount
For Each cl In WsMap.Range("A2", WsMap.Range("A" & Rows.Count).End(xlUp))
For rCount = 1 To MapVl - 1
'get the first item and add it to the subdictionary
' MsgBox "MapVl=" & MapVl & " and rCount=" & rCount
If rCount = 1 Then
If subDict(rCount).Exists(cl.Offset(0, MapVl - (rCount + 1)).Value2) Then
' MsgBox cl.Offset(0, MapVl - (rCount + 1)) & " Exists"
Else
' MsgBox cl.Offset(0, MapVl - (rCount + 1)) & " First Add"
subDict(rCount).Add CStr(cl.Offset(0, MapVl - (rCount + 1)).Value2), CStr(cl.Offset(0, MapVl - rCount).Value2)
End If
ElseIf rCount < MapVl Then
subDict(rCount).Add CStr(cl.Offset(0, MapVl - (rCount + 1)).Value2), subDict(rCount - 1)
End If
' MsgBox "Prev Cell Blank=" & previousCell & "cl.value=" & cl.value
Next rCount
rCount = 1
Next cl
For Each cl In WsRslt.Range("B2", WsRslt.Range("B" & Rows.Count).End(xlUp))
For mCount = 1 To MapVl - 1
VluD = cl.Value2 'Join(Application.Index(cl.RESIZE(, Lc).value, 1, 0), "|")
MsgBox "Cell Value=" & VluD
If subDict(MapVl - mCount).Exists(VluD) Then
MsgBox "VluD= " & VluD
cResult = subDict(MapVl - mCount).Item(VluD) '<-- Run-time error '450': Wrong number of arguments or invalid property assignment
ArrRslt(x - 1) = cResult
MsgBox "cResult=" & cResult
Else
ArrRslt(x - 1) = "?"
End If
ReDim Preserve ArrRslt(0 To x)
x = x + 1
Next mCount
Next cl
LastRow = WsRslt.Cells.Find("*", LookIn:=xlFormulas, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
Set RngTrgt = WsRslt.Range(Cells(2, Lc + 2), Cells(LastRow, Lc + 2))
RngTrgt = Application.WorksheetFunction.Transpose(ArrRslt)
MsgBox "Mapping Complete!"
End Sub
Once I am able to read the values, I still need to figure out how I am going to account for the "wildcard" values in MAP.
Any guidance is greatly appreciated.
I'm able to find all the underlines but I want to be able to eliminate the ones that are followed by a " (". How can I manipulate the array to check for a space and then " (" ? In the example below only "hello" would be extracted but "for" and "do" would not because those two are followed by a " (".
Sub proj()
Dim dataRng As range, cl As range
Dim arr As Variant
Set dataRng = Worksheets("ItalicSourceSheet").range("C1:C5") '<--| change "ItalicSourceSheet" with your actual source sheet name
With Worksheets("ItalicOutputSheet") '<--|change "ItalicOutputSheet" with your actual output sheet name
For Each cl In dataRng
arr = GetItalics(cl) '<--| get array with italic words
If IsArray(arr) Then .Cells(.Rows.Count, 1).End(xlUp).Offset(1).Resize(UBound(arr) + 1) = Application.Transpose(arr) '<--| if array is filled then write it down to output sheet first blank cell in column "A"
Next
End With
End Sub
Function GetItalics(rng As range) As Variant
Dim strng As String
Dim iEnd As Long, iIni As Long, strngLen As Long
strngLen = Len(rng.Value2)
iIni = 1
Do While iEnd <= strngLen
Do While rng.Characters(iEnd, 1).Font.Italic And rng.Characters(iEnd, 1).Font.Underline
If iEnd = strngLen Then Exit Do
iEnd = iEnd + 1
Loop
If iEnd > iIni Then strng = strng & Mid(rng.Value2, iIni, iEnd - iIni) & "|"
iEnd = iEnd + 1
iIni = iEnd
Loop
If strng <> "" Then GetItalics = Split(Left(strng, Len(strng) - 1), "|")
End Function
I would build the array within the function.
Option Explicit
Sub proj()
Dim dataRng As Range, cl As Range
Dim arr As Variant
Set dataRng = Worksheets("ItalicSourceSheet").Range("C1:C5") '<--| change "ItalicSourceSheet" with your actual source sheet name
With Worksheets("ItalicOutputSheet")
For Each cl In dataRng
If CBool(Len(cl.Value2)) Then
arr = getUnderlinedItalics(cl) '<--| get array with italic words
If IsArray(arr) Then .Cells(.Rows.Count, 1).End(xlUp).Offset(1).Resize(UBound(arr) + 1) = Application.Transpose(arr) '<--| if array is filled then write it down to output sheet first blank cell in column "A"
End If
Next
End With
End Sub
Function getUnderlinedItalics(rng As Range, _
Optional non As String = " (") As Variant
Dim str As String, tmp As String, a As Long, p As Long, ars As Variant
'make sure that rng is a single cell
Set rng = rng(1, 1)
'initialize array
ReDim ars(a)
'create a string that is longer than the original
str = rng.Value2 & Space(Len(non))
For p = 1 To Len(rng.Value2)
If rng.Characters(p, 1).Font.Italic And rng.Characters(p, 1).Font.Underline Then
tmp = tmp & Mid(str, p, 1)
ElseIf CBool(Len(tmp)) And Mid(str, p, 2) <> non Then
ReDim Preserve ars(a)
ars(a) = tmp
a = a + 1: tmp = vbNullString
Else
tmp = vbNullString
End If
Next p
getUnderlinedItalics = ars
End Function
change
If iEnd > iIni Then strng = strng & Mid(rng.Value2, iIni, iEnd - iIni) & "|"
into
If iEnd > iIni Then If Mid(rng.Value2, iIni + iEnd - iIni, 2) <> " (" Then strng = strng & Mid(rng.Value2, iIni, iEnd - iIni) & "|"
I got help with this code but when it runs it does not execute what it needs to do. I'm trying to extract words that are underlined and italicized from row C of the first sheet and move them to the secondsheet. The expected outcome is in the second image. Would array splitting be of use in this situation? Hopefully the sample data make it more clear.
Sub proj()
For Each cl In Range("C1:C5")
Call CopyItalicUnderlined(cl, Worksheets("Sheet2").Range("A1"))
Next
End Sub
Sub CopyItalicUnderlined(rngToCopy, rngToPaste)
rngToCopy.Copy rngToPaste
Dim i
For i = Len(rngToCopy.Value2) To 1 Step -1
With rngToPaste.Characters(i, 1)
If Not .Font.Italic And Not .Font.Underline Then
.Text = vbNullString
End If
End With
Next
End Sub
Split() could help, but only after you already found out and parsed italic words since Characters() method can be called on Range object only
you could then try the following code:
Option Explicit
Sub proj()
Dim dataRng As range, cl As range
Dim arr As Variant
Set dataRng = Worksheets("ItalicSourceSheet").range("C1:C5") '<--| change "ItalicSourceSheet" with your actual source sheet name
With Worksheets("ItalicOutputSheet") '<--|change "ItalicOutputSheet" with your actual output sheet name
For Each cl In dataRng
arr = GetItalics(cl) '<--| get array with italic words
If IsArray(arr) Then .Cells(.Rows.Count, 1).End(xlUp).Offset(1).Resize(UBound(arr) + 1) = Application.Transpose(arr) '<--| if array is filled then write it down to output sheet first blank cell in column "A"
Next
End With
End Sub
Function GetItalics(rng As range) As Variant
Dim strng As String
Dim iEnd As Long, iIni As Long, strngLen As Long
strngLen = Len(rng.Value2)
iIni = 1
Do While iEnd <= strngLen
Do While rng.Characters(iEnd, 1).Font.Italic And rng.Characters(iEnd, 1).Font.Underline
If iEnd = strngLen Then Exit Do
iEnd = iEnd + 1
Loop
If iEnd > iIni Then strng = strng & Mid(rng.Value2, iIni, iEnd - iIni) & "|"
iEnd = iEnd + 1
iIni = iEnd
Loop
If strng <> "" Then GetItalics = Split(Left(strng, Len(strng) - 1), "|")
End Function
It's not the prettiest solution, but you can take each cell, put their contents in an array. Then, make some room, and "unload them" and move along.
I tested with some simple data, but if you have errors, can you show more examples of text/data?
Sub proj()
Dim cl As Range
Dim x As Long
x = 0
For Each cl In Sheets("Sheet1").Range("C1:C5")
Call CopyItalicUnderlined(cl, Worksheets("Sheet2").Range("A1").Offset(x, 0))
x = x + 1
Next
Call breakOutWords
End Sub
Sub CopyItalicUnderlined(rngToCopy As Range, rngToPaste As Range)
Dim foundWords() As Variant
rngToCopy.Copy rngToPaste
Dim i
For i = Len(rngToCopy.Value2) To 1 Step -1
With rngToPaste.Characters(i, 1)
Debug.Print .Text
If Not .Font.Italic And Not .Font.Underline Then
If .Text <> " " Then
.Text = vbNullString
Else
.Text = " "
End If
End If
End With
Next
rngToPaste.Value = Trim(rngToPaste.Value)
rngToPaste.Value = WorksheetFunction.Substitute(rngToPaste, " ", " ")
End Sub
Sub breakOutWords()
Dim lastRow As Long, i As Long, k As Long, spaceCounter As Long
Dim myWords As Variant
Dim groupRange As Range
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = lastRow To 1 Step -1
' Determine how many spaces - this means we have X+1 words
spaceCounter = Len(Cells(i, 1)) - Len(WorksheetFunction.Substitute(Cells(i, 1), " ", "")) + 1
If spaceCounter > 1 Then
Set groupRange = Range(Cells(i, 1), Cells(WorksheetFunction.Max(2, i + spaceCounter - 1), 1))
groupRange.Select
myWords = Split(Cells(i, 1), " ")
groupRange.Clear
For k = LBound(myWords) To UBound(myWords)
groupRange.Cells(1 + k, 1).Value = myWords(k)
Next k
Else
' how many new rows will we need for the next cell?
Dim newRows As Long
newRows = Len(Cells(i - 1, 1)) - Len(WorksheetFunction.Substitute(Cells(i - 1, 1), " ", ""))
Range(Cells(i, 1), Cells(i + newRows - 1, 1)).EntireRow.Insert
End If
Next i
End Sub
I think this should work - I modified your code to match your example.
Change the top constants to mark where you want to start appending
into Sheet 2
Change names of Worksheets to match your real life sheets
Change range of cells to check in Set rge = ws1.Range("C8:C100")
Example Code:
Option Explicit
Public Sub ExtractUnderlinedItalicizedWords()
' Where to start appending new words '
Const INSERT_COL As Integer = 1
Const START_AT_ROW As Integer = 1
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim rge As Range
Dim cel As Range
Dim c As Object
Dim countChars As Integer
Dim i As Integer
Dim intRow As Integer
Dim strWord As String
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
intRow = START_AT_ROW
' Define the range of cells to check
Set rge = ws1.Range("C8:C100")
For Each cel In rge.Cells
countChars = cel.Characters.count
' Only do this until we find a blank cell
If countChars = 0 Then Exit For
strWord = ""
For i = 1 To countChars
Set c = cel.Characters(i, 1)
With c.Font
If (.Underline <> xlUnderlineStyleNone) And (.Italic) Then
strWord = strWord & c.Text
Else
If Len(strWord) > 0 Then
ws2.Cells(intRow, INSERT_COL).Value = strWord
intRow = intRow + 1
strWord = ""
End If
End If
End With
Next i
' Get Last Word in cell
If Len(strWord) > 0 Then
ws2.Cells(intRow, INSERT_COL).Value = strWord
intRow = intRow + 1
strWord = ""
End If
Next ' Next cell in column range
End Sub
The code below break the cells in image 1 into an array pictured in image 2. The new array is moved to start at AG. After that the program looks through the array and finds the words 'hello' and 'bye'. It takes those words and moves them into a new sheet and column pictured in image 3. Where I'm having trouble is that I want to still pull the strings 'hello' and 'bye' but I want to also pull the string directly before it from the array. In my example (image 3) I would've wanted it to read 'John Hello' instead of 'hello' on its own. What function would I use to extract the string before 'hello' or 'bye' also from the array?
Sub SplitWithFormat()
Dim R As Range, C As Range
Dim i As Long, V As Variant
Dim varHorizArray As Variant
Dim rge As Range
Dim intCol As Integer
Dim s As String
Set R = Range("d1", Cells(Rows.Count, "d").End(xlUp))
For Each C In R
With C
.TextToColumns Destination:=.Range("AD1"), DataType:=xlDelimited, _
consecutivedelimiter:=True, Tab:=False, semicolon:=True, comma:=False, _
Space:=True, other:=True, Otherchar:=vbLf
Set rge = Selection
varHorizArray = rge
.Copy
Range(.Range("AD1"), Cells(.Row, Columns.Count).End(xlToLeft)).PasteSpecial xlPasteFormats
End With
Next C
Application.CutCopyMode = False
For intCol = LBound(varHorizArray, 2) To UBound(varHorizArray, 2)
Debug.Print varHorizArray(1, intCol)
Next intCol
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
varHorizArray = Array("hello", "bye")
Set NewSh = Worksheets.Add
With Sheets("Sheet2").Range("AD1:AZ100")
Rcount = 0
For i = LBound(varHorizArray) To UBound(varHorizArray)
Set Rng = .find(What:=varHorizArray(i), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
Rng.Copy NewSh.Range("A" & Rcount)
NewSh.Range("A" & Rcount).Value = Rng.Value
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next i
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Option Explicit
Sub Tester()
Dim c As Range, v As String, arr, x As Long, e
Dim d As Range
'EDIT: changed destination for results
Set d = WorkSheets("Sheet2").Range("D2") '<<results start here
For Each c In ActiveSheet.Range("A2:A10")
v = Trim(c.Value)
If Len(v) > 0 Then
'normalize other separators to spaces
v = Replace(v, vbLf, " ")
'remove double spaces
Do While InStr(v, " ") > 0
v = Replace(v, " ", " ")
Loop
'split to array
arr = Split(v, " ")
For x = LBound(arr) To UBound(arr)
e = arr(x)
'see if array element is a word of interest
If Not IsError(Application.Match(LCase(e), Array("hello", "bye"), 0)) Then
If x > LBound(arr) Then
d.Value = arr(x - 1) & " " & e 'prepend previous word
Else
d.Value = "??? " & e 'no previous word
End If
Set d = d.Offset(1, 0)
End If
Next x
End If
Next c
End Sub
Something like this?
Option Explicit
Sub strings()
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim lookingForThese() As String
Set ws = ThisWorkbook.Worksheets(1)
Set rng = ws.Range(ws.Range("A1"), ws.Range("A1").End(xlDown))
ReDim lookingForThese(1 To 2)
lookingForThese(1) = "bye"
lookingForThese(2) = "hello"
For Each cell In rng
Dim i As Integer
Dim parts() As String
'Split the string in the cell
parts = Split(cell.Value, " ")
'I'm parsing the parts to a 2. worksheet and the hello/bye + the word before those on a 3.
For i = LBound(parts) To UBound(parts)
Dim j As Integer
ThisWorkbook.Worksheets(2).Cells(cell.Row, i + 1).Value = parts(i)
For j = LBound(lookingForThese) To UBound(lookingForThese)
If parts(i) = lookingForThese(j) Then
If i <> LBound(parts) Then
ThisWorkbook.Worksheets(3).Cells(cell.Row, 1).Value = parts(i - 1) & " " & parts(i)
Else
ThisWorkbook.Worksheets(3).Cells(cell.Row, 1).Value = parts(i)
End If
End If
Next j
Next i
Next cell
End Sub
I want to assign values to arrays from a sheet using loop
I tried using this but gives error "Subscript out of Range"
i=1
With ws
Do While i <= 40
ReDim Preserve WorkID(1 To i)
ReDim Preserve Work(1 To i)
ReDim Preserve ComposerName(1 To i)
WorkID(i) = Range("A" & i + 1).Value
Work(i) = Range("B" & i + 1).Value
ComposerName(i) = Range("C" & i + 1).Value
i = i + 1
Loop
End With
I tried both ways to initialize but none of them worked
Initialize Type 1
Dim WorkID() As Variant
Dim Work() As Variant
Dim ComposerName() As Variant
Initialize Type 2
Dim WorkID(1 to 40) As Variant
Dim Work(1 to 40) As Variant
Dim ComposerName(1 to 40) As Variant
Also I tried without Redim as well like this but nothing worked:
i=1
With ws
Do While i <= 40
WorkID(i) = Range("A" & i + 1).Value
Work(i) = Range("B" & i + 1).Value
ComposerName(i) = Range("C" & i + 1).Value
i = i + 1
Loop
End With
Full Sub here :
Option Explicit
Sub Join()
Dim WorkID() 'Stores the workID from Works Sheet
Dim Work() 'Stores the work from Works Sheet
Dim ComposerName() 'Stores the composer from Works Sheet
Dim ConductorID() 'Stores the ConductorID from Conductors Sheet
Dim ConductorNames() 'Stores Conductor Names from Conductors Sheet
Dim CDWorkID() 'Stores CDWorkID from CD Sheet
Dim CDCondID() 'Stores CDConductor ID from CD Sheet
Dim i, j, k, m As Long
Dim ws, wcon, wcd, wj As Worksheet
Set ws = Sheets("Works")
Set wcon = Sheets("Conductors")
Set wcd = Sheets("CDs")
Set wj = Sheets("Join")
i = j = k = 1 'Initalize
ws.Activate
Do While i <= 40
ReDim Preserve WorkID(1 To i)
ReDim Preserve Work(1 To i)
ReDim Preserve ComposerName(1 To i)
WorkID(i) = Range("A" & i + 1).Value
Work(i) = Range("B" & i + 1).Value
ComposerName(i) = Range("C" & i + 1).Value
i = i + 1
Loop
wcon.Activate
Do While j <= 10
ReDim Preserve ConductorID(1 To j)
ReDim Preserve ConductorNames(1 To j)
ConductorID(j) = Range("A" & j + 1).Value
ConductorNames(j) = Range("B" & j + 1).Value
j = j + 1
Loop
wcd.Activate
Do While k <= 132
ReDim Preserve CDWorkID(1 To k)
ReDim Preserve CDCondID(1 To k)
CDWorkID(k) = Range("A" & k + 1).Value
CDCondID(k) = Range("B" * k + 1).Value
k = k + 1
Loop
wj.Activate
For i = LBound(CDWorkID) To UBound(CDWorkID)
Range("F" & i) = CDWorkID(i)
Next i
End Sub
RedDim Preserve is generally an expensive operation since it involves allocating space for a larger array and moving contents from the old array. It is almost always a bad idea to use it inside of a loop. Instead -- determine ahead of time how big the arrays need to be and ReDim just once. If you don't know ahead of time, make them larger than needed and then use a ReDim Preserve after the loop to trim them down to size. In your case, I would Redim the arrays before entering for loops (or even -- why not Dim them the right size to begin with?). Also -- prefix each range with the appropriate worksheet variable rather than activating each in turn. Something like:
Sub Join()
Dim WorkID() 'Stores the workID from Works Sheet
Dim Work() 'Stores the work from Works Sheet
Dim ComposerName() 'Stores the composer from Works Sheet
Dim ConductorID() 'Stores the ConductorID from Conductors Sheet
Dim ConductorNames() 'Stores Conductor Names from Conductors Sheet
Dim CDWorkID() 'Stores CDWorkID from CD Sheet
Dim CDCondID() 'Stores CDConductor ID from CD Sheet
Dim i As Long
Dim ws, wcon, wcd, wj As Worksheet
Set ws = Sheets("Works")
Set wcon = Sheets("Conductors")
Set wcd = Sheets("CDs")
Set wj = Sheets("Join")
ReDim WorkID(1 To 40)
ReDim Work(1 To 40)
ReDim ComposerName(1 To 40)
For i = 1 To 40
WorkID(i) = ws.Range("A" & i + 1).Value
Work(i) = ws.Range("B" & i + 1).Value
ComposerName(i) = ws.Range("C" & i + 1).Value
Next i
ReDim ConductorID(1 To 10)
ReDim ConductorNames(1 To 10)
For i = 1 To 10
ConductorID(i) = wcon.Range("A" & i + 1).Value
ConductorNames(i) = wcon.Range("B" & i + 1).Value
Next i
ReDim CDWorkID(1 To 132)
ReDim CDCondID(1 To 132)
For i = 1 To 132
CDWorkID(k) = wcd.Range("A" & i + 1).Value
CDCondID(k) = wcd.Range("B" & i + 1).Value
Next i
For i = LBound(CDWorkID) To UBound(CDWorkID)
wj.Range("F" & i) = CDWorkID(i)
Next i
End Sub
Range("B" * k + 1).Value has a typo - you meant Range("B" & k + 1).Value. This makes the range raise an "type" error.
Eliminating this makes the code run without error - I suspect the error message is incorrect.
BTW, there is another pitfall (which does not lead to a runtime error, at least not for the code shown):
Dim i, j, k, m As Long
Dim ws, wcon, wcd, wj As Worksheet
will NOT declare i, j, kas Integer but as Variants. Same for ws, wcon, wcd which are variants and NOT worksheet objects.