I have a problem I need help with involving Excel and VBA. I know next to nothing about Excel/VBA, and I need a coding solution to help me avoid performing the extremely tedious task of doing this manually (think hundreds of lines that need to be parsed where one row could become multiple rows in a new sheet). I've been searching the web for solutions, but I just keep getting confused by the answers (because I don't know anything about VB and using it to program a macro in Excel), so I figured I'd seek help for my specific problem.
Here is the rundown: I have a spreadsheet where I need to copy rows from a source sheet to a target sheet. The source sheet has 2 columns (A & B) that can be thought of as a key/value pair where col A contains the key and col B contains the value. The problem lies with the values in col B. The values can either be a single line of text or a numbered list of different texts
What I want to do is for each row in the source:
split the values in col B to get an array of each individual value (if the value is in the form of a numbered list)
create new rows in the target sheet by looping over the split array of values such that a new row will be created where:
new row col A = source row col A key and new row col B = current iteration index from the array of split values.
if no numbered list, just copy the source row into target sheet
Source
A B
key1 1. text1
2. text2
key2 1. text3
Target
A B
key1 text1
key1 text2
key2 text3
The numbered list in a cell will be multiple lines where each line of text is prepended by a decimal and a dot. This applies to single line cells as well.
(Update) Bear in mind that the values in either col A or B are not simple text values. These are full on sentences. So, I'm not sure a simple formula is going to work.
Split Multi Line
It is unclear which line separator occurs in the multi line cells. Choose one, vbLf worked for me.
Adjust the values in the constants section to fit your needs.
The Code
Sub SplitMultiLine()
Const cSheet1 As Variant = "Sheet1" ' Source Worksheet Name/Index
Const cFirstR As Integer = 1 ' Source First Row Number
Const cFirstC As Variant = "A" ' Source First Column Letter/Number
Const cLastC As Variant = "C" ' Source Last Column Letter/Number
Const cMulti As Integer = 2 ' Multi Column
Const cSplit As String = vbLf ' Split Char(vbLf, vbCrLf, vbCr)
Const cDot As String = "." ' Dot Char (Delimiter)
Const cSheet2 As Variant = "Sheet1" ' Target Worksheet Name/Index
Const cTarget As String = "E1" ' Target First Cell Address
Dim vntS As Variant ' Source Array
Dim vntSplit As Variant ' Split Array
Dim vntT As Variant ' Target Array
Dim lastR As Long ' Source Last Row
Dim i As Long ' Source Array Row Counter
Dim j As Integer ' Source/Target Array Column Counter
Dim k As Long ' Target Array Row Counter
Dim m As Integer ' Split Array Row Counter
' Paste Source Range into Source Array.
With Worksheets(cSheet1)
lastR = .Cells(.Rows.Count, cFirstC).End(xlUp).Row
vntS = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR, cLastC))
End With
' Count the number of rows in target array.
For i = 1 To UBound(vntS)
k = k + UBound(Split(vntS(i, cMulti), cSplit)) + 1
Next
' Write from Source to Target Array.
ReDim vntT(1 To k, 1 To UBound(vntS, 2))
k = 0
For i = 1 To UBound(vntS)
k = k + 1
vntSplit = Split(vntS(i, cMulti), cSplit)
For m = 0 To UBound(vntSplit)
If InStr(vntSplit(m), cDot) > 0 Then
vntT(k, cMulti) = Trim(Right(vntSplit(m), Len(vntSplit(m)) _
- InStr(vntSplit(m), cDot)))
Else
vntT(k, cMulti) = vntSplit(m)
End If
For j = 1 To UBound(vntS, 2)
If j <> cMulti Then
vntT(k, j) = vntS(i, j)
End If
Next
k = k + 1
Next
k = k - 1
Next
' Paste Target Array into Target Range calculated from Target Frist Cell.
With Worksheets(cSheet2).Range(cTarget)
.Resize(UBound(vntT), UBound(vntT, 2)) = vntT
End With
End Sub
An Over-Commenting
Sub SplitMultiLineOverCommented()
Const cSheet1 As Variant = "Sheet1" ' Source Worksheet Name/Index
Const cFirstR As Integer = 1 ' Source First Row Number
Const cFirstC As Variant = "A" ' Source First Column Letter/Number
Const cLastC As Variant = "C" ' Source Last Column Letter/Number
Const cMulti As Integer = 2 ' Multi Column
Const cSplit As String = vbLf ' Split Char(vbLf, vbCrLf, vbCr)
Const cDot As String = "." ' Dot Char (Delimiter)
Const cSheet2 As Variant = "Sheet1" ' Target Worksheet Name/Index
Const cTarget As String = "E1" ' Target First Cell Address
Dim vntS As Variant ' Source Array
Dim vntSplit As Variant ' Split Array
Dim vntT As Variant ' Target Array
Dim lastR As Long ' Source Last Row
Dim i As Long ' Source Array Row Counter
Dim j As Integer ' Source/Target Array Column Counter
Dim k As Long ' Target Array Row Counter
Dim m As Integer ' Split Array Row Counter
' Paste Source Range into Source Array.
With Worksheets(cSheet1)
' The last row of data is usually calculated going from the bottom up,
' it is like selecting the last cell and pressing CTRL UP and returning
' =ROW() in Excel.
lastR = .Cells(.Rows.Count, cFirstC).End(xlUp).Row
' Paste a range into an array actually means copying it. The array
' created is a 1-based 2-dimensional array which has the same number
' of rows and columns as the Source Range.
vntS = .Range(.Cells(cFirstR, cFirstC), .Cells(lastR, cLastC))
End With
' Count the number of rows in Target Array.
' You refer to the last row of the array with UBound(vntS) which is short
' for UBound(vntS, 1) which reveals that it is referring to the first
' dimension (rows).
For i = 1 To UBound(vntS)
' We are splitting the string by cSplit which is the line
' separator (delimiter). When you enter something into a cell and
' hold left Alt and press ENTER, the vbLf character is set in place
' of the line separator. But the data may have been imported from
' another system that uses another line separator. When splitting the
' string, a 0-based array is 'created' and its UBound is the last
' row, but since it is 0-based we have to add 1.
k = k + UBound(Split(vntS(i, cMulti), cSplit)) + 1
Next
' Write from Source to Target Array.
' After we have calculated the number of rows, we have to resize the
' Target Array. To avoid confusion, I always use '1 To' to be certain that
' it is a 1-based array. Since the number columns of the Source Array and
' the Target Array is the same, we use the UBound of the Source Array to
' resize the second dimension of the Target Array - UBound(vntS, 2) where
' 2 is indicating the second dimension, columns.
ReDim vntT(1 To k, 1 To UBound(vntS, 2))
' We will use again k as the row counter since its value is no more
' needed. This is what I have many times forgotten, so maybe it is
' better to use a different variable.
k = 0
' Loop through the columns of Source Array.
For i = 1 To UBound(vntS)
' Increase the row of Target Array or e.g. align it for writing.
k = k + 1
' Split the string (lines) in the Multi Column into the 0-based
' Split Array.
vntSplit = Split(vntS(i, cMulti), cSplit)
' Loop through the values of the Split Array
For m = 0 To UBound(vntSplit)
' Check if value contains cDot. The Instr function returns 0 if
' a string has not been found, it's like =FIND(".",A1) in Excel,
' except that Excel would return an error if not found.
If InStr(vntSplit(m), cDot) > 0 Then
' If cDot was found then write the right part after cDot
' to the current row of column cMulti but trim the result
' (remove space before and after.
' It's like =TRIM(RIGHT(A1,LEN(A1)-FIND(".",A1))) in Excel.
vntT(k, cMulti) = Trim(Right(vntSplit(m), Len(vntSplit(m)) _
- InStr(vntSplit(m), cDot)))
Else
' If cDot was not found then just write the value to the
' current row.
vntT(k, cMulti) = vntSplit(m)
End If
' Loop through all columns.
For j = 1 To UBound(vntS, 2)
If j <> cMulti Then
' Write to other columns (Not cMulti)
vntT(k, j) = vntS(i, j)
End If
Next ' Next Source/Target Array Column
' Increase the current row of Target Array before going to next
' value in Split Array.
k = k + 1
Next ' Next Split Array Row
' Since we have increased the last current row but haven't written to
' it, we have to decrease one row because of the "k = k + 1" right below
' "For i = 1 To UBound(vntS)" which increases the row of Target Array
' for each next row in Source Array.
k = k - 1
Next ' Next Source Array Row
' Paste Target Array into Target Range calculated from Target Frist Cell.
' Like we pasted a range into an array, we can also paste an array into
' a range, but it has to be the same size as the array, so by using
' the Resize method we adjust the Target Range First Cell to the Target
' Range, using the last row and column of the Target Array. Again,
' remember UBound(vntT) is short for UBound(vntT, 1) (rows).
With Worksheets(cSheet2).Range(cTarget)
.Resize(UBound(vntT), UBound(vntT, 2)) = vntT
End With
End Sub
You can do this with two formulas.
I'm assuming your data is in Sheet1.
For the first columns, use the following formula:
=IF(ISBLANK(Sheet1!A2),A1,Sheet1!A2)
For the second one use:
=IFERROR(RIGHT(Sheet1!B2,LEN(Sheet1!B2)-FIND(". ",Sheet1!B2)-1),Sheet1!B2)
And populate down.
edit:
The first formula will look at the corresponding cell in Sheet1, column A. If it is blank, it will take the value of the cell above where the formula is. If it isn't blank, it will take the value of the cell in Sheet1, column A that it just checked.
The second formula looks for the string ". " in the cells in Sheet1 column B and removes it and everything to the left of it from the text. If the string in question (". ") is not found (meaning there is no numbering in that given cell) it would return an error, so the whole thing is wrapped in an IFERROR statement which returns the value of the cell in Sheet1 column B if it is triggered.
Related
Example data set
A B c D E F G H I J K L M N O P
-10 5 16 23 8 2 6 3162625 -10 5 16 23 8 2 6 3162626
Desired output
A B C D E F G H I J K L M N O P
-10 5 16 23 8 2 6 3162625
-10 5 16 23 8 2 6 3162626
Constant is -10 and i need the 7 columns after it
using VBA I can transfer column A to H to another sheet, but i can't get the VBA to move to Column I, Q etc etc
The VBA I have is
Sub search_and_extract_singlecriteria()
'1.
'2.
'3.
Dim datasheet As Worksheet
Dim reportsheet As Worksheet
Dim recordid As String
Dim finalrow As Integer
Dim i As Integer
Set datasheet = Sheet1
Set reportsheet = Sheet2
recordid = "-46" 'reportsheet.Range("B2").Value
'reportsheet.Range("A1:L100").ClearContents
datasheet.Select
finalrow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To finalrow
If Cells(i, 1) = recordid Then
Range(Cells(i, 9), Cells(i, 17)).Copy
reportsheet.Select
Range("A200").End(xlUp).Offset(1, 0).PasteSpecial xlPasteFormulasAndNumberFormats
datasheet.Select
End If
Next i
reportsheet.Select
Range("B2").Select
End Sub
Simple copy and paste is not an option as on one row, the 8 column sets repeat over a 1000 columns. each row, has varying column lengths. i will end up with 300k plus rows across 8 columns A:H if this can be done
Any suggestions would be greatly appreciated.
Try this. Have added some comments to explain.
If it's slow, more efficient to use arrays.
Sub x()
Dim r As Range
application.screenupdating=false
Set r = Sheet1.Range("A1").Resize(, 8) 'set starting range 1 x 8
Do Until IsEmpty(r(1)) 'keep doing this until first cell is empty
r.Copy Sheet2.Range("A" & Rows.Count).End(xlUp)(2) 'copy to first blank cell in A sheet2
Set r = r.Offset(, 8) 'move copy range along by 8 cells to the right
Loop
application.screenupdating=true
End Sub
Array vs Range
Option Explicit
'START ****************************************************************** START'
' Title: Search and Extract Single Criteria '
' Purpose: In a specified Data Worksheet, each non-empty row contains '
' an unknown number of consecutive data sets of a specified '
' size (columns). '
' By looping through each row of Data Worksheet, copies each '
' data set to another specified Report Worksheet one below '
' another, starting from a specified cell range. '
'******************************************************************************'
Sub search_and_extract_singlecriteria()
' 10s for 1280 cols and 3000 rows = 480000 rows in Report Sheet
Const Noc As Long = 8 ' Size of Data Set (Number of Columns)
' = Number of Columns in Report Array
Const strRR As String = "B2" ' Report First Cell Range Address
Dim wsD As Worksheet: Set wsD = Sheet1 ' Data Sheet
Dim wsR As Worksheet: Set wsR = Sheet2 ' Report Sheet
Dim rng As Range ' Last Non-Empty Cell in the Last Non-Empty Row,
' Non-Empty Range (both in Data Sheet)
Dim vntD As Variant ' Data Array (2D 1-based)
Dim vntC As Variant ' Count Array (1D 1-based)
Dim vntR As Variant ' Report Array (2D 1-based)
Dim Nor As Long ' Number of Data Sets
' = Number of Rows in Report Array
Dim i As Long ' Data/Count Array Rows Counter
Dim j As Long ' Data Array Columns Counter
Dim k As Long ' Count Array Values Counter
Dim m As Long ' Report Array Rows Counter
' IN DATA SHEET
' Note: It is assumed that Data Sheet contains ONLY Data Sets.
' By defining the Last Non-Empty Cell in the Last Non-Empty Row
' using the Find method, check if the sheet is not empty.
Set rng = wsD.Cells.Find("*", wsD.Cells(wsD.Rows.Count, wsD.Columns.Count), _
xlFormulas, , xlByRows)
If rng Is Nothing Then Exit Sub
' Define Non-Empty Range on Data Sheet.
Set rng = wsD.Range(wsD.Cells(rng.Row, wsD.Cells.Find("*", _
wsD.Cells(wsD.Rows.Count, wsD.Columns.Count), , , xlByColumns).Column), _
wsD.Cells(wsD.Cells.Find("*", , , , xlByRows, xlPrevious).Row, _
wsD.Cells.Find("*", , , , xlByColumns, xlPrevious).Column))
' Write values of Non-Empty Range on Data Sheet to Data Array.
vntD = rng
' Release object variables. Necessary data is in Data Array (vntD).
Set rng = Nothing
Set wsD = Nothing
' IN ARRAYS
' Task: Calculate Number of Rows in Report Array and populate Count Array.
' Resize Count Array (vntC) to number of rows of Data Array (vntD).
ReDim vntC(1 To UBound(vntD))
' Loop through rows (1st dimension) of Data Array (vntD).
For i = 1 To UBound(vntD)
' Loop through every Noc-th column (2nd dimension) of Data Array (vntD).
For j = 1 To UBound(vntD, 2) Step Noc
' Check if value of current element in Data Array (vntD) is <> "".
If vntD(i, j) <> "" Then
' Value of current element in Data Array (vntD) is <> "".
' Increase Count Array Value (Count of Data Sets in current row
' of Data Array).
k = k + 1
' Increase Number of Rows in Report Array
' (Total Count of Data Sets).
Nor = Nor + 1
Else
' Value of current element in Data Array (vntD) is = "".
' The following will leave the current element in Count Array
' empty, i.e. 0 which becomes obvious only later in:
' "If vntC(i) > 0 Then...".
Exit For
End If
Next
' Write current Count Array Value (k) to current element
' of Count Array (vntC).
' Note: The i-th row in Data Array contains k Data Sets.
vntC(i) = k
' Reset Count Array Values Counter.
k = 0
Next
' Remarks: Count Array (vntC) has the same number of elemnts
' as Data Array (vntD) has rows. Each value in Count Array (vntC)
' respresents the number of Data Sets per row of Data Array (vntD).
' The implementation of Count Array (vntC) makes it possible
' to write the last loop as a For Next loop:
' "For j = (k - 1) * Noc + 1 To (k - 1) * Noc + Noc...",
' without checking if there are "" values, because it has
' already been checked previously in:
' "If vntD(i, j) <> "" Then)...".
' Task: Define and populate Report Array.
' Resize Report Array (vntR) to rows defined by Number of Data Sets (Nor)
' and columns specified by (Column) Size of Data Set (Noc).
ReDim vntR(1 To Nor, 1 To Noc)
' Loop through rows (1st dimension) of Data Array (vntD).
For i = 1 To UBound(vntD)
' Check if the value in the same row (i) in Count Array (vntC) is > 0.
If vntC(i) > 0 Then
' Value in the same row (i) in Count Array (vntC) is > 0.
' Loop through Data Sets from Data Array.
For k = 1 To vntC(i)
' Increase Report Array Rows Counter (m).
m = m + 1
' Loop through columns (j) of current Data Set.
For j = (k - 1) * Noc + 1 To (k - 1) * Noc + Noc
' Write value of current element of Data Array (Set) to
' current element of Report Array.
vntR(m, j - (k - 1) * Noc) = vntD(i, j)
Next
Next
'Else
' Value in the same row (i) in Count Array (vntC) is NOT > 0 i.e.
' skipping (No Data Set in) current row of Data Array (vntD).
End If
Next
' IN REPORT SHEET
' Copy values of Report Array to Report Range defined by the specified
' Report First Cell Range Address (strRR) in specified Report Sheet (wsR)
' and the size (rows and columns) of Report Array (vntR).
wsR.Range(strRR).Resize(UBound(vntR), UBound(vntR, 2)) = vntR
End Sub
'END ********************************************************************** END'
My data sheet ("srData") is a pivot table that is filled using a userform. All data have a unique ID in column A of the data sheet. In the userform checkboxes are selected, which will change the cells, in columns K:AB, interior color to white(2), else interior color is grey(15)
In my main worksheet ("Formulier"), based on the value of a drop down box (C6)where the unique ID is selected (i.e. SR-1, SR-2,SR-3 etc...), the headers from sheet("srData") are returned in column A of sheet("Formulier") starting from row 20 if the interior.colorindex=2. The values in the cells are returned in column D starting from row 20.
Now in Column Y and Z of ("srData") I have placed a hyperlink which links to a PDF.(see SR-4 first image) In column Y and Z there will allways be hyperlinks in the cells with interior.colorindex=2.
When I now select the unique ID from the dropdown on sheet("Formulier") I would like it to return an active hyperlink and not just tekst as it does now. Is this possible?
This is the code I have for returning the header and the values. The code was created by VBasic2008 so credit goes to him.
`
Option Explicit
Public Const CriteriaCell As String = "C6" ' Criteria Cell Range Address
Sub ColorSearch()
' Source
Const cSource As Variant = "srData" ' Worksheet Name/Index
Const cCriteriaColumn As Variant = "A" ' Criteria Column Letter/Number
Const cColumns As String = "K:AB" ' Columns Range Address
Const cHeaderRow As Long = 1 ' Header Row Number
Const cColorIndex As Long = 2 ' Criteria Color Index (2-White)
' Target
Const cTarget As Variant = "Formulier" ' Worksheet Name/Index
Const cFr As Long = 20 ' First Row Number
Const cCol As Variant = "A" ' Column Letter/Number
Const cColVal As Variant = "D" ' Value Column Letter/Number
Dim Rng As Range ' Source Found Cell Range
Dim vntH As Variant ' Header Array
Dim vntC As Variant ' Color Array
Dim vntV As Variant ' Value Array
Dim vntT As Variant ' Target Array
Dim vntTV As Variant ' Target Value Array
Dim i As Long ' Source/Color Array Column Counter
Dim k As Long ' Target Array Row Counter
Dim sRow As Long ' Color Row
Dim SVal As String ' Search Value
Dim Noe As Long ' Source Number of Elements
' Write value from Criteria Cell Range to Search Value.
SVal = ThisWorkbook.Worksheets(cTarget).Range(CriteriaCell)
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Search for Search Value in Source Criteria Column and create
' a reference to Source Found Cell Range.
Set Rng = .Columns(cCriteriaColumn) _
.Find(SVal, , xlValues, xlWhole, , xlNext)
' Check if Search Value not found. Exit if.
If Rng Is Nothing Then Exit Sub
' Write row of Source Found Cell Range to Color Row.
sRow = Rng.Row
' Release rng variable (not needed anymore).
Set Rng = Nothing
' In Source Columns
With .Columns(cColumns)
' Copy Header Range to Header Array.
vntH = .Rows(cHeaderRow)
' Copy Color Range to Color Array.
vntC = .Rows(sRow)
' *** Copy Color Range to Value Array.
' Note: The values are also written to Color Array, but are
' later overwritten with the Color Indexes.
vntV = .Rows(sRow)
' Write number of columns in Source Columns to Source Number
' of Elements.
Noe = .Columns.Count
' Loop through columns of Color Range/Array.
For i = 1 To Noe
' Write current ColorIndex of Color Range to current
' element in Color Array.
vntC(1, i) = .Cells(sRow, i).Interior.ColorIndex
Next
End With
End With
' Resize Target Array to Number of Elements rows and one column.
ReDim vntT(1 To Noe, 1 To 1)
' *** Resize Target Value Array to Number of Elements rows and one column.
ReDim vntTV(1 To Noe, 1 To 1)
' Loop through columns of Color Array.
For i = 1 To Noe
' Check if current value in Color Array is equal to Criteria
' Column Index.
If vntC(1, i) = cColorIndex Then
' Count row in Target Array.
k = k + 1
' Write value of current COLUMN in Header Array to
' element in current ROW of Target Array.
vntT(k, 1) = vntH(1, i)
' *** Write value of current COLUMN in Value Array to
' element in current ROW of Target Value Array.
vntTV(k, 1) = vntV(1, i)
End If
Next
' Erase Header and Color Arrays (not needed anymore).
Erase vntH
Erase vntC
Erase vntV '***
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget)
' Calculate Target Range by resizing the cell at the intersection of
' Target First Row and Target Column, by Number of Elements.
' Copy Target Array to Target Range.
.Cells(cFr, cCol).Resize(Noe) = vntT
' *** Calculate Target Value Range by resizing the cell at the
' intersection of Target First Row and Value Column, by Number of
' Elements.
' Copy Target Value Array to Target Value Range.
.Cells(cFr, cColVal).Resize(Noe) = vntTV
End With
End Sub
`
Make a backup before and give this a try:
Option Explicit
Public Const CriteriaCell As String = "C6" ' Criteria Cell Range Address
Sub ColorSearch()
' Source
Const cSource As Variant = "srData" ' Worksheet Name/Index
Const cCriteriaColumn As Variant = "A" ' Criteria Column Letter/Number
Const cColumns As String = "K:AB" ' Columns Range Address
Const cHeaderRow As Long = 1 ' Header Row Number
Const cColorIndex As Long = 2 ' Criteria Color Index (2-White)
' Target
Const cTarget As Variant = "Formulier" ' Worksheet Name/Index
Const cFr As Long = 20 ' First Row Number
Const cCol As Variant = "A" ' Column Letter/Number
Const cColVal As Variant = "D" ' Value Column Letter/Number
Dim Rng As Range ' Source Found Cell Range
Dim targetCell As Range ' Cell to add hyperlink
Dim vntH As Variant ' Header Array
Dim vntC As Variant ' Color Array
Dim vntV As Variant ' Value Array
Dim vntHy As Variant ' Hyperlink Array (*)
Dim vntT As Variant ' Target Array
Dim vntTV As Variant ' Target Value Array
Dim vntTH As Variant ' Target Hyperlink
Dim i As Long ' Source/Color Array Column Counter
Dim k As Long ' Target Array Row Counter
Dim sRow As Long ' Color Row
Dim SVal As String ' Search Value
Dim Noe As Long ' Source Number of Elements
Dim hyperlinkCounter As Long ' Counter for assigning hyperlink
' Write value from Criteria Cell Range to Search Value.
SVal = ThisWorkbook.Worksheets(cTarget).Range(CriteriaCell)
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource)
' Search for Search Value in Source Criteria Column and create
' a reference to Source Found Cell Range.
Set Rng = .Columns(cCriteriaColumn) _
.Find(SVal, , xlValues, xlWhole, , xlNext)
' Check if Search Value not found. Exit if.
If Rng Is Nothing Then Exit Sub
' Write row of Source Found Cell Range to Color Row.
sRow = Rng.Row
' Release rng variable (not needed anymore).
Set Rng = Nothing
' In Source Columns
With .Columns(cColumns)
' Copy Header Range to Header Array.
vntH = .Rows(cHeaderRow)
' Copy Color Range to Color Array.
vntC = .Rows(sRow)
' *** Copy Color Range to Value Array.
' Note: The values are also written to Color Array, but are
' later overwritten with the Color Indexes.
vntV = .Rows(sRow)
' Write number of columns in Source Columns to Source Number
' of Elements.
Noe = .Columns.Count
' Redimension
ReDim vntHy(1 To 1, 1 To Noe)
' Loop through columns of Color Range/Array.
For i = 1 To Noe
' Write current ColorIndex of Color Range to current
' element in Color Array.
vntC(1, i) = .Cells(sRow, i).Interior.ColorIndex
If .Cells(sRow, i).Hyperlinks.Count > 0 Then
vntHy(1, i) = .Cells(sRow, i).Hyperlinks(1).Address
End If
Next
End With
End With
' Resize Target Array to Number of Elements rows and one column.
ReDim vntT(1 To Noe, 1 To 1)
' *** Resize Target Value Array to Number of Elements rows and one column.
ReDim vntTV(1 To Noe, 1 To 1)
' Resize target hyperlink array
ReDim vntTH(1 To Noe, 1 To 1)
' Loop through columns of Color Array.
For i = 1 To Noe
' Check if current value in Color Array is equal to Criteria
' Column Index.
If vntC(1, i) = cColorIndex Then
' Count row in Target Array.
k = k + 1
' Write value of current COLUMN in Header Array to
' element in current ROW of Target Array.
vntT(k, 1) = vntH(1, i)
' *** Write value of current COLUMN in Value Array to
' element in current ROW of Target Value Array.
vntTV(k, 1) = vntV(1, i)
' Add hyperlink to array
vntTH(k, 1) = vntHy(1, i)
End If
Next
' Erase Header and Color Arrays (not needed anymore).
Erase vntH
Erase vntC
Erase vntV '***
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget)
' Calculate Target Range by resizing the cell at the intersection of
' Target First Row and Target Column, by Number of Elements.
' Copy Target Array to Target Range.
.Cells(cFr, cCol).Resize(Noe) = vntT
' *** Calculate Target Value Range by resizing the cell at the
' intersection of Target First Row and Value Column, by Number of
' Elements.
' Copy Target Value Array to Target Value Range.
.Cells(cFr, cColVal).Resize(Noe) = vntTV
' Assign hyperlinks to cells
For Each targetCell In .Cells(cFr, cColVal).Resize(Noe)
' Remove previous hyperlinks
If targetCell.Hyperlinks.Count > 0 Then
targetCell.Hyperlinks.Item(1).Delete
End If
' Add new hyperlink
If vntTH(hyperlinkCounter + 1, 1) <> vbNullString Then
ThisWorkbook.Worksheets(cTarget).Hyperlinks.Add targetCell, vntTH(hyperlinkCounter + 1, 1)
End If
hyperlinkCounter = hyperlinkCounter + 1
Next targetCell
End With
End Sub
In general, the way you can turn a string to a Hyperlink is the following:
Sub text2Hyperlink()
Dim sht As Worksheet
Dim URL As String
Dim filePath As String
Set sht = ThisWorkbook.Worksheets("Worksheet Name") ' whichever worksheet you're working with
filePath = ".....\Something.pdf"
URL = "https://www.google.com/"
sht.Hyperlinks.Add sht.Range("A1"), filePath
sht.Hyperlinks.Add sht.Range("A2"), URL
End Sub
This takes some text stored in a string, and assigns it as a hyperlink in a cell. It works both for websites and files
In this case you end up with a link to a file in cell A1 and with a link to a webpage in cell A2.
You can modify this to suit your needs.
I have a special case where I need to count a specific number from a range of cells or a column, which will look like this
1 A
2 1,2,3
3 1,4,5
4 1,3,5,6
I need to count the "1" alone from this column A. Same way for every other numbers e.g., '2', '3' etc..
I have tried the following code, however it gives me the unique numbers count from a single cell
Public Function Count(r As Range) As Long
Dim c As Collection
Set c = New Collection
ary = Split(r.Text, ",")
On Error Resume Next
For Each a In ary
c.Add a, CStr(a)
If Err.Number = 0 Then
Count = Count + 1
Else
Err.Number = 0
End If
Next a
On Error GoTo 0
End Function`
How do I change this to a range as well as only specific to counting one number from that range?
You can do something like this:
Public Function CountNum(rng As Range, num) As Long
Dim rv As Long, c As Range, arr, a
num = CStr(num)
For Each c In rng.Cells
If Len(c.Value) > 0 Then
arr = Split(c.Value, ",")
For Each a In arr
If a = num Then rv = rv + 1
Next a
End If
Next c
CountNum = rv
End Function
To call (for example):
=countnum(A2:A4,1)
Count Delimited String Occurrences (UDF)
The Code
'***********************************************************************
' Title: Count Delimited String Occurrences
' Purpose: Counts the number of occurrences of a value in delimited parts
' of cells of a range containing not numeric values.
' Inputs:
' CountRange: Required. The range which cells to search.
' CountValue: Required. The value to search for. Variant.
' CountDelimiter: Optional. The delimiter by which each part of each
' cell will be checked against CountValue. Default is ",".
' CompareBinary0Text1: Optional. The method how the check will be
' performed. (Binary) - 0 i.e. AA <> Aa <> aa. Default.
' (Textual) - 1 i.e. AA = Aa = aa.
' All0OnlyOne1: Optional. Determines if all (0 - Default) or only
' the first (1) occurrence in each cell has to be found.
'*************************************************************************
Function CDSO(CountRange As Range, CountValue As Variant, _
Optional CountDelimiter As String = ",", _
Optional CompareBinary0Text1 As Long = 0, _
Optional All0OnlyOne1 As Long) As Long
Dim rng As Range ' Current Range (of Areas Collection)
Dim vntR As Variant ' Range Array (2D 1-based)
Dim vntC As Variant ' Cell Array (1D 0-based)
Dim vntCell As Variant ' Cell Variant
Dim i As Long ' Current Cell Row Counter
Dim j As Long ' Current Cell Column Counter
Dim k As Long ' CountRange Areas Counter
Dim m As Long ' Cell Array Element Counter
Dim ValCount As Long ' Value Counter
Dim strVal As String ' Value String
Dim strCell As String ' Cell String
' Convert CountValue to string (CStr), because arrays created
' using Split do only contain strings.
' Write CountValue to Value String.
strVal = CStr(CountValue)
' Loop through Areas Collection (ranges) of CountRange.
For k = 1 To CountRange.Areas.Count
' Check if Current Range contains one cell only.
If CountRange.Areas(k).Cells.Count = 1 Then
' Write value of Current Range (one cell only) to Cell Variant.
vntCell = CountRange.Areas(k)
' Go to Occurrences Counter Subroutine.
GoSub OccurrencesCounter
Else
' Copy Current Range to Range Array.
vntR = CountRange.Areas(k)
' Loop through rows of Range Array.
For i = 1 To UBound(vntR)
' Loop through columns of Range Array.
For j = 1 To UBound(vntR, 2)
' Write value of current element of Range Array to Cell
' Variant.
vntCell = vntR(i, j)
' Go to Occurrences Counter Subroutine.
GoSub OccurrencesCounter
Next
Next
End If
Next
' Write value of Value Counter to Count String Occurrences (CDSO).
CDSO = ValCount
Exit Function
' Occurrences Counter
' Purpose: Count the number of occurrences of CountValue in Cell String.
OccurrencesCounter:
' Check if Cell Variant is a number.
If IsNumeric(vntCell) Then Return
' Write value of Cell Variant converted to string to Cell String.
strCell = CStr(vntCell)
' Check if Cell String is not empty ("").
If strCell = "" Then Return
' Split Cell String by CountDelimiter into Cell Array.
vntC = Split(strCell, CountDelimiter)
' Loop through elements of Cell Array.
For m = 0 To UBound(vntC)
' Sometimes the values contain deliberate or accidental
' spaces, so Trim is used to remove them.
' If you want to use the vbTextCompare i.e. AA = Aa, AA = aa,
' in the formula set CompareBinary0Text1 to 1.
' Check if value of current element in Cell Array
' is equal to CountValue.
If StrComp(Trim(vntC(m)), strVal, CompareBinary0Text1) = 0 Then
' Count the occurrence i.e. increase Value Counter.
ValCount = ValCount + 1
' Note: If only the first occurrence in each cell is needed,
' increase efficiency with Exit For i.e. in the formula
' set All0OnlyOne1 to 1.
' Check if All0OnlyOne1 is equal to 1.
If All0OnlyOne1 = 1 Then
' Stop looping, occurrence found.
Exit For
End If
End If
Next
Return
End Function
'******************************************************************************
What I exactly need is to get the first 3 words out of each cell of the range selected, and then set it in the same place (each cell), so that I end up with the first 3 words in each cell. It doesn´t matter the number of words there were before. Basically, I need a code with a bucle to do that with each cell in the selection.
I´ve tried to use formula local, but it doesn´t work.
Sub EXTRAER_NOMBRES_Y_APELLIDO()
'Convierte los textos seleccionados a formato de nombre propio
'La primera letra en mayúscula y el resto en minúsculas
'Dim CELDA As String
'Dim B As Integer
For Each CELDA In Selection
'CELDA.Value = Left(Range("Y3"), 5)
'Range("Y3") = Left(Range("Y3"), 5)
'CELDA.Value = Left(CELDA, 3)
ActiveCell.FormulaLocal = "=LEFT(Planilla[#Estudiante];FIND(" ";Planilla[#Estudiante])-1)"
Next CELDA
End Sub
What I expect is to get the first 3 words in each cell of the column (range previously selected).
Split Names
Assumptions
There are two or three names per cell range (person):
First Name and Last Name or
First Name, Middle Name and Last Name.
You wanted the names from one column split into three columns.
The Code
Adjust the values in the constants section to fit your needs.
You can choose the same column letter or number if you want to
overwrite the initial data, but do this after testing the code.
Sub SplitNames()
Const cSource As Variant = "A" ' Source Column Letter/Number
Const cTarget As Variant = "B" ' Target Column Letter/Number
Const cFirstR As Long = 2 ' Source/Target First Row Number
Dim vntS As Variant ' Source Array
Dim vntD As Variant ' Delimited Array
Dim vntT As Variant ' Target Array
Dim LastR As Long ' Source/Target Last Row Number
Dim i As Long ' Source/Target Array Row Counter
' Calculate Source/Target Last Row Number.
LastR = Cells(Rows.Count, cSource).End(xlUp).Row
' Copy Source Range into Source Array.
vntS = Range(Cells(cFirstR, cSource), Cells(LastR, cSource))
' Resize Target Array's rows to the number of rows in Source Array,
' but to three columns: First, Middle, Last.
ReDim vntT(1 To UBound(vntS), 1 To 3)
' Copy from Source Array to Target Array.
For i = 1 To UBound(vntS) ' Rows of Source/Target Array
vntD = Split(vntS(i, 1)) ' Split each row of Source Array.
vntT(i, 1) = vntD(0) ' First Name
If UBound(vntD) = 2 Then ' Does have middle name.
vntT(i, 2) = vntD(1) ' Middle Name
vntT(i, 3) = vntD(2) ' Last Name
Else ' Does not have middle name.
vntT(i, 3) = vntD(1) ' Last Name
End If
Next
' Copy Target Array into Target Range.
Range(Cells(cFirstR, cTarget), Cells(LastR, cTarget)) _
.Resize(UBound(vntT), UBound(vntT, 2)) = vntT
End Sub
Second Version
Adjust the values in the constants section to fit your needs.
You can choose the same column letter or number if you want to
overwrite the initial data, but do this after testing the code.
Sub SplitNames2()
Const cSource As Variant = "A" ' Source Column Letter/Number
Const cTarget As Variant = "B" ' Target Column Letter/Number
Const cFirstR As Long = 7 ' Source/Target First Row Number
Const cNum As Long = 3 ' Number of Words
Dim vntS As Variant ' Source Array
Dim vntD As Variant ' Delimited Array
Dim vntT As Variant ' Target Array
Dim LastR As Long ' Source/Target Last Row Number
Dim i As Long ' Source/Target Array Row Counter
Dim j As Long ' Delimited Array Rows Counter
' Calculate Source/Target Last Row Number.
LastR = Cells(Rows.Count, cSource).End(xlUp).Row
' Copy Source Range into Source Array.
vntS = Range(Cells(cFirstR, cSource), Cells(LastR, cSource))
' Resize Target Array's rows to Source Array,
ReDim vntT(1 To UBound(vntS), 1 To 1)
' Copy from Source Array to Target Array.
For i = 1 To UBound(vntS) ' Rows of Source/Target Array
vntD = Split(vntS(i, 1)) ' Split each row of Source Array.
j = UBound(vntD)
If j > cNum - 1 Then
j = cNum - 1
End If
If j <> -1 Then
For j = 0 To j
If j > 0 Then
vntT(i, 1) = vntT(i, 1) & " " & vntD(j)
Else
vntT(i, 1) = vntD(j)
End If
Next
End If
Next
' Copy Target Array into Target Range.
Range(Cells(cFirstR, cTarget), Cells(LastR, cTarget)) = vntT
End Sub
This is a proposal, using basic Excel formulas:
Replace the first space with an underscore
Replace the first space with an underscore (as a result, both first spaces are replaced by underscore)
Determine the location of the first space (which gives the location of the third space in the original text)
Take the text, at the left of the nth character.
Hereby the formulas (the original text is in cell B2):
B3 : =SUBSTITUTE(B2;" ";"_";1)
B4 : =SUBSTITUTE(B3;" ";"_";1)
B5 : =FIND(" ";B4)
B6 : =LEFT(B2;B5-1)
The task in general is to sum specific values from an Excel dataset and paste it into another Worksheet.
My idea is to nest three loops.
The first Loop Counts the Project specific number
The second Loop Counts the columns (Begins with column 'H')
The third Loop Counts the rows (Begins with row '9')
Inside this function the program sums the values related to the project number.
After it is done, the accumulated value should be pasted into
another worksheet. The cell it has to be pasted in, is the specific cell for
the project number and column.
The third loop ends when it reached the last filled row.
The second loop ends when it reached the last filled column.
The first loop ends when it reached the last predefined project number
Paste the accumulated values into another Sheet
Sum and Copy Loop
Adjust the values in the constants section to fit your needs.
The Code
Sub SumAndCopy()
' Source
Const cSheet1 As Variant = "Sheet1" ' Worksheet Name/Index
Const cCol1 As Variant = "D" ' Criteria Column Letter/Number
Const cValFirst1 As Variant = "H" ' First Value Column/Number
Const cFirstRow1 As Integer = 9 ' First Row
' Target
Const cSheet2 As Variant = "Sheet2" ' Worksheet Name/Index
Const cCol2 As Variant = "D" ' Criteria Column Letter/Number
Const cValFirst2 As Variant = "H" ' First Value Column/Number
Const cFirstRow2 As Integer = 9 ' First Row
' Both
Const cValCols As Integer = 6 ' Number of Value Columns
Dim ws1 As Worksheet ' Source Worksheet
Dim ws2 As Worksheet ' Target Worksheet
Dim lngLast1 As Long ' Source Last Used Row
Dim lngLast2 As Long ' Target Last Used Row
Dim intFirst1 As Integer ' Source First Value Column Number
Dim intFirst2 As Integer ' Target First Value Column Number
Dim i As Long ' Source Row Counter
Dim j As Integer ' Source/Target Value Column Counter
Dim k As Long ' Target Row Counter
Dim lngTemp As Long ' Value Accumulator
Set ws1 = Worksheets(cSheet1)
Set ws2 = Worksheets(cSheet2)
' Calculate Last Used Rows.
lngLast1 = ws1.Cells(ws1.Rows.Count, cCol1).End(xlUp).Row
lngLast2 = ws2.Cells(ws2.Rows.Count, cCol2).End(xlUp).Row
' Calculate First Columns.
intFirst1 = ws1.Cells(1, cValFirst1).Column
intFirst2 = ws2.Cells(1, cValFirst2).Column
' Loop through cells (rows) of Target Criteria Column.
For k = cFirstRow2 To lngLast2
' Loop through Value Columns.
For j = 1 To cValCols
lngTemp = 0 ' Reset Value Accumulator.
' Loop through cells (rows) of Source Criteria Column.
For i = cFirstRow1 To lngLast1
' Check if criterias are equal.
If ws1.Cells(i, cCol1) = ws2.Cells(k, cCol2) Then
' Add value to Val7ue Accumlator
lngTemp = lngTemp + ws1.Cells(i, j + intFirst1 - 1)
End If
Next
' Write accumulated value to current target cell.
ws2.Cells(k, j + intFirst2 - 1) = lngTemp
Next
Next
End Sub