I have a gnarly amount of data across many sheets in LibreOffice -- an ADDRESS column and a DATA column -- and I'd like to count the number of times each address occurs, put into a NUM_ADDR column. E.g.:
ADDR | DATA | NUM_ADDR
00000000bbfe22d0 | 876d4eb163886d4e | 1
00000000b9dfffd0 | 4661bada6d4661ba | 1
00000000b9dfc3d0 | 5d4b40b4705d4b40 | 1
00000000b9def7d0 | 8f8570a5808f8570 | 1
00000000b9de17d0 | 63876d4eb163886d | 1
00000000b9dddfd0 | 6d4eb163886d4eb1 | 3
00000000b9dddfd0 | 705d4b40b4705d4b |
00000000b9dddfd0 | b4705d4b40b4705d |
00000000b7df83d0 | 40b4705d4b40b470 | 1
00000000b7d607d0 | 705d4b40b4705d4b | 1
...
When doing things manually I used the COUNTIF function on each address, but I've found that a macro would save time in the long run. Here's a snippet of what I have so far, given that a previous function has already determined the length (number of rows) of the data, stored in RowCounter:
Dim CountedAddr(RowCounter, RowCounter) as String
Dim CountedAddrPtr as Integer
Dim CurrentCell as Object
Dim i as Integer
CountedAddrPtr = 0
' Populate CountedAddr array
For i = 1 to RowCounter-1
CurrentCell = CurrentSheet.getCellByPosition(0, i)
If Not CurrentCell.String In CountedAddr(?) Then
CurrentSheet.getCellByPosition(2, i).Value = 1 ' for debugging
CountedAddr(CountedAddrPtr, 0) = CurrentCell.String
CountedAddrPtr = CountedAddrPtr + 1
Else
CurrentSheet.getCellByPosition(2, i).Value = 0 ' for debugging
EndIf
Next
' For each unique address, count number of occurances
For i = 0 to UBound(CountedAddr())
For j = 1 to RowCounter-1
If CurrentSheet.getCellByPosition(0, j).String = CountedAddr(i, 0) Then
CountedAddr(i, 1) = CountedAddr(i, 1)+1
EndIf
Next
Next
' Another function to populate NUM_ADDR from CountedAddr array...
So my first question is: how can we determine if an element (the address in the current cell) is in the CountedAddr array (see the (?) above)? Second, is there a much more efficient way to achieve the second block of code? Unfortunately sorting is out of the question, since the chronology of the addresses and data form something of a time base. Third, is the whole shebang a foolish way to attack this problem?
Many thanks from a hardware dood on a software task!
Dictionary-type objects such as a VB6 Collection are efficient for looking up items, because it finds the key directly rather than looping through a long array. Our countedAddrs collection below will store a count for each address.
Sub CountAddrs
Dim countedAddrs As New Collection
Dim oCurrentSheet As Object
Dim oCurrentCell As Object
Dim currentAddr As String
Dim i As Integer
Dim newCount As Integer
Dim rowCounter As Integer
Const ADDR_COL = 0
Const COUNT_COL = 2
oCurrentSheet = ThisComponent.CurrentController.ActiveSheet
rowCounter = 11
' Populate countedAddrs array.
For i = 1 to rowCounter - 1
oCurrentCell = oCurrentSheet.getCellByPosition(ADDR_COL, i)
currentAddr = oCurrentCell.String
If Contains(countedAddrs, currentAddr) Then
' Increment the count.
newCount = countedAddrs.Item(currentAddr) + 1
countedAddrs.Remove(currentAddr)
countedAddrs.Add(newCount, currentAddr)
oCurrentSheet.getCellByPosition(COUNT_COL, i).Value = newCount ' for debugging
Else
countedAddrs.Add(1, currentAddr)
oCurrentSheet.getCellByPosition(COUNT_COL, i).Value = 1 ' for debugging
EndIf
Next
End Sub
This code requires the following helper function. In most languages, dictionary objects have this functionality built-in, but Basic is rather simplistic.
' Returns True if the collection contains the key, otherwise False.
Function Contains(coll As Collection, key As Variant)
On Error Goto ErrorHandler
coll.Item(key)
Contains = True
Exit Function
ErrorHandler:
Contains = False
End Function
Related
How do I size arrays dynamically while trying to assign values to individual elements of the array from the sheet? In columns A and B I have
A B
1 Houston
2 Miami
3 New York
4 Toronto
5 Los Angeles
I want the VBA to determine the number of elements and size the arrays based on how many elements are there. Then, the defined array gets the values from column B assigned to the elements. In the code below I am trying a For loop to get the values and assign them to each of the elements.
Here is the code I have:
Sub getNames()
Dim n As Integer 'denotes the number of elements
Dim i As Integer 'index
Dim Name() As String
Dim flag As Boolean
'Initialize values
i = 0
n = 0
flag = True
'For loop to determine number of elements
While flag = True
'check if the current cell has data in it
If Cells(i + 1, 1) <> "" Then
i = i + 1
Else
flag = False
End If
Wend
n = i
ReDim Name(n)
For i = 1 To n
Name(i) = cells(i,2).value
Next i
End Sub
However, I keep getting Syntax Error when trying to assign the value from the Cell.
Declare Name as variant
Dim Name as Variant
Then fill it in 3 lines:
With ActiveSheet 'Should change to the sheet in question; WorkSheets("Sheet1")
Name = .Range("B1", .Cells(.Cells(.Rows.Count,1).End(xlup).Row,2)).Value
End With
Maybe this post is a tad long but I hope you will read it and have some input for me.
I have been working to make a user form for Welder Qualification Test Records (WQTR). Since the form is 107 fields, I have created a fairly involved Module that allows the user to save their progress. Rather type lengthy explanations, I'm adding the completed Module here. I have made detailed comments in the code but let me know if something needs explaining.
Everything compiles fine and it does what I expect it to do with one exception. I assumed (not sure why) that the order the TextBoxes are being read by the script and entered into the worksheet (all in one row) would be in sync with the tab order. However, that is not case. In fact, I can see no particular order at all. I think this has something to do with how I am creating the headers for columns based on the Labels in the user form, but I'm not sure. I did make sure that the labels are in the proper order in the tab order even though I set all label's TabStop properties for to False.
Below is the code I have in the module. I have several other modules but none of them should have any bearing on this issue.
Option Explicit
Dim ws As Worksheet
Dim welderNameEntered As String
Dim welderName_ As Variant
Dim welderFirstName As String
Dim welderMiddlename As String
Dim welderLastName As String
Dim sheetName As String
Dim arrayLength As Integer
'********************************************************************************
'***Controls the order of execution in this module for all Subs and Functions.***
'********************************************************************************
Public Sub TempSaveProgress()
Application.ScreenUpdating = False
Call SplitName
funcCheckAndAddNewSheet sheetName
Call SaveData
Call Protection.DangerMouse(sheetName)
Application.ScreenUpdating = True
ActiveWorkbook.Save
End Sub
'************************************************************************************
'***Splits the Welders's first and last names by the space between them and grabs****
'***the first three characters of each. Sets the value of the sheetname variable***
'************************************************************************************
Sub SplitName()
welderNameEntered = WQTR_Form.welderNameText.Value
welderName_ = Split(welderNameEntered, " ")
Dim arrayLength As Integer
arrayLength = UBound(welderName_) - LBound(welderName_) + 1
Dim answer As Long
If arrayLength = 0 Then
Call ArrayLengthZero
Exit Sub
ElseIf arrayLength = 1 Then
Call ArrayLengthOneAndThree
Exit Sub
ElseIf arrayLength = 2 Then
welderFirstName = Left(welderName_(0), 3)
welderLastName = Left(welderName_(1), 3)
sheetName = "Temp-" + welderLastName + "-" + welderFirstName
ElseIf arrayLength = 3 Then
welderFirstName = Left(welderName_(0), 3)
welderMiddlename = Left(welderName_(1), 1)
welderLastName = Left(welderName_(2), 3)
sheetName = "Temp-" + welderLastName + "-" + welderFirstName + "-" + welderMiddlename
ElseIf arrayLength > 3 Then
Call ArrayLengthOneAndThree
Exit Sub
End If
End Sub
'**************************************************************************************
'***Adds and new worksheet after all other worksheets and gives it a temporary name.***
'**************************************************************************************
Function funcCheckAndAddNewSheet(argCheckAndAdd As String)
For Each ws In ThisWorkbook.Worksheets
If argCheckAndAdd = ws.Name Then
Call SheetNameAlreadyExists
End If
Next ws
If sheetName <> "" Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = argCheckAndAdd
End If
End Function
'*****************************************
'***Message if the arrayLength is zero ***
'*****************************************
Sub ArrayLengthZero()
Dim answer As Long
answer = MsgBox("You must enter a welder's name in order to Save Your Progress.", vbOKOnly, "No name?")
End Sub
'**************************************************
'***Message if the arrayLength is One or Three. ***
'**************************************************
Sub ArrayLengthOneAndThree()
Dim answer As Long
answer = MsgBox("The Welder's Name you entered is not valid. The name must conform to one these examples:" + _
vbNewLine + vbNewLine + _
" 1. FirstName LastName as in John Doe." + vbNewLine + _
" 2. FirstName MiddleName LastName as in Franklin Deleno Roosevilt." + vbNewLine + _
" 3. FirstName MiddleInitial LastName as in Joe D. Public." + vbNewLine + vbNewLine + _
"You also must make sure that names are no more than three names. " + _
"A name such as Roy Wayne David Johnson will not work. " + _
"In such cases, one of the two middle names must be omitted." _
, vbOKOnly, "Name is incorrect")
End Sub
'******************************************************************************
'***Message if sheetName matches the name of an already existing worksheet. ***
'******************************************************************************
Sub SheetNameAlreadyExists()
Dim answer As Long
answer = MsgBox("A WorkSheet by by the name " + sheetName + " already exists." + _
" Did you already Save Progress for this welder on another occasion?" + _
" If so, would you like to overwrite the data in the Worksheet named " + _
sheetName + "?", vbYesNo, sheetName + " Already Exists.")
If answer = vbYes Then
Call SafeMouse
Worksheets(sheetName).Activate
Application.DisplayAlerts = False
Worksheets(sheetName).Delete
Application.DisplayAlerts = True
Exit Sub
Else
Exit Sub
End If
End Sub
'****************************************************************************************
'***Sets the Active Sheet for all of the subs it calls. Again, this basically ***
'***controls the order of execution. Then does some minor worksheet level formatting. ***
'****************************************************************************************
Private Sub SaveData()
Worksheets(sheetName).Activate
Call LabelNames
Call LabelCaptions
Call TextBoxText
Call DeleteEmptyColumns
'-----Worksheet-level Formatting-----
Worksheets(sheetName).range("A1:DD1").Font.Bold = True
Worksheets(sheetName).Columns("A:DD").AutoFit
End Sub
'***************************************************************************************
'***Takes the names of all of the form lables and enters them in the first row of the***
'***active sheet. ***
'***************************************************************************************
Private Sub LabelNames()
Dim ctlLblName As control
Dim col As Integer: col = 0
For Each ctlLblName In WQTR_Form.Controls
If TypeName(ctlLblName) = "Label" Then
col = col + 1
Cells(1, col).Value = ctlLblName.Name
Cells(1, col).Interior.ColorIndex = 15
End If
Next ctlLblName
End Sub
'*******************************************************************************************
'***Takes the captions of all of the form lables and enters them in the second row of the***
'***active sheet. ***
'*******************************************************************************************
Private Sub LabelCaptions()
Dim ctlLblCaption As control
Dim col As Integer: col = 0
For Each ctlLblCaption In WQTR_Form.Controls
If TypeName(ctlLblCaption) = "Label" Then
col = col + 1
Cells(2, col).Value = ctlLblCaption.Caption
Cells(2, col).Interior.ColorIndex = 6
End If
Next ctlLblCaption
End Sub
'***************************************************************************************************
'***The Label Names and the TextBox Names were made to be identical except for the last part of ***
'***the names which are "Label" and "Text", respectively. This code finds all TextBox Names, ***
'***strips "Text" out of the TexBox Name and replaces it with "Label" which makes it identical ***
'***to the Label Name. Then it searches for the label name in the active sheet. When a match ***
'***found it inserts the TextBox.Text Value (entered by the user) in the cell in row three. ***
'***************************************************************************************************
Private Sub TextBoxText()
Dim ctlTxtBx As control
Dim col As Variant: col = 0
Dim strTextBoxName As String
Dim strShortenedTxtBxName As String
Dim strConvertedTxtBxName As String
For Each ctlTxtBx In WQTR_Form.Controls
If TypeName(ctlTxtBx) = "TextBox" Then
strTextBoxName = ctlTxtBx.Name
strShortenedTxtBxName = Left(strTextBoxName, Len(strTextBoxName) - 4)
strConvertedTxtBxName = strShortenedTxtBxName + "Label"
col = Application.Match(strConvertedTxtBxName, Worksheets(sheetName).Rows(1), 0)
col = CInt(col)
Cells(3, col).Value = ctlTxtBx.Text
End If
Next ctlTxtBx
End Sub
'******************************************************************************************************
'***Search columns from A through DF (110) and deletes columns where the cell in row three is empty.***
'******************************************************************************************************
Private Sub DeleteEmptyColumns()
Dim col As Integer
For col = 110 To 1 Step -1
If Cells(3, col) = "" Then
ActiveSheet.Columns(col).Delete
End If
Next col
End Sub
So, what I would have expected based on Tab order, is the following
'| welderNameLabel | testDateLabel | wqtrNumberLabel | shopLabel | companyNameLabel | revisionNumberLabel | wpsNumberLabel | bm1_specificationLabel |
'|---------------- | ------------- | --------------- | --------- | ---------------- | ------------------- | -------------- | ---------------------- |
'| Welder Name | Test Date | WQTR Number | Shop | Company Name | Revision Number | WPS Number | Specification |
'|---------------- | ------------- | --------------- | --------- | ---------------- | ------------------- | -------------- | ---------------------- |
'| Dean Marin | 5-23-2017 | DM-1234-6G-FCAW | Bravo | ABC Company | Rev. 0 | 12345 | AWS D1.1 Code |
What I actually get is like this:
'| testDateLabel | welderNameLabel | companyNameLabel | shopLabel | wqtrNumberLabel | revisionNumberLabel | wpsNumberLabel | bm1_specificationLabel |
'| ------------- | --------------- | ---------------- | --------- | --------------- | ------------------- | -------------- | ---------------------- |
'| Test Date | Welder Name | Company Name | Shop | WQTR Number | Revision Number | WPS Number | Specification |
'| ------------- | --------------- | ---------------- | --------- | --------------- | ------------------- | -------------- | ---------------------- |
'| 5-23-2017 | Dean Marin | ABC Company | Bravo | DM-1234-6G-FCAW | Rev. 0 | 12345 | AWS D1.1 Code |
I have tested this many many times and it always puts the data in the worksheet in the exact same order. I could write some code to sort it the order I want but before I do that I wanted to post this question to see if anyone has any ideas about why it behaves this way. I am a bit concerned that I will write something to sort the columns and then later find that my experiments were misleading and the order of the data entry really is more random than it appears.
I code have simply written a line of code for every TextBox in the User Form and explicitly specified the exact cell where the data should go, but I wanted something more general I could adapt in other workbooks which I have planned, as they are all related to each other - (Welding Procedures, Procedure Qualifications and Welder Qualification Continuity Logs).
Perhaps there is some method someone knows of controlling this this order before the data is entered rather than doing some kind of sorting operations after the fact?
I appreciate any responses.
UPDATE and ANSWER
I agree with jsotola that it has to be they are ordered in accordance with the order they were created. jsotola provided some code to list the order and I ran it multiple times and always got the exact same list in the exact same order.
Mystery solved!
If you are interested, here is the list. Part of the reason I agree so strongly with the answer is that, from memory, I can say this is the order I added the controls to the form. If you skim the controls you will see that there is a logical grouping of the names. They are related to each other as you read down the list.
bm1_tubeSizeText
bm1_pipeFrame
bm1_pipeDiameterLabel
bm1_pipeDiameterText
baseMetalFrame2
bm2_baseMetalListBox
bm2_specificationLabel
bm2_specificationText
bm2_awsGroupNumberText
bm2_awsGroupNumberLabel
bm2_gradeLabel
bm2_gradeText
bm2_plateFrame
bm2_plateThicknessLabel
bm2_plateThicknessText
bm2_tubeFrame
bm2_tubeWallThicknessLabel
bm2_tubeWallThicknessText
bm2_tubeSizeLabel
bm2_tubeSizeText
bm2_pipeFrame
bm2_pipeSizeLabel
bm2_pipeSizeText
bm2_pipeSheduleLabel
bm2_pipeSheduleText
bm2_pipeDiameterLabel
bm2_pipeDiameterText
actualTestValuesFrame
atv_TypeOfWeldJointText
atv_filletPipeDiameterText
atv_filletPipeDiameterLabel
atv_baseMetalLabel
atv_baseMetalText
atv_filletFrame
atv_filletPipeTubeThicknessLabel
atv_filletPipeTubeThicknessText
atv_filletPlateThicknessLabel
atv_filletPlateThicknessText
atv_weldingFrame
atv_processLabel
atv_processText
atv_TypeOfWeldJointLabel
atv_grooveFrame
atv_groovePipeTubeThicknessLabel
atv_groovePipeTubeThicknessText
atv_groovePlateThicknessLabel
atv_groovePlateThicknessText
atv_groovePipeDiameterLabel
atv_groovePipeDiameterText
atv_processTypeLabel
atv_processTypeText
atv_backingLabel
atv_backingText
atv_weldingProcessFrame
atv_InstructionLabel_1
atv_InstructionLabel_2
atv_fillerMetalFrame
atv_awsSpecLabel
atv_awsSpecText
atv_awsClassificationLabel
atv_awsClassificationText
atv_fNumberLabel
atv_fNumberText
atv_positionFrame
atv_positionWeldedLabel
atv_positionWeldedText
rq_transferModeLabel
rq_transferModeText
rq_progressionLabel
rq_progressionText
atv_InstructionLabel_3
rq_InstructionLabel_4
rq_InstructionLabel_5
rq_singleOrMultipleElectrodesLabel
rq_singleOrMultipleElectrodesText
rq_gasFluxTypeLabel
rq_gasFluxTypeText
rangesQualiifedFrame
rq_weldingFrame
rq_weldingProcessFrame
rq_processLabel
rq_processText
rq_processTypeLabel
rq_processTypeText
rq_backingLabel
rq_backingText
rq_InstructionLabel_1
rq_InstructionLabel_2
rq_fillerMetalFrame
rq_awsSpecLabel
rq_awsSpecText
rq_awsClassificationLabel
rq_awsClassificationText
rq_fNumberLabel
rq_fNumberText
rq_positionFrame
rq_groovePipe24DownLabel
rq_groovePipe24DownText
rq_groovePlatePipe24UpLabel
rq_groovePlatePipe24UpText
rq_filletPlatePipe24UpLabel
rq_filletPlatePipe24UpText
rq_filletPipe24DownLabel
rq_filletPipe24DownText
rq_TypeOfWeldJointLabel
rq_TypeOfWeldJointText
rq_baseMetalLabel
rq_baseMetalText
rq_filletFrame
rq_filletPipeTubeThicknessLabel
rq_filletPipeTubeThicknessText
rq_filletPlateThicknessLabel
rq_filletPlateThicknessText
rq_filletPipeDiameterLabel
rq_filletPipeDiameterText
rq_grooveFrame
rq_groovePipeTubeThicknessLabel
rq_groovePipeTubeThicknessText
rq_groovePlateThicknessLabel
rq_groovePlateThicknessText
rq_groovePipeDiameterLabel
rq_groovePipeDiameterText
atv_gasFluxTypeText
atv_transferModeLabel
atv_transferModeText
atv_progressionLabel
atv_progressionText
atv_InstructionLabel_4
atv_InstructionLabel_5
atv_singleOrMultipleElectrodesLabel
atv_singleOrMultipleElectrodesText
atv_gasFluxTypeLabel
testResultsFrame
acceptanceCriteria_1Label
acceptanceCriteria_1Text
typeOfTest_1Label
typeOfTest_1Text
results_1Label
results_1Text
remarks_1Label
remarks_1Text
acceptanceCriteria_3Text
typeOfTest_3Text
results_3Text
remarks_3Text
acceptanceCriteria_2Text
typeOfTest_2Text
results_2Text
remarks_2Text
acceptanceCriteria_4Text
typeOfTest_4Text
results_4Text
remarks_4Text
acceptanceCriteria_5Text
typeOfTest_5Text
results_5Text
remarks_5Text
certificationFrame
laboratoryLabel
laboratoryText
testConductedByLabel
testNumberLabel
testNumberText
fileNumberLabel
fileNumberText
certStatementLabel_1
codeYearText
certStatementLabel_2
certStatementLabel_3
manufacturerOrContractorLabel
manufacturerOrContractorText
authorizedByLabel
authorizedByText
dateLabel
dateText
finishFrame
finishInstructionsLabel
saveProgressButton
rq_positionsQualifiedFrame
testConductedByText
AbortButton
typeOfTest_2Label
acceptanceCriteria_2Label
results_2Label
remarks_2Label
typeOfTest_3Label
typeOfTest_4Label
typeOfTest_5Label
acceptanceCriteria_3Label
acceptanceCriteria_4Label
acceptanceCriteria_5Label
results_3Label
results_4Label
results_5Label
remarks_3Label
remarks_4Label
remarks_5Label
WelderIDLabel
WelderIDText
this will show the order-of-creation of the form controls
Private Sub UserForm_Click() ' runs when form background is clicked
Stop ' put here so that the code window shows up ( press F8 or F5 to continue)
Dim i As Integer
For i = 0 To UserForm1.Controls.Count - 1
Debug.Print UserForm1.Controls(i).Name
Next i
stop
End Sub
VB2010. May be a bit hard to understand but I have a list of classes and one field is a string message. I'm outputting these messages to an RTF document but want to maximize use of horizontal space so am trying to dynamically create a table and fit as many messages in one row as possible and then another row. This while I maintain a max width possible for a row.
Public Class TripInfo
Public SysId As String = ""
Public CreateDate As DateTime
Public OutMessage As String = ""
Public OutMessageWidth As Integer = 0 'the width of the message in char count up to first LF
End Class
Dim myTrips1 as New List(Of TripInfo)
Dim myTrips2 as New List(Of TripInfo)
So as I iterate through the lists I want to create rows that are themselves no longer than 45 characters. Something like:
---------------------------------------------
|"Message1 |"Message2 |"Much longer message |
| Trip1 "| Trip2" | with two lines" |
---------------------------------------------
|"message is even longer than the others" |
---------------------------------------------
|"trip is ok |"trip was cancelled due to CW |
| enroute" | must log to system" |
---------------------------------------------
|"Message3 |"Message4 |"Message5 |"Message6"|
| Trip3 "| Error" | Stop" | |
---------------------------------------------
*Note that the message itself can span more than 1 line with LFs to display a multi-line message
I have scratch code to write the RTF code for the tables and have substituted fake messages with multiple embedded LFs and the output looks good.
Dim sbTable As New StringBuilder
sbTable.Append("\pard \trowd\trql\trgaph108\trleft36\cellx1636\cellx3236\cellx4836\intbl R1C1\cell R1C2\cell R1C3\cell\row \pard")
sbTable.Append("\pard \trowd\trql\trgaph108\trleft36\cellx4642\intbl R1C1\cell\row \pard")
sbTable.Append("\pard \trowd\trql\trgaph0\trleft36\cellx4642\cellx5500\intbl R1C1\cell R1C2\cell\row \pard")
However I just cant seem to get my head around how to even start this to do it dynamically. It seems like I may need to do two iterations. One to break up the messages into rows and then another to actually write the RTF code.
I have so far pseudo code but need some help with my logic.
dim v as integer = 0 'total width of current row
For each t as TripInfo in myTrips1 and myTrips2
if (t.OutMessageWidth added to v) > 45 then
start new row and append
else
append to current row
endif
Next t
Without knowing the properties of your TripInfo class, I'm going to have to make some assumptions. If any property I assume doesn't exist, you can either create it or modify the code to get the same effect.
Dim t As TripInfo, AllTrips As New List(Of TripInfo)
For Each t In myTrips1
AllTrips.Add(t)
Next
For Each t In myTrips2
AllTrips.Add(t)
Next
If AllTrips.Count > 0 Then
For Each t In AllTrips
Dim NewRow() As String = t.Lines
Dim w As Integer = t.OutMessageWidth
Dim h As Integer = t.Lines.Count
For ItemHeight As Integer = h To 1 Step -1
For Each CompareTrip As TripInfo In AllTrips
If AllTrips.IndexOf(t) <> AllTrips.IndexOf(CompareTrip) _
And CompareTrip.Lines.Count = ItemHeight _
And w + CompareTrip.OutMessageWidth <= 45 Then
w += CompareTrip.OutMessageWidth
For l As Integer = 0 To h -1
NewRow(l) = NewRow(l).PadRight(w) & CompareTrip.Lines(l)
Next
AllTrips.Remove(CompareTrip)
End If
Next
Next
AllTrips.Remove(t)
'Write lines of NewRow to your RTF
Next
End If
I am not familiar with using macro's, but I think that what I would like excel to perform is best handled with a macro. So I can use all the input you may have!
I have these headers;
ID Tag Pen Sex Weight Class Inside range
With 450 rows of data. Based on the distribution of the weight data, I have in two other columns (class and number) the number of rows I want to select within each class. The selected rows must have the value "Yes" in the column "Inside range".
I want to randomly select the rows, based on the number needed for each class, and copy these rows to a new sheet. It sums up to 30 rows in the new sheet.
I hope you have a suggestion how to complete this action!
can you try the following, you will need to add a reference to Microsoft Scripting Runtime library:
Const rowCount = 450
Public Sub copyRows()
Dim i As Integer
Dim j As Integer
Dim classes As Scripting.Dictionary
Dim source As Worksheet
Dim colNumber As Integer
Dim colClassName as Integer
Dim colInsideRange As Integer
Dim allSelected As Boolean
Dim randomRow as Integer
Dim sumRemaining as Integer
allSelected = False
Set source = Worksheets("YourWorksheetName")
colClassName = 6 'this is the column number where class names are entered. I am assuming 6
colNumber = 7 'this is the column number where number of rows to be selected are entered. I am assuming 7
colInsideRange = 8 'this is the column number where "Inside Range" values are entered. I am assuming 9
For i = 2 to rowCount + 1 'assuming you have a header row
classes(CStr(source.Cells(i, colClassName))) = CInt(source.cells(i, colNumber)
Next i
Do until allSelected
Randomize
randomRow = Int ((Rnd * 450) + 2) 'assuming you have a header row, + 1 if you don't
If classes(CStr(source.Cells(randomRow, colClassName))) = 0 Then
With classes
sumRemaining = 0
For j = 1 to .Count - 1
sumRemaining = sumRemaining + .Items(j)
If sumRemaining > 0 Then Exit For
Next j
allSelected = (sumRemaining = 0)
End With
Else
source.Cells(randomRow, colInsideRange) = "Yes"
classes(CStr(source.Cells(randomRow, colClassName))) = classes(CStr(source.Cells(randomRow, colClassName))) - 1
End If
Loop
'Enter your code to copy rows with "Inside Range" = "Yes"
End Sub
Sorry if there are some errors or typos, I wrote from my mobile phone.
I Have about 10 (DatagridView Count may varies As per User Selected Files From 2 to 10) Datagridview ,So How can i find common value from all Datagridviews ??
Comment If you need more brief details
Below is mine but It find common from 2 -2 datagridviews
For i As Integer = 1 To dgvCont
For j As Integer = 0 To Main.DGVM(i).Rows.Count - 1
For Each Val As DataGridViewRow In Main.DGVM(i + 1).Rows
If Val.Cells(0).Value = Main.DGVM(i).Rows.Item(j).Cells(0).Value Then
Dim cm As String = Val.Cells(0).Value
If cm = "" Then
Else
Analysis.lvCmn.Items.Add(Val.Cells(0).Value)
End If
End If
Next
Next
Next
I understand that you want to set two nested loops accounting for an undetermined number of elements (items in an array of DataGridView, I presume), performing the checks you want:
For count1 As Integer = 1 To dgvCont 'Assuming indices from 1 to dgvCont
For row1 As Integer = 0 To Main.DGVM(count1).Rows.Count - 1
If (Main.DGVM(count1).Rows(row1).Cells(0).Value Is Nothing) Then Continue For
Dim val1 As String = Main.DGVM(count1).Rows(row1).Cells(0).Value
Dim found As Boolean = False
For count2 As Integer = 1 To dgvCont 'Assuming indices from 1 to dgvCont
If (count2 = count1) Then Continue For
For row2 As Integer = 0 To Main.DGVM(count2).Rows.Count - 1
If (Main.DGVM(count2).Rows(row2).Cells(0).Value Is Nothing) Then Continue For
Dim val2 As String = Main.DGVM(count2).Rows(row2).Cells(0).Value.ToString()
If val1 = val2 Then
Dim cm As String = val1
If cm = "" Then
Else
Analysis.lvCmn.Items.Add(val1)
End If
found = True
Exit For 'By assuming that you want to stop searching after finding a match
End If
Next
If (found) Then Exit For 'By assuming that you want to stop searching after finding a match
Next
Next
Next
Your code is not too clear (neither what you want); but this should give you a good enough start to carry out the implementation you are looking for. Bear in mind that this code (like yours) only considers one column (first one); in case of wanting to iterate through all the columns, you would have to add further nested loops accounting for that.