Here's my draft data
Sheet_name: "FIRST"
The data represents the Pass and fail of X & Y fields. If the Area fails when it comes to X it will be mark as F and if it pass, the field X will mark as P. Same procedure to Field Y
And
Sheet_name: "SECOND"
Here's the summary of the Sheet: "FIRST"
It calculates the counts of passes and fails.
Using the idea of Countif Function.
=COUNTIF(FIRST!B2:B5,"P")
=COUNTIF(FIRST!C2:C5,"F")
What I'm trying to do is,
When you try to click the counts of passes and fails. It will redirect you to new sheet where the sheet gives the data who are the areas passed and failed.
Example:
If I click the "3" under the field of Passed
It will give me something like this,
| X |
Area1 | p |
Area2 | p |
Area4 | p |
Sorry, this one is not my project, homework, or exam.
I just need to understand the logic of opening the data when you click a cell.
Cell-Click to Another Sheet
Copy the code into the Sheets("SECOND") sheet code (in VBA
double-click on "SECOND") and rename a sheet as "THIRD".
In sheet THIRD there will be 2 columns with headers AREA and X. The
headers are excluded from ClearContents.
Below the results will be either for Pass or Fail depending on which
cell was 'clicked' (selected) at the moment.
The Code
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Const cStrPass As String = "A3" ' Pass Cell Range
Const cStrFail As String = "B3" ' Fail Cell Range
If Target = Range(cStrPass) Then
CellClick Range("A3")
End If
If Target = Range(cStrFail) Then
CellClick Range("B3")
End If
End Sub
Sub CellClick(CellRange As Range)
Const cVntName1 As Variant = "FIRST"
Const cVntName3 As Variant = "THIRD"
Dim vntSrc As Variant ' Source Array
Dim vntTgt As Variant ' Target Array
Dim lngLastRow As Long ' Source Last Row
Dim i As Long ' Source Row Counter
Dim k As Long ' Target Row Counter
Dim j As Integer ' Source/Target Column Counter
Dim strPF As String ' PassFail String
' Paste Source Range into Source Array.
With Worksheets(cVntName1)
lngLastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
vntSrc = .Range("A2", .Cells(lngLastRow, "B"))
End With
' Determine PassFail String.
If CellRange.Column = 1 Then
strPF = "P"
Else
strPF = "F"
End If
' Count rows for Target Array
For i = 1 To UBound(vntSrc)
If vntSrc(i, 2) = strPF Then
k = k + 1
End If
Next
' Write data to Target Array
ReDim vntTgt(1 To k, 1 To 2)
k = 0
For i = 1 To UBound(vntSrc)
If vntSrc(i, 2) = strPF Then
k = k + 1
For j = 1 To UBound(vntSrc, 2)
vntTgt(k, j) = vntSrc(i, j)
Next
End If
Next
' Paste Target Array into Target Range.
With Worksheets(cVntName3)
.Range("A2", "B" & .Rows.Count).ClearContents
.Range("A2").Resize(UBound(vntTgt), UBound(vntTgt, 2)) = vntTgt
.Select
End With
End Sub
Related
I'm novice in VBA. I'm trying to transpose my data:
I would like to have this results:
I tried all day the methodes like: Resize(UBound(Table2, 1), UBound(Table2, 2)) = Table2, Application.transpose(Tbl1) but I don't have the diserid result. Could you help me please? Thank you very much!
A Power Query Solution (CVR)
Added corrections; credits to Ron Rosenfeld.
Click into your table.
Select Data > From Table/Range: The Power Query Editor opens containing your data.
The first columns is selected. If not, click the header of your first column (Date) to select it.
Select Transform > Pivot Column: The Pivot Column window opens.
In the Values Column combo box the second column (Values) is already selected. If not, select it.
Click Advanced Options where Sum is already selected which will sum multiple entries for the same ID/Date columns. If not, select it.
Press OK. The data is transformed.
Select Home > Close & Load: The Power Query Editor closes and the transformed data is presented in a table in a new worksheet.
Short Version
Click into your table.
Select Data > From Table/Range: The Power Query Editor opens containing your data.
Select Transform > Pivot Column: The Pivot Column window opens.
Press OK. The data is transformed.
Select Home > Close & Load: The Power Query Editor closes and the transformed data is presented in a table in a new worksheet.
With Office365 you can use below formulas (as per my screenshot).
F2=UNIQUE(C1:C11)
G1=TRANSPOSE(SORT(UNIQUE(A1:A10)))
G2=FILTER($B$1:$B$11,($C$1:$C$11=$F2)*($A$1:$A$11=G$1),"")
After putting FILTER() formula to G2 cell drag across right and down as needed.
You can also use XLOOKUP() instead of FILTER() formula to G2 like-
=XLOOKUP(1,($A$1:$A$11=G$1)*($C$1:$C$11=$F2),$B$1:$B$11,"")
Pivot CVR
CVR: Column Labels, Values, Row Labels.
It is assumed that the initial data, the Source Range, contains a row of headers, whose third cell value will be copied to the first cell of the resulting data, the Destination Range.
Adjust the values in the constants section.
Copy the complete code to a standard module, e.g. Module1.
Only run the first procedure, pivotDataCVR, the other two are being called by it, when necessary.
A similar solution, which I based this solution on, although RCV, can be found here.
The Code
Option Explicit
Sub pivotDataCVR()
' Define constants.
Const srcName As String = "Sheet1"
Const srcFirst As String = "A1"
Const dstName As String = "Sheet2"
Const dstFirst As String = "A1"
' Define workbook.
Dim wb As Workbook
Set wb = ThisWorkbook
' Define Source First Cell Range.
Dim cel As Range
Set cel = wb.Worksheets(srcName).Range(srcFirst)
' Define Source Range.
Dim rng As Range
With cel.CurrentRegion
Set rng = cel.Resize(.Rows.Count + .Row - cel.Row, _
.Columns.Count + .Column - cel.Column)
End With
' Get unique values.
Dim dts As Variant
dts = getUniqueColumn1D(rng.Columns(1).Resize(rng.Rows.Count - 1).Offset(1))
sort1D dts
Dim idx As Variant
idx = getUniqueColumn1D(rng.Columns(3).Resize(rng.Rows.Count - 1).Offset(1))
sort1D idx
' Write values from Source Range to Source Array.
Dim Source As Variant
Source = rng.Value
' Define Destination Array.
Dim Dest As Variant
ReDim Dest(1 To UBound(idx) - LBound(idx) + 2, _
1 To UBound(dts) - LBound(dts) + 2)
' Write values from arrays to Destination Array.
Dest(1, 1) = Source(1, 3)
Dim n As Long
Dim i As Long
i = 1
For n = LBound(idx) To UBound(idx)
i = i + 1
Dest(i, 1) = idx(n)
Next n
Dim j As Long
j = 1
For n = LBound(dts) To UBound(dts)
j = j + 1
Dest(1, j) = dts(n)
Next n
For n = 2 To UBound(Source, 1)
i = Application.Match(Source(n, 3), idx, 0) + 1
j = Application.Match(Source(n, 1), dts, 0) + 1
Dest(i, j) = Source(n, 2)
Next n
' Define Destination First Cell Range.
Set cel = wb.Worksheets(dstName).Range(dstFirst)
' Define Destination Range.
Set rng = cel.Resize(UBound(Dest, 1), UBound(Dest, 2))
' Write from Destination Array to Destination Range.
rng.Value = Dest
' Inform user.
MsgBox "Data transferred.", vbInformation, "Success"
End Sub
' Returns the unique values from a column range in a 1D array.
Function getUniqueColumn1D(ColumnRange As Range, _
Optional ByVal Sorted As Boolean = False) _
As Variant
Dim Data As Variant
Data = ColumnRange.Columns(1).Value
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
Dim Key As Variant
Dim i As Long
For i = 1 To UBound(Data, 1)
Key = Data(i, 1)
If Not IsError(Key) And Not IsEmpty(Key) Then
.Item(Key) = Empty
End If
Next i
If .Count > 0 Then
getUniqueColumn1D = .Keys
End If
End With
End Function
' Sorts a 1D array only if it contains values of the same data type.
Sub sort1D(ByRef OneD As Variant, _
Optional ByVal Descending As Boolean = False)
With CreateObject("System.Collections.ArrayList")
Dim i As Long
For i = LBound(OneD) To UBound(OneD)
.Add OneD(i)
Next i
.Sort
If Descending Then
.Reverse
End If
OneD = .ToArray
End With
End Sub
I have a set of x names (in row 4) with corresponding dates (row 3) (the combination of name and date is unique).
I would like to copy the unique name and date, and then paste it x times (where x is the total number of names) in a different sheet.
I would like the code to loop through all names and dates and paste them within column A,B in a new sheet. Where column A has heading name and column B has heading date.
Initial data:
After Code:
What I have attempted so far - i can't seem to get the paste correct
Sub Test()
Dim o As Variant
Dim CountC_Range As Range
Dim cel_3 As Range
Dim MyRange As Range
'count the number of different engagement areas
Worksheets("Sheet8").Activate
Range("B4").Select
Set CountC_Range = Range("B4", Selection.End(xlToRight))
'Set the letter k as number of engagements as we'll use this later
o = WorksheetFunction.CountA(CountC_Range) - "1"
Worksheets("sheet9").Activate
Range("A1").Select
MyRange = Range("Selection.End(xlDown) + 1", "Selection.End(xlDown) + o + 1")
For Each cel_3 In Worksheets("Sheet8").Range("4:4")
If cel_3.Value <> "" Then
MyRange = cel_3.Value
End If
Next cel_3
End Sub
There are plenty of ways to do it, but having this input:
The code below will provide this:
Sub TestMe()
With Worksheets("Source")
Dim k As Long
k = .Range("A4").End(xlToRight).Column
End With
With Worksheets("Target")
Dim i As Long, ii As Long
Dim currentRow As Long
For i = 1 To k
For ii = 1 To k
currentRow = currentRow + 1
.Cells(currentRow, "A") = Worksheets("Source").Cells(3, i)
.Cells(currentRow, "B") = Worksheets("Source").Cells(4, i)
Next
Next
End With
End Sub
Dependencies:
Name the input worksheet "Source"
Name the output worksheet "Target"
A must read - How to avoid using Select in Excel VBA
I have an Excel table which may contain such:
Screenshot of content from a table, columns C and D
It may be much longer
on top of column D may be an empty cell, but after that it is always the same sequence of contents repeating.
I want to copy and paste in another sheet, with transpose, the contents of the neighboring cells, that is in C, so it would look like:
a screenshot from destination table
It is easy to copy the header, but I am completely unable to have the code loop through and copy all the column C contents that appear left to what is between 1tst and 27tst in the original column D, until all of the blocks of data are copied.
To complicate things even further, I want all empty cells in this destination table to take the value from the cell above, basically filling the blanks that way. This would then look like
Final look of the destination table
In this example, the Words "Algeria | DZ" have to be automatically copied down. The cell under "24tst" remains blank as there is nothing but the header preceding this row.
I have absolutely no starting code here, as these data already made a long process from a Word file through a csv using Ruby, and then the csv is read in and reformatted into various sheets in the Excel file with already long line sof code. That all works so far, but these are my missing steps.
Any help is greatly appreciated. I only started coding again 3 weeks ago, after having never programmed in VBA but years ago in perl and R.
-- In response to VBasic2008 and to try that out I made now a test spreadsheet that looks this way:this is closer to what it really looks like
I changed the constants here:
enter code hereConst sName As String = "Tabelle1" ' Source Worksheet Name
enter code hereConst sFirst As String = "C2" ' Source First Cell Address
enter code hereConst tName As String = "Tabelle2" ' Target Worksheet Name
enter code hereConst tFirst As String = "B1" ' Target First Cell Address
The groups will actually be constant in length, actually more than 11, but that can be fixed later.
These:
1tst
2tst
3tst
11tst
4tst
22tst
23tst
24tst
25tst
26tst
27tst -
I pasted this already into target sheet.
What I get from my test using my thus modified solution from VBasic2008 is this:
Afghanistan | AF Ă…land Islands | AX Albania | AL Algeria | DZ American Samoa | AS Belgium | BE Belize | BZ 24tst Bermuda | BM Bhutan | BT Bolivia | BO
Bonaire, Sint Eustatius and Saba | BQ Bosnia and Herzegovina | BA Botswana | BW Algeria | DZ Brazil | BR Christmas Island | CX Cocos (Keeling) Islands | CC Colombia | CO Comoros | KM n/a Congo | CD
This is almost perfect, except for it should not, in the first row in the target sheet after the headers, copied down the "24tst". Can this still be tweaked?
A Copy Transpose
This will work correctly only if the data is consistent i.e. 11 rows of data and 1 empty (Next-Group) row (can be changed in the constants section) i.e. if you have 5 data sets, there has to be 60 rows of data. If there is 65, only 60 will be processed and if there is 59, only 48 will be processed.
The following image shows what the current setup in the code will produce (without the formatting).
The Code
Option Explicit
Sub transposeData()
Const sName As String = "Sheet1" ' Source Worksheet Name
Const sFirst As String = "A2" ' Source First Cell Address
Const tName As String = "Sheet1" ' Target Worksheet Name
Const tFirst As String = "D1" ' Target First Cell Address
Const NoE As Long = 11 ' Number of Elements
Const NoER As Long = 1 ' Number of Empty Rows
Dim wb As Workbook
Set wb = ThisWorkbook ' The workbook containing this code.
' Define Source Worksheet ('ws').
Dim ws As Worksheet
Set ws = wb.Worksheets(sName)
' Define Source First Cell ('First').
Dim First As Range
Set First = ws.Range(sFirst)
' Define Source Last Cell ('Last').
Dim Last As Range
Set Last = First.Offset(ws.Rows.Count - First.Row, 1).End(xlUp)
If Last.Row - First.Row + 1 < NoE Then
GoTo ProcExit
End If
' Define Source Range ('rng').
Dim rng As Range
Set rng = ws.Range(First, Last)
' Write values from Source Range to Source Array ('Source').
Dim Source As Variant
Source = rng.Value
' Define number of Data Sets ('NoDS').
Dim NoDS As Long
NoDS = Int(UBound(Source, 1) / (NoE + NoER))
' Define Target Number of Rows ('NoR').
Dim NoR As Long
NoR = NoDS + 1
' Define Target Array ('Target').
Dim Target As Variant
ReDim Target(1 To NoR, 1 To NoE)
' Declare additional variables for the upcoming loops.
Dim CurrentValue As Variant ' Source Current Value
Dim CurrentLR As Long ' Source Current Last Row
Dim j As Long ' Target Columns Counter
Dim i As Long ' Target Rows Counter
' Write headers.
For j = 1 To NoE
Target(1, j) = Source(j, 2)
Next j
' Write data.
For i = 2 To NoR
CurrentLR = (i - 2) * (NoE + NoER)
For j = 1 To NoE
CurrentValue = Source(CurrentLR + j, 1)
If Not IsEmpty(CurrentValue) Then
Target(i, j) = CurrentValue
Else
Target(i, j) = Target(i - 1, j)
End If
Next j
Next i
' Define Target Worksheet ('ws').
Set ws = wb.Worksheets(tName)
' Define Target First Cell ('First').
Set First = ws.Range(tFirst)
' Define Target Range ('rng').
Set rng = First.Resize(NoR, NoE)
' Write values from Target Array to Target Range.
rng.Value = Target
' Inform user
MsgBox "Data transferred.", vbInformation, "Success"
ProcExit:
End Sub
EDIT
Tiny Change
Instead of Target(i, j) = Target(i - 1, j) use
If i > 2 Then
Target(i, j) = Target(i - 1, j)
End If
I think the easiest way of doing this is looping through cells with headers and checking each value.
When you find your "next-group" cell then trigger some ifs;
Example program which covers your problem below:
Sub solution()
'Set first row
Dim firstrow As Integer
firstrow = 1
'Find last row
Dim lastrow As Integer
lastrow = Cells(Rows.Count, 1).End(xlUp).Row 'Go to bottom of file and jump up to last non-empty cell
'Set first column
Dim firstcolumn As Integer
firstcolumn = 1
'find last column
Dim lastcolumn As Integer
lastcolumn = 2
'Set first cell of target table
Dim targetrange As Range
Set targetrange = Range("E2")
Dim i As Integer
Dim cnt As Integer 'conuter for creating offset (for columns)
Dim cnt2 As Integer 'conuter for creating offset (for rows)
'Copy headers
cnt = 0
For i = firstrow To lastrow
If Cells(i, lastcolumn).Value = "next-group" Then Exit For
Cells(i, lastcolumn).Copy targetrange.Offset(0, cnt)
cnt = cnt + 1
Next i
'Copy data
cnt = 0
cnt2 = 1
For i = firstrow To lastrow
'If we have text "next group"
If Cells(i, lastcolumn).Value = "next-group" Then
cnt = 0 'start with first column
cnt2 = cnt2 + 1 'Start with next row
'This cell is not copied
Else
'cell is copied
Cells(i, firstcolumn).Copy targetrange.Offset(cnt2, cnt)
'column counter is increased
cnt = cnt + 1
End If
Next i
'Change blank cells in current region into formula which points to cell one row above
'targetrange.CurrentRegion.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
'Same formula but keep cells in first row of data blank istead copying header
Dim targetArea As Range
Set targetArea = targetrange.CurrentRegion
targetArea.Offset(2).Resize(targetArea.Rows.Count - 2).SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
End Sub
I didn't cover case when you have empty cell in first row as you didn't described what you're expecting (at this moment it have same formula so it will be filled with header value).
UPDATE: I didnt put "=" inside R1C1 formula, now its fixed.
UPDATE2: Changed part of filling empty cells so it skips first 2 rows (Headers and first row of data) instead filling it as mentioned in question update
Having an issue looping through application match.
I get an error Object required on line K.Offset(0, 1).Copy FV.Offset(2, 0)
The code should
1) loop through CS range,
2) where CS matches in range FV,
3) input the cells from CS Offset(0,1) into FV 2 columns over, Offset(2,0).
Here is my full code:
Sub n()
Dim FV As Variant
Dim CS As Variant
Dim K As Variant
FV = Sheets("NEW").Range("A28:A34").Value
CS = Sheets("CS").Range("A1:L1").Value
For Each K In CS
If Not IsError(Application.Match(CS, FV, 0)) Then
K.Offset(0, 1).Copy FV.Offset(2, 0)
Else:
End If
Next K
End Sub
You could use a pure VBA function like:
Sub CopyMatchingValues()
Dim FV As Range
Dim CS As Range
Dim cellFV As Range
Dim cellCS As Range
Set FV = Sheets("NEW").Range("A28:A34")
Set CS = Sheets("CS").Range("A1:L1")
For Each cellCS In CS.Cells
For Each cellFV In FV.Cells
If cellFV.Value = cellCS.Value Then
cellFV.Offset(2, 0).Value = cellCS.Offset(0, 1).Value
End If
Next
Next
End Sub
What a Match feat. the FirstMatch Issue
Option Explicit
Sub XMatch()
Const FirstMatch As Boolean = True
Dim FV As Variant ' Search Array (Vertical)
Dim CS As Variant ' Source Array (Horizontal)
Dim K As Variant ' Target Array (Vertical)
Dim iFV As Integer ' Search Array Rows Counter
Dim iCS As Integer ' Source Array Columns Counter
' Paste ranges into arrays.
FV = Sheets("NEW").Range("A28:A34").Value ' Search Array = Search Range
CS = Sheets("CS").Range("A1:L2").Value ' Source Array = Source Range
' The Target Array is the same size as the Search Array.
ReDim K(1 To UBound(FV), 1 To 1)
' ReDim K(LBound(FV, 1) To UBound(FV, 1), LBound(FV, 2) To UBound(FV, 2))
' Loop through first and only COLUMN of first dimension of Search Array.
For iFV = 1 To UBound(FV)
' For iFV = LBound(FV, 1) To UBound(FV, 1)
' Loop through first ROW of second dimension of Source Array.
For iCS = 1 To UBound(CS, 2)
' For iCS = LBound(CS, 2) To UBound(CS, 2)
If FV(iFV, 1) = CS(1, iCS) Then
' Match is found, read from second ROW of the second dimension of Source
' Array and write to first and only COLUMN of first dimension of Target
' Array.
K(iFV, 1) = CS(2, iCS)
' Check True/False
If FirstMatch Then
' When FirstMatch True, stop searching.
Exit For
' Else
' When FirstMatch False, try to find another match to use as result.
End If
' Else
' Match is not found.
End If
Next
Next
' Paste Target Array into Target Range, which is two columns to the right of
' Search Range.
Sheets("NEW").Range("A28:A34").Offset(0, 2) = K ' Target Range = Target Array
End Sub
I have multiple cells with string like
for. ex cel A1
m2_10cm[0.10],m2_20cm[0.20],m2_5cm[0.05],m3[1.9]
and cell A2
m3_22[2.2],m3_19[1.9]
Hov can i split it to one column like
Column B
Cell 1 m2_10cm[0.10]
Cell 2 m2_20cm[0.20]
Cell 3 m2_5cm[0.05]
Cell 4 m3[1.9]
Cell 5 m3_22[2.2]
Cell 6 m3_19[1.9]
I will be apriciate for any help
Using VBA code in Excel:
note: google how to a command button on the sheet and paste this as the code.
Option Explicit
' note: vbNullString is the same as "" (empty string)
Const START_ROW = 1
Const SRC_COL = 1 ' column A
Const DST_COL = 2 ' column B
' this gets triggered when the button is pressed
Private Sub CommandButton1_Click()
' call the routine
Call Go
End Sub
Function Go()
' assume the button is on the sheet to be processed
Dim ws As Excel.Worksheet
Set ws = Excel.ActiveSheet
Dim srcRow As Integer ' current row being processed
Dim dstRow As Integer ' current row to put result in
srcRow = START_ROW: dstRow = START_ROW
' keep going while column 'A' is not blank
While ws.Cells(srcRow, SRC_COL) <> vbNullString
Call Split(ws, ws.Cells(srcRow, SRC_COL), dstRow)
srcRow = srcRow + 1
Wend
End Function
Sub Split(ws As Excel.Worksheet, srcStr As String, ByRef dstRow As Integer)
If (srcStr = vbNullString) Then
'remove comment if you want blanks at the end
' ex. Apple,Banana,
' will create 3 entries, notice the comma at the end
'ws.Cells(dstRow, DST_COL) = vbNullString
'dstRow = dstRow + 1
Exit Sub
endif
' find ","
Dim pos As Integer
pos = InStr(1, srcStr, ",")
If (pos = 0) Then
' no "," - put the whole string
ws.Cells(dstRow, DST_COL) = Trim(srcStr)
dstRow = dstRow + 1
Else
' has "," - put the left part of the string
' ex: apple,banana,carrot
' put "apple"
' continue processing "banana,carrot"
ws.Cells(dstRow, DST_COL) = Trim(Mid(srcStr, 1, pos - 1))
' move to next row and process the right of the string
dstRow = dstRow + 1
Call Split(ws, Mid(srcStr, pos + 1), dstRow)
End If
End Sub
Here is a solution where you need to have every other column hidden:
Say that you have the value in A1 then put in the following formulas:
B1: =IF(ISERR(FIND(",";A1;1));A1;LEFT(A1;FIND(",";A1;1)-1))
C1: =IF(ISERR(FIND(",";A1;1));"";RIGHT(A1;LEN(A1)-FIND(",";A1;1)))
B1 will then contain the first value in the list, C1 will contain the list minus the first value. Now you can copy these formulas to D1 and E1 and they will now look like
D1: =IF(ISERR(FIND(",";C1;1));C1;LEFT(C1;FIND(",";C1;1)-1))
E1: =IF(ISERR(FIND(",";C1;1));"";RIGHT(C1;LEN(C1)-FIND(",";C1;1)))
Now keep on copying this formulas for as long to the right as you need.
Once this is done you can hide all columns that contain the shortened list, starting with C, then E, etc.