I'm am relatively familiar with Excel and its functions, but am very new to VBA (I do however have background in MATLAB and C). Basically what I have is a sheet with a different machine populating each column header and a name of an employee populating the first column. The table contains text values of either "Train", indicating that the person in that row is trained on the equipment in the specified column, or "No", indicating that they are not. What I want to do is to make a separate sheet that has the Equipment in the first column and one column headered as "Trained". Each cell will theoretically be populated with the names of the people who are trained on the equipment for that row. I have a for loop code in VBA that successfully outputs the names into the immediate window
Function Train(Data As Range, Name As Range)
For Counter = 1 To Data.Rows.Count
If Data(Counter, 1).Value = "Train" Then
'Debug.Print Name(Counter, 1)
End If
Next Counter
End Function
but I have been unable in a few hours of searching to figure out how to display these values in a single cell. Is this possible?
Thanks in advance!
You have to choose if you want to do "For each person, for each machine", or "for each machine, for each person" first. Let say you want to go with the second idea, you could go with this pseudo code:
set wsEmployee = Worksheets("EmployeeSheet")
set wsEmployee = Worksheets("MachineSheet")
'Clear MachineSheet and add headers here
xEmployee = 2
yMachine = 2
do while (wsEmployee.Cells(1, xEmployee).Value <> "") 'or your loop way here
yEmployee = 2
trained = ""
do while (wsEmployee.Cells(yEmployee, 1).Value <> "") 'or your loop way here
if (wsEmployee.Cells(yEmployee, xEmployee).Value = "Trained") then
trained = trained & wsEmployee.Cells(yEmployee, 1).Value & ", "
end if
yEmployee = yEmployee + 1
loop
'remove the last , in the trained string
wsMachine.Cells(yMachine, 1).Value = wsEmployee.Cells(1, xEmployee).Value
wsMachine.Cells(yMachine, 2).Value = trained
yMachine = yMachine + 1
xEmployee = xEmployee + 1
loop
That's the basic idea. For better performances, I would do all these operation in some arrays and paste them in one operation.
Use the concatenation operator (&) to assemble the values into a string:
Dim names as String
names = ""
For Counter = 1 To Data.Rows.Count
If Data(Counter, 1).Value = "Train" Then
If counter = 1 Then
names = names & Name(counter, 1)
Else
names = names & "," & Name(counter, 1)
End If
End If
Next Counter
Then just place names in whatever cell you want.
Related
I would briefly like to start off with I have never touched VBA let alone excel macros until a couple days ago.
I need to transfer and convert data of 1000 rows (4 columns) from one sheet (Sheet 1) to another (Sheet 2).
A quick description of what I'm given, each row is an object, I have 4 columns.
The first one (column) is the Object ID, the second one is the Object name, the third one explain the what of the object and the final column explains the how. This is a very simplified version as explaining the entire project would be complicated.
On the second sheet, I have 6000 rows all with the object's IDs and Names however the What and How are missing.
My goal is to take the what and how of an object from this sheet, convert the wording to a form in which the second sheet accepts and make sure it gets added to the proper ID.
I have tried multiple code samples I have found online to try and select and organize into tables (arrays) the information from the first sheet, I failed miserably.
Converting the What and How
The second sheet has a very strict format in which everything can be written. In my mind (Lua is my main language), I would have a dictionary or table with all possible ways of the How/What could be written on the first sheet and checking each one to see if they match then change it to the corresponding sheet 2 format. Let me show you. (This is the what. There'd be another table for the how which I'll show below)
local MType = {
["Industrial"] = {"MILPRO : Industrial","Industrial"};
["Public Saftey"] = {"MILPRO : Public Saftey", "Public Saftey"};
["Military"] = {"MILPRO : Military","Military"};
["Paddling"] = {"Recreation : Paddling","Paddling"};
["Sporting Goods"] = {"Recreation : Sporting Goods","Sporting Goods"};
["Outdoor"] = {"Recreation : Outdoor", "Outdoor"};
["Hook & Bullet"] = {"Recreation : Hook & Bullet", "Hook & Bullet"};
["Marine"] = {"Recreation : Marine","Marine","Marina / Lodge"};
["Sailing"] = {"Recreation : Sailing","Sailing"};
["Unknown"] = {"UNKNOWN"}
}
local CType = {
["Multi-Door"] = {"Multi-Door","Multi-door"};
["Dealer & Distributor"] = {"Distributor","Dealer & Distributor"};
["Independant Specialty"] = {"Independant Specialty","Specialty"};
["OEM"] = {"OEM","OEM - VAR"};
["Internal"] = {"Internal","Sales Agency","Repairs Facility"};
["Rental"] = {"Rental / Outfitter", "Rental"};
["End User"] = {"End User"};
["Institution"] = {"Institution","Government Direct"};
["Unknown"] = {"UNKNOWN"}
}
The first position in each table (table = the curly brackets) is the format in which the second sheet accepts. The rest in the tables is how they might be written in the first sheet. (This is how I imagine this would go down. Idk the functions and limits of VBA)
Matching the Information to the Proper IDs
Every object has an ID 6 characters long ranging from 000100 to 999999. When taking information from the first sheet, I need to make sure it gets placed back in the row with the right ID in the second sheet (Note there's 1000 rows on the first sheet and 6000 on the second sheet).
Final notes: The IDs are stored as text and not numbers (If they need to change lmk). Both sheet's information are within tables. I'll probably be using this method for other similar sheet 1s. Any conversions (for the what and how) that fail should be marked down as Unknown.
A Visual Representation of the 2 Sheets
Sheet 1 Format
Sheet 2 format
We can create a 2 dimensional array to hold all the pairs of one dictionary, then check against each element using a For..Next loop.
Sub transcribe()
On Error GoTo Handler
Application.ScreenUpdating = False
Dim WS1 As Worksheet, WS2 As Worksheet
Dim ID1 As Range, ID2 As Range
'This is assuming youre working in Sheets 1 and 2
Set WS1 = Sheets(1)
Set WS2 = Sheets(2)
'This is assuming your tables are in these locations
Set ID1 = WS1.Range(WS1.Cells(1, 1), WS1.Cells(10, 1))
Set ID2 = WS2.Range(WS2.Cells(1, 1), WS2.Cells(20, 1))
Dim cellx As Range
Dim rowID1 As Integer
Dim FieldA As String, FieldB As String
Dim IDfound As Boolean
IDfound = True
Dim arrayA(1 To 10, 1) As String
arrayA(1, 0) = "MILPRO : Industrial"
arrayA(1, 1) = "Industrial"
arrayA(2, 0) = "MILPRO : Public Saftey"
arrayA(2, 1) = "Public Saftey"
'... etc. You have to complete this array with all the pairs of your dictionary of Field A
'array(X, 1) holds what you expect to find in table 1, and array(X, 0) holds what you want to write down in table 2.
Dim arrayB(1 To 9, 1) As String
arrayB(1, 0) = "Multi-Door"
arrayB(1, 1) = "Multi-Door"
arrayB(2, 0) = "Distribuitor"
arrayB(2, 1) = "Dealer & Distribuitor"
'... etc. You have to complete this array with all the pairs of your dictionary of Field B
'array(X, 1) holds what you expect to find in table 1, and array(X, 0) holds what you want to write down in table 2.
'Now we sweep each cell in Table 2
For Each cellx In ID2.Cells
'And we search its ID for a match in Table 1.
rowID1 = Application.Match(cellx.Value, ID1, 0)
If IDfound = True Then
'We then write down the values of Field A and B in the found row
FieldA = ID1.Resize(1).Offset(rowID1 - 1, 2).Value
FieldB = ID1.Resize(1).Offset(rowID1 - 1, 3).Value
'And we call a function (see below) to correct their values
cellx.Offset(0, 2).Value = corrected(FieldA, arrayA, 10)
cellx.Offset(0, 3).Value = corrected(FieldB, arrayB, 9)
Else
cellx.Offset(0, 2).Value = "ID not found"
cellx.Offset(0, 3).Value = "ID not found"
IDfound = True
End If
Next
Application.ScreenUpdating = True
Exit Sub
Handler:
IDfound = False
Resume Next
End Sub
Function corrected(Field As String, arrayX As Variant, UB As Integer) As String
'This is the dictionary-like function
Dim found As Boolean
'We sweep each element in the dictionary array until we find a match
For i = 1 To UB
If Field = arrayX(i, 1) Then
corrected = arrayX(i, 0)
found = True
Exit Function
Exit For
End If
Next
'If no match was found, we will write that down in the result
If found = False Then
corrected = Field & " - Not found in dictionary"
Exit Function
End If
'This code should never be reached, its just for foolproofing
corrected = "Error"
End Function
I am new to macros in Excel, and I’m trying to speed up a process. I need to add a varying number of blank rows, if certain text is present in the cell above it. Not equal, but containing.
For example if A1 contains 'Apples', add two blank rows beneath. If A6 has 'Plums', add four blank rows beneath, etc.
What I have now is this:
For a=1 To ActiveSheet.Cells(Rows.Count,1).End(x1Up).Row
If ActiveSheet.Cells(a,1).Value = “Apples” Then
ActiveSheet.Rows(2).Insert
a = a+1
ELSE
If ActiveSheet.Cells(a,1).Value = “Plums” Then
ActiveSheet.Rows(4).Insert
a = a+1
End If
End Sub
So far I've gotten a Compile Error, stating "Block If without End If" though I believe I closed them both. I'm not sure if I'm correctly comparing or searching for a string as well (referring to my use of ="Apples"), but cannot get it to run at all to test that part.
For a = 1 To ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
If TypeName(ActiveSheet.Cells(a, 1)) = "String" Then
If ActiveSheet.Cells(a, 1).Value = "Apples" Then
ActiveSheet.Rows(2).Insert
a = a + 1
ElseIf ActiveSheet.Cells(a, 1).Value = "Plums" Then 'One error here
ActiveSheet.Rows(4).Insert
a = a + 1
End If
End If
Next 'And here too
I am naive to macros. I have data in column A with 500 rows of data. Note some rows are blank in between. The data are image files names like (X0011#00.jpg). I need to rename them sequentially with user input. The user input must be only 400500. The new name must be like (400500_Image-1.jpg). The _Image-1, _Image-2 and so on must be generated automatically in sequence omitting blank rows.
See below how my data is displayed in column A and how I want it in column B.
I appreciate if anyone can provide macro for this.
Col A Col B
X0011#00.jpg 400500_Image-1.jpg
X0021#00.jpg 400500_Image-2.jpg
X0041#00.jpg 400500_Image-3.jpg
X0071#00.jpg 400500_Image-4.jpg
X0051#00.jpg 400500_Image-5.jpg
X0031#00.jpg 400500_Image-6.jpg
X0061#00.jpg 400500_Image-7.jpg
X0091#00.jpg 400500_Image-8.jpg
Thanks
Sub naming()
RowsToProcess = Range("A" & Rows.Count).End(xlUp).Row
j = 1
userInput = InputBox("Give me text")
For i = 1 To RowsToProcess
If Not Cells(i, 1).Value = "" Then
Cells(i, 2).Value = userInput & "_Image-" & j & ".jpg"
j = j + 1
End If
Next
End Sub
This macro creates column B as desired
I'm trying to get excel to put together a series of text strings that haven't been formatted systematically, so that they end up split into different rows on a data sheet.
I'm aware this might've been solved elsewhere so sorry for that but I'm struggling to describe the issue, and I can't post images on it but basically it's
Column 1 with a list of the entries, and
Column 2 with text strings that are spread over 2 or more rows
Is it possible to write some kind of formula or macro that would be able to check the first column and then stitch together all entries in the second column going down until it found a new entry in the first column? I've got a feeling it might be possible using some sort of loop thing with index functions, but I've no idea where to start even.
Thanks,
Mike
Mike give this a ty
Sub appendValues()
'The sub is designed to loop through code and when ever there is a null value and column a it will take the value of what is in column B and appended to the row above it and delete the row.
Dim row As Integer
row = 1
'This code starts with row one but this can be changed at will.
Do Until ThisWorkbook.Sheets("sheet1").Cells(row, 2).Value = ""
'loop statement is designed to continue to Loop until there is a null value inside of you the value in the second column.
If ThisWorkbook.Sheets("sheet1").Cells(row, 1).Value = "" Then
ThisWorkbook.Sheets("sheet1").Cells(row - 1, 2).Value = ThisWorkbook.Sheets("sheet1").Cells(row - 1, 2).Value & ThisWorkbook.Sheets("sheet1").Cells(row, 2).Value
Rows(row).Delete
Else
'else statement is needed because there is an implied looping by decreasing the total number of rows after the delete.
row = row + 1
End If
Loop
End Sub
Sub appendValues()
'The sub is designed to loop through code and when ever there is a null value and column a it will take the value of what is in column B and appended to the row above it and delete the row.
Dim row As Integer
row = 1
'This code starts with row one but this can be changed at will.
Do Until ThisWorkbook.Sheets("sheet1").Cells(row, 2).Value = ""
'loop statement is designed to continue to Loop until there is a null value inside of you the value in the second column.
If ThisWorkbook.Sheets("sheet1").Cells(row, 1).Value = "" Then
ThisWorkbook.Sheets("sheet1").Cells(row - 1, 2).Value = ThisWorkbook.Sheets("sheet1").Cells(row - 1, 2).Value & ThisWorkbook.Sheets("sheet1").Cells(row, 2).Value
Rows(row).Delete
Else
'else statement is needed because there is an implied looping by decreasing the total number of rows after the delete.
row = row + 1
End If
Loop
End Sub
Please be aware that I am working with a series of ~1000 line medical information databases. Due to the size of the databases, manual manipulation of the data is too time consuming. As such, I have attempted to learn VBA and code an Excel 2010 macro using VBA to help me accomplish parsing certain data. The desired output is to split certain characters from a provided string on each line of the database as follows:
99204 - OFFICE/OUTPATIENT VISIT, NEW
will need to be split into
Active Row Active Column = 99204 ActiveRow Active Column+3 = OFFICE/OUTPATIENT VISIT, NEW
I have researched this topic using Walkenbach's "Excel 2013: Power Programming with VBA" and a fair amount of web resources, including this awesome site, but have been unable to develop a fully-workable solution using VBA in Excel. The code for my current macro is:
Sub EasySplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, "-", 2)
For a = 0 To 1
Cells(1, a + 3).Value = Trim(name(a))
Next a
End Sub
The code uses the "-" character as a delimiter to split the input string into two substrings (I have limited the output strings to 2, as there exists in some input strings multiple "-" characters). I have trimmed the second string output to remove leading spaces.
The trouble that I am having is that the output is being presented at the top of the activesheet, instead of on the activerow.
Thank you in advance for any help. I have been working on this for 2 days and although I have made some progress, I feel that I have reached an impasse. I think that the issue is somewhere in the
Cells(1, a + 3).Value = Trim(name(a))
code, specifically with "Cells()".
Thank you Conrad Frix!
Yah.. funny enough. Just after I post I have a brainstorm.. and modify the code to read:
Sub EasySplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, "-", 2)
For a = 0 To 1
ActiveCell.Offset(0, 3 + a).Value = Trim(name(a))
Next a
End Sub
Not quite the colkumn1,column4 output that I want (it outputs to column3,column4), but it will work for my purpose.
Now I need to incorporate a loop so that the code runs on each successive cell in the column (downwards, step 1) skipping all bolded cells, until it hits an empty cell.
Modified answer to modified request.
This will start on row 1 and continue until a blank cell is found in column A. If you would like to start on a different row, perhaps row 2 if you have headers, change the
i = 1
line to
i = 2
I added a check on the upper bound of our variant before doing the output writes, in case the macro is run again on already formatted cells. (Does nothing instead of erroring out)
Sub EasySplit()
Dim initialText As String
Dim i As Double
Dim name As Variant
i = 1
Do While Trim(Cells(i, 1)) <> ""
If Not Cells(i, 1).Font.Bold Then
initialText = Cells(i, 1).text
name = Split(initialText, "-", 2)
If Not UBound(name) < 1 Then
Cells(i, 1) = Trim(name(0))
Cells(i, 4) = Trim(name(1))
End If
End If
i = i + 1
Loop
End Sub
just add a variable to keep track of the active row and then use that in place of the constant 1.
e.g.
Dim iRow as Integer = ActiveCell.Row
For a = 0 To 1
Cells(iRow , a + 3).Value = Trim(name(a))
Next a
Alternate method utilizing TextToColumns. This code also avoids using a loop, making it more efficient and much faster. Comments have been added to assist with understanding the code.
EDIT: I have expanded the code to make it more versatile by using a temp worksheet. You can then output the two columns to wherever you'd like. As stated in your original question, the output is now to columns 1 and 4.
Sub tgr()
Const DataCol As String = "A" 'Change to the correct column letter
Const HeaderRow As Long = 1 'Change to be the correct header row
Dim rngOriginal As Range 'Use this variable to capture your original data
'Capture the original data, starting in Data column and the header row + 1
Set rngOriginal = Range(DataCol & HeaderRow + 1, Cells(Rows.Count, DataCol).End(xlUp))
If rngOriginal.Row < HeaderRow + 1 Then Exit Sub 'No data
'We will be using a temp worksheet, and to avoid a prompt when we delete the temp worksheet we turn off alerts
'We also turn off screenupdating to prevent "screen flickering"
Application.DisplayAlerts = False
Application.ScreenUpdating = False
'Move the original data to a temp worksheet to perform the split
'To avoid having leading/trailing spaces, replace all instances of " - " with simply "-"
'Lastly, move the split data to desired locations and remove the temp worksheet
With Sheets.Add.Range("A1").Resize(rngOriginal.Rows.Count)
.Value = rngOriginal.Value
.Replace " - ", "-"
.TextToColumns .Cells, xlDelimited, Other:=True, OtherChar:="-"
rngOriginal.Value = .Value
rngOriginal.Offset(, 3).Value = .Offset(, 1).Value
.Worksheet.Delete
End With
'Now that all operations have completed, turn alerts and screenupdating back on
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
You can do this in a single shot without looping using the VBA equivalent of entering this formula, then taking values only
as a formula
=IF(NOT(ISERROR(FIND("-",A1))),RIGHT(A1,LEN(A1)-FIND("-",A1)-1 ),A1)
code
Sub Quicker()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
With rng1.Offset(0, 3)
.FormulaR1C1 = "=IF(NOT(ISERROR(FIND(""-"",RC[-3]))),RIGHT(RC[-3],LEN(RC[-3])-FIND(""-"",RC[-3])-1 ),RC[-3])"
.Value = .Value
End With
End Sub