Error "object required" in VBA , referred to duplicate questions - vba

My purpose is to split a task into constituent tasks and find the most important one.The macro is written in "May" sheet of workallotment.xlsm and the tasks are in tasks.xlsx
For example:
Constituents Constituents Important Imp
Praveen T1 T2 T3 T4 T5 T6 T1+T2+T3 =T5 T3+T5+T6 =T9 T1 T6
4 3 1 2 8 9
Karthik P1 P2 P3 P4 " among T1,T2,T3- T1 takes more time".its imp
6 3 2 2
Walter c1 c2 c3 c4
1 2 3 4
Arvind g1 g2 g3
2 1 3
Sreelatha h1 h2 h3
2 1 1
Code:
Sub workallotment()
Dim workallotmentWB, tasksWB As Workbook
Dim waSheet As Worksheet
Dim str(9) As String
Dim splitArray() As String, S(10) As String
Dim col_new As Integer
Dim wa_nameRng As Range
Dim r As Integer, max As Integer, imps As String
Dim wa_nameRow, wa_firstRow, wa_lastRow As Integer 'work allotment rows
Dim t_firstRow, t_lastrow As Integer 'task rows
Dim curTaskCol As Integer 'current task column
Dim wa_tmpcol As Integer 'work allotment, temp column
Set workallotmentWB = ThisWorkbook
Set tasksWB = Workbooks.Open("E:/tasks.xlsx")
'notes on data structure:
'- tasks workbook:
'first name starts in A1 of "Sheet1"
'- workallotment workbook:
'first name starts in A2 of Sheet named "workallotment"
'tasks are to be written starting in B2
'in Row 1 are headers (number of days)
t_firstRow = 1
wa_firstRow = 2
wa_nameRow = 0
Set waSheet = workallotmentWB.Worksheets("May") ' in this file - workallotment.xlsm
With tasksWB.Worksheets("May") ' in tasks.xlsx which is attached
'finding the last rows
t_lastrow = .Range("A1000000").End(xlUp).row + 1
wa_lastRow = waSheet.Range("A1000000").End(xlUp).row
'goes through all the names in tasks_Sheet1
For r = t_firstRow To t_lastrow Step 2
Set wa_nameRng = waSheet.Range("A:A").find(.Range("A" & r).Value, _
LookIn:=xlValues, LookAt:=xlWhole, SearchDirection:=xlNext, MatchCase:=False)
If Not wa_nameRng Is Nothing Then
wa_nameRow = wa_nameRng.row
curTaskCol = 2
wa_tmpcol = 2
Do While Not IsEmpty(.Cells(r, curTaskCol).Value)
For C = 1 To .Cells(r + 1, curTaskCol).Value
waSheet.Cells(wa_nameRow, wa_tmpcol).Value = .Cells(r, curTaskCol).Value
wa_tmpcol = wa_tmpcol + 1
Next C
curTaskCol = curTaskCol + 1
Loop
End If
Next r
End With
MsgBox ("done")
For r = t_firstRow To t_lastrow Step 2 ' loop to find importance
col = 2 'setting to initial col
curTaskCol = 17 ' position input - constituent jobs at 17th col in tasks.xls
Do While Not IsEmpty(tasksWB.Worksheets("May").Cells(r, curTaskCol).Value)
str(curTaskCol - 16) = tasksWB.Worksheets("May").Cells(r, curTaskCol).Value
' reading input to first array of string element
substr = Left(str(curTaskCol - 16), Application.WorksheetFunction.find("=", str(curTaskCol - 16)) - 1) ' if T1+T2=T3 it'll look before "=" symbol
MsgBox (substr)
splitArray() = Split(substr, "+") ' if T1+T2 it will be split as T1 & T2
For i = LBound(splitArray) To UBound(splitArray)
S(i + 1) = splitArray(i) ' assigning split elements to string array
Next i
For i = LBound(splitArray) To UBound(splitArray)
col_new = 2 ' checking from 2nd column
Do While Not IsEmpty(tasksWB.Worksheets("May").Cells(r, col_new).Value)
If (S(i + 1) = tasksWB.Worksheets("May").Cells(r, col_new).Value) Then 'initialising max and imps
imps = S(i + 1) ' most important job
max = tasksWB.Worksheets("May").Cells(r + 1, col_new).Value
End If ' maximum time taken for task
col_new = col_new + 1
Loop
For j = LBound(splitArray) To UBound(splitArray)
col_new = findcol(S(j + 1), r, tasksWB)
If (max < tasksWB.Worksheets("May").Cells(r + 1, col_new).Value) Then
max = tasksWB.Worksheets("May").Cells(r + 1, col_new).Value
imps = tasksWB.Worksheets("May").Cells(r, col_new).Value
End If
Next j
Next i
tasksWB.Worksheets("May").Cells(r, curTaskCol + 6).Value = imps
' assign most IMPORTANT task on 6th column from current column
curTaskCol = curTaskCol + 1 ' RUNTIME ERROR 1004
Loop
Next r
End Sub
Public Function findcol(S As String, row As Integer, theWB As Workbook) As Integer
Dim col As Integer, addr As Integer
col = 2 ' checking from column 2
'Set tasksWB = Workbooks.Open("E:/tasks.xlsx")
Do While Not IsEmpty(theWB.Worksheets("May").Cells(row, col).Value)
If (StrComp(Trim(S), Trim(theWB.Worksheets("May").Cells(row, col).Value)) = 0) Then
addr = col ' if task string is found in column
End If
col = col + 1 ' return column found
Loop
findcol = addr
End Function

Krishnan,
In your main proc workallotment you declare the variable tasksWB.
In your method 'findcol' you then reference tasksWB. It looks like you've pulled this code out of the main proc. The tasksWB only has scope within workallot and so you need to give findcol this object so it will have it within it's scope as well.
I would recommend that you pass the tasksWB into the method, as a third parameter.
Your method would then look as follows.
Edit for your comment of why findcol doesn't return. The Exit Function will ensure that the method is exited immediately after setting the return value. Without this you would end up in the asking for the correct task name again.
Public Function findcol(S As String, row As Integer, theWB as Workbook) As Integer
col = 2 ' checking from column 2
Do While Not IsEmpty(theWB.Worksheets("May").Cells(row, col).Value)
If (S = theWB.Worksheets("May").Cells(row, col).Value) Then
findcol = col ' if task string is found in column
Exit Function
End If
'MsgBox ("Enter correct task names") Not sure why this is here.
col = col + 1 ' return column found
Loop
End Function
and you'd call it with
col_new = findcol(S(j + 1), r, tasksWB) ' ERROR line function to find column of task string
This will ensure that you do not "leak" your variable definition into global scope, and that you also ensure that your method doesn't depend on external globals.
Edit 3:
Your findcol is still wrong.
Public Function findcol(S As String, row As Integer, theWB As Workbook) As Integer
Dim col As Integer
'******* you don't need this because you can exit early
'Dim addr As Integer
col = 2 ' checking from column 2
'***** THIS LINE NEEDS TO BE REMOVED because you are using theWB being passed in *****
'Set tasksWB = Workbooks.Open("E:/tasks.xlsx")
Do While Not IsEmpty(theWB.Worksheets("May").Cells(row, col).Value)
'****** this line must use theWB
'If (StrComp(Trim(S), Trim(tasksWB.Worksheets("May").Cells(row, col).Value)) = 0) Then
If (StrComp(Trim(S), Trim(theWB.Worksheets("May").Cells(row, col).Value)) = 0) Then
'************* you can exit early once you've found what you need.
'addr = col ' if task string is found in column
findcol = col
exit function
End If
col = col + 1 ' return column found
Loop
' You can exit early so don't need this.
' findcol = addr
End Function
You should probably do a check when you call the function that the value hasn't returned 0, eg
new_col = findcol( .... )
if new_col = 0 then
msgbox "couldn't find the column with that str" & S(j + 1)
end if

tasksWB isn't recognized in the findcol function as it is declared as Private (=Dim) in the main process.
Declare it at the top of your module, and it'll work! ;)

Related

How to do Loop to calculate the cell values in DataGridView?

How to multiply a value of a row of a column to all the values in another column and do the loop for all rows.Then, at the end, sum the values in each row?
Here is an example.
Please note that I do not need to have or show these three columns (I, II, III). I just put them in there to show the steps.
Thanks a lot for your help!
The loop works only for the 1st two rows in Column 3..!!?
Dim i As Integer
For k As Integer = 0 To Data1.ColumnCount - 4
For j = 0 to Data1.Rows.Count - 2
Data1.Rows(j).Cells(2).Value = Data1.Rows(j).Cells(0).Value * _
Data1.Rows(j).Cells(1).Value
For i = 0 To EOF()
i = i + 1
Data1.Rows(j).Cells(3).Value = Data1.Rows(j).Cells(0).Value *_
Data1.Rows(k+i).Cells(1).Value + Data1.Rows(j).Cells(2).Value
Next i
Next j
Next k
This solution doesn't use a DataGridView (or GridView) but using arrays. The trick is creating a list of row, column and the multiplication result. See the table below to see how the list looks like. After the list is built, it's just grouping by the second value (row) and calculate the sum the third value.
Dim C1 = { 40, 30, 20, 10 } ' column C1
Dim C2 = { 1, 2, 3 } ' column C2
' to store the result of multiplication between C1 and C2
' first value is Column, second value is Row, third column is the multiplication result
Dim list = New List(Of Tuple(Of Integer, Integer, Integer))
For i = 0 To C2.Length - 1
For j = 0 To C1.Length - 1
list.Add(New Tuple(Of Integer, Integer, Integer)( i, i + j, C2(i) * C1(j) )) ' column, row, value
Next
Next
' to store sum of each row
' key is row, value is sum of the row
Dim dict = New Dictionary(Of Integer, Integer)
For Each row In list ' iterate each row in list
If dict.ContainsKey(row.Item2) ' if dictionary contains row number
dict(row.Item2) += row.Item3 ' add value to existing row
Else
dict.Add(row.Item2, row.Item3) ' add new row
End If
Next
For Each entry In dict
Console.WriteLine("Total Row {0} = {1}", entry.Key, entry.Value)
Next
Alternative using LINQ to get the sum.
Dim C1 = { 40, 30, 20, 10 } ' column C1
Dim C2 = { 1, 2, 3 } ' column C2
' to store the result of multiplication between C1 and C2
' first value is Column, second value is Row, third column is the multiplication result
Dim list = New List(Of Tuple(Of Integer, Integer, Integer))
For i = 0 To C2.Length - 1
For j = 0 To C1.Length - 1
list.Add(New Tuple(Of Integer, Integer, Integer)( i, i + j, C2(i) * C1(j) )) ' column, row, value
Next
Next
' LINQ sum
Dim result = From l In list
Group By l.Item2 ' group by row
Into Sum(l.Item3) ' sum of value
For Each row In result
Console.WriteLine("Total Row {0} = {1}", row.Item2, row.Sum)
Next
Spreadsheet version of the list with colored rows grouped by Row (2nd) column.
Result:
Total Row 0 = 40
Total Row 1 = 110
Total Row 2 = 200
Total Row 3 = 140
Total Row 4 = 80
Total Row 5 = 30
I hope you get the idea to implement this code in your project.
EDIT. Optimized solution with less looping.
Dim C1 = { 40, 30, 20, 10 } ' column C1
Dim C2 = { 1, 2, 3 } ' column C2
Dim dict = New Dictionary(Of Integer, Integer)
For i = 0 To C2.Length - 1
For j = 0 To C1.Length - 1
If dict.ContainsKey(i + j) ' if dictionary contains row number
dict(i + j) += C2(i) * C1(j) ' add value to existing row
Else
dict.Add(i + j, C2(i) * C1(j)) ' add new row
End If
Next
Next
For Each entry In dict
Console.WriteLine("Total Row {0} = {1}", entry.Key, entry.Value)
Next
Sample in Windows Form application. Add a DataGridView and a Button into the form.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LoadInitialData()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Calculate()
End Sub
Sub LoadInitialData()
DataGridView1.Rows.Clear()
DataGridView1.Rows.Add(40, 1)
DataGridView1.Rows.Add(30, 2)
DataGridView1.Rows.Add(20, 3)
DataGridView1.Rows.Add(10, Nothing)
End Sub
Sub Calculate()
Dim dict = New Dictionary(Of Integer, Integer)
For i = 0 To DataGridView1.Rows.Count - 1
For j = 0 To DataGridView1.Rows.Count - 1
' check if both are numbers
If IsNumeric(DataGridView1(0, i).Value) And IsNumeric(DataGridView1(1, j).Value) Then
Dim C1 = Convert.ToInt32(DataGridView1(0, i).Value) ' value of C1 from 1st column of outer loop
Dim C2 = Convert.ToInt32(DataGridView1(1, j).Value) ' value of C2 from 2nd column of inner loop
If dict.ContainsKey(i + j) Then ' check if dictionary has entry
dict(i + j) += C1 * C2 ' increment the value in dictionary
Else
dict.Add(i + j, C1 * C2) ' add new entry into dictionary
End If
End If
Next
Next
For Each entry In dict
' check if row in datagridview is lesser than dictionary entries
If DataGridView1.Rows.Count < dict.Keys.Count Then
DataGridView1.Rows.Add() ' add empty row
End If
DataGridView1(2, entry.Key).Value = entry.Value ' set value in 3rd column
Next
End Sub
End Class

Shortest Flow Layout Solver

I have a routing sequence for a set of machines on an assembly line. Each route has to go through the entire line (that is, if you only run the first and second machine, you still account for the distance from the second to the end of the line).
I have six different machines (720 possible combinations of machines) with fixed distances between each location on the line. The distance between the first and second machine is 100', the distance between second and third is 75', third and fourth is 75', fourth and fifth is 25', and fifth and sixth is 25'.
I have 4 different products that have to run down the line, and each of them have a fixed routing.
My problem is, how do I set up a vba code or solver that will allow me to run through all possible combinations of the line setup and determine the optimal setup for this line? Any machine can be placed at any location, as long as it optimizes the result!
The four product routes are :
A - B - C - D - F
A - C - B - D – E - F
A - F - E - D - C - B - A - F
A - C - E - B - D – F
Running through all possible combinations - if you really need to do that - is a job for something like Heap's algorithm, although I prefer the plain changes method:
Sub Evaluate(Lineup() As String)
' dummy evaluation, just output the permutation
Dim OffCell As Long
For OffCell = LBound(Lineup, 1) To UBound(Lineup, 1)
ActiveCell.Offset(0, OffCell).Value = Lineup(OffCell)
Next OffCell
ActiveCell.Offset(1, 0).Activate
End Sub
Sub AllPerms(Lineup() As String)
' Lineup is a 1-D array indexed at 1
Dim LSize As Long
Dim Shift() As Long
Dim Tot As Long
Dim Idx As Long
Dim Level As Long
Dim Change As Long
Dim Offset As Long
Dim TempStr As String
LSize = UBound(Lineup)
ReDim Shift(LSize)
'count of permutations, set initial changes
Tot = 1
For Idx = 2 To LSize
Tot = Tot * Idx
Shift(Idx) = 1 - Idx
Next Idx
Shift(1) = 2 ' end condition
' go through permutations
For Idx = 1 To Tot
' check this one
Call Evaluate(Lineup)
' switch for the next
Level = LSize
Offset = 0
Change = Abs(Shift(Level))
Do While Change = 0 Or Change = Level
If Change = 0 Then Shift(Level) = 1: Offset = Offset + 1
If Change = Level Then Shift(Level) = 1 - Level
Level = Level - 1
Change = Abs(Shift(Level))
Loop
Shift(Level) = Shift(Level) + 1
Change = Change + Offset
TempStr = Lineup(Change)
Lineup(Change) = Lineup(Change + 1)
Lineup(Change + 1) = TempStr
Next Idx
End Sub
Sub ABCDEF_case()
Dim LU(6) As String
LU(1) = "A"
LU(2) = "B"
LU(3) = "C"
LU(4) = "D"
LU(5) = "E"
LU(6) = "F"
Call AllPerms(LU)
End Sub

How can I convert these inelegant formulae into VBA?

Good people of Stackland
I'm analysing strings comprised of 5 alpha chars which in their raw format look like this;
A2) BCDBE
A3) TLDPP
A4) FGGFC
A5) BBGBB
I need a way of evaluating each character to identify patterns within the strings themselves, eg repeating letters. I want to represent these patterns as follows, where the 1st letter is always given as "A", the 2nd "B"...;
A2) BCDBE --> ABCAD
A3) TLDPP --> ABCDD
A4) FGGFC --> ABBAC
A5) BBGBB --> AABAA
Now, I have achieved this with some pretty inelegant conditional formulae but had to do this to evaluate each character individually, as follows;
1) =IF(LEFT(A2,1)>0,"A")
2) =IF(MID(A2,2,1)=LEFT(A2,1),"A","B")
3) =IF(MID(A2,3,1)=LEFT(A2,1),"A",IF(MID(A2,3,1)=MID(A2,2,1),M2,CHAR(CODE(M2)+1)))
4) =IF(MID(A2,4,1)=LEFT(A2,1),"A",IF(MID(A2,4,1)=MID(A2,2,1),M2,IF(MID(A2,4,1)=MID(A2,3,1),N2,CHAR(MAX(CODE(L2:N2)+1)))))
5) =IF(MID(A2,5,1)=LEFT(A2,1),"A",IF(MID(A2,5,1)=MID(A2,2,1),M2,IF(MID(A2,5,1)=MID(A2,3,1),N2,IF(MID(A2,5,1)=MID(A2,4,1),O2,CHAR(MAX(CODE(L2:O2)+1))))))
Translated...
1) Call the first character "A"
2) If the 2nd character is the same as the same as the 1st call it "A", otherwise cause it "B"
3) If the 3rd character is the same as the 1st call it "A", if it's the same as the 2nd call it whatever the 2nd is, if not give it the value of the next letter, ie "C"
4) If the 4th character is the same as the 1st, call it "A", if it's the sames as the 2nd call it whatever the 2nd is, if it's the same as the 3rd call it whatever the 3rd is, if not then call it the next letter in the alphabet, ie "D"
5) If the 5th character is the same as the 1st, call it "A", if it's the same as the 2nd call it whatever the 2nd is, if it's the same as the 3rd call it whatever the 3rd is called, if it's the same as the 4th call it whatever the 4th is called, if not then call it the next letter in the alphabet, ie "E"
I'm doing this over 5 cols, one formula per col, and the concatenating the 5 results into one cell to get AABAA or whatever.
I just need to know if there's a nice, clean VBA solution to this.
Any ideas?
Here is the a Function to do the letter instead of numbers:
Function findPattern(inputStr As String) As String
Dim i As Integer
Dim t As Integer
t = 1
For i = 1 To 5 Step 1
If Asc(Mid(inputStr, i, 1)) > 54 Then
inputStr = Replace(inputStr, Mid(inputStr, i, 1), t)
t = t + 1
End If
Next i
For i = 1 To 5
inputStr = Replace(inputStr, i, Chr(i + 64))
Next i
findPattern = inputStr
End Function
Put it in a module attached to the workbook, and you can call it thus:
=findPattern(A2)
Driectly from the worksheet where A2 is the cell you want tested.
Or from vba:
Sub test()
Dim str as string
str = findPattern(Range("A2").value)
debug.print str
End Sub
Edit: By your Comment I assume you have more than just the first 5 characters that you want left original. If that is the case use this:
Function findPattern(Str As String) As String
Dim inputStr As String
Dim i As Integer
Dim t As Integer
inputStr = Left(Str, 5)
t = 1
For i = 1 To 5 Step 1
If Asc(Mid(inputStr, i, 1)) > 54 Then
inputStr = Replace(inputStr, Mid(inputStr, i, 1), t)
t = t + 1
End If
Next i
For i = 1 To 5
inputStr = Replace(inputStr, i, Chr(i + 64))
Next i
'This is the return line. As is it will only return 5 characters.
'If you want the whole string with only the first five as the pattern
'Remove the single quote in the middle of the string.
findPattern = inputStr '& Mid(Str, 6, (Len(Str)))
End Function
This seems like an easy approach:
's is the input string
dim pos, c, s_new, s_old
pos = 1 : c = 49
s_new = mid(s, 1, 5) ' take only first five characters
do while pos <= 5
s_old = s_new
s_new = replace(s_new, mid(s, pos, 1), chr(c))
if s_new <> s_old then c = c + 1
loop
s_new = replace(s_new, "1", "A")
s_new = replace(s_new, "2", "B")
s_new = replace(s_new, "3", "C")
s_new = replace(s_new, "4", "D")
s_new = replace(s_new, "5", "E")
'm assuming that you don't have any numeric characters in your input.
This has a certain elegance:
Function Pattern(r As Range)
Dim c&, i&, a
Const FORMULA = "iferror(find(mid(~,{2,3,4,5},1),left(~,{1,2,3,4})),)"
a = Evaluate(Replace(FORMULA, "~", r.Address))
c = 1: Pattern = "A"
For i = 1 To 4
If a(i) = 0 Then c = c + 1: a(i) = c
Pattern = Pattern & Chr$(64 + a(i))
Next
End Function
I had this for a while (it's handy for cryptograms), so I'll post it:
Function Pattern(ByVal sInp As String) As String
' shg 2012
' Returns the pattern of a string as a string of the same length
' First unique letter and all repeats is a, second is b, …
' E.g., Pattern("mississippi") returns "abccbccbddb"
Dim iChr As Long ' character index to sInp & Pattern
Dim sChr As String ' character in sInp
Dim iPos As Long ' position of first appearance of sChr in sInp
sInp = LCase(Trim(sInp))
If Len(sInp) Then
sChr = Chr(64)
Pattern = sInp
For iChr = 1 To Len(sInp)
iPos = InStr(sInp, Mid(sInp, iChr, 1))
If iPos = iChr Then ' it's new
sChr = Chr(Asc(sChr) + 1)
Mid(Pattern, iChr) = sChr
Else
Mid(Pattern, iChr) = Mid(Pattern, iPos, 1)
End If
Next iChr
End If
End Function

Updating A DataGridView Cell Incrementally

I'm currently having a slight issue duplicating a row and incrementing the sequence number.
So based on a button click, this is how I'm duplicating row 0, duplicated only one time per click.
Dim dr As DataRow
For n As Integer = 0 To 0 ' how many dupes you want
dr = tbl.NewRow
For c As Integer = 0 To tbl.Columns.Count - 1 ' copy data from 0 to NewRow
dr.Item(c) = tbl.Rows(0).Item(c)
Next
tbl.Rows.Add(dr) ' add NewRow to datatable
Next n
Here's how I'm creating the sequence number, pads with leading zeros, which seems to increment, but only after I click the duplicate button, so essentially the last row added, it the duplicated row 0, but doesn't represent the new sequence number needed.
'UPDATE SEQUENCE NUMBER
i += 1
Dim value As Integer = i
Dim r As Integer
Dim decimalLength1 As Integer = value.ToString("D").Length + 7
Dim decimalLength2 As Integer = value.ToString("D").Length + 6
Dim decimalLength3 As Integer = value.ToString("D").Length + 5
Dim decimalLength4 As Integer = value.ToString("D").Length + 4
If i >= 0 And i <= 9 Then
'1 TO 9 FORMAT
DataGridView1.CurrentCell = DataGridView1.CurrentRow.Cells("sequence")
DataGridView1.Item(73, r).Value = value.ToString("D" + decimalLength1.ToString())
'Debug.Print(value.ToString("D" + decimalLength1.ToString()))
ElseIf i >= 10 And i <= 99 Then
'10 TO 99 FORMAT
DataGridView1.CurrentCell = DataGridView1.CurrentRow.Cells("sequence")
DataGridView1.Item(73, r).Value = value.ToString("D" + decimalLength2.ToString())
'Debug.Print(value.ToString("D" + decimalLength1.ToString()))
ElseIf i >= 100 And i <= 999 Then
'100 TO 999 FORMAT
DataGridView1.CurrentCell = DataGridView1.CurrentRow.Cells("sequence")
DataGridView1.Item(73, r).Value = value.ToString("D" + decimalLength3.ToString())
'Debug.Print(value.ToString("D" + decimalLength1.ToString()))
ElseIf i >= 1000 And i <= 9999 Then
'1000 TO 9999 FORMAT
DataGridView1.CurrentCell = DataGridView1.CurrentRow.Cells("sequence")
DataGridView1.Item(73, r).Value = value.ToString("D" + decimalLength4.ToString())
'Debug.Print(value.ToString("D" + decimalLength1.ToString()))
End If
Row 0 will always have a sequence number of 1, so in theory I need to start incrementing at 2.
Suggestions? Is there a better/cleaner way of doing this?
UPDATE 2
Dim startSeq As Integer = Convert.ToInt32(tbl.Rows(0).Item(73))
MsgBox("startSeq = " & startSeq)
For n As Integer = 0 To NumericUpDown1.Value - 1
MsgBox("n = " & n)
dr = tbl.NewRow
For c As Integer = 0 To tbl.Columns.Count - 1
dr.Item(c) = tbl.Rows(0).Item(c)
If c = "73" Then ' if this is the SEQ column,
' add the current seq val to the seq column
dr.Item(c) = (startSeq + n).ToString("00000000")
End If
Next c
tbl.Rows.Add(dr)
Next n
It seems like you should be able to add the sequencer as you create the duplicates. Perhaps make it a method and pass the index of the column which has the sequence string. Something like:
Private Sub DuplicateRows(ColIndx As Integer,
Dupes As Integer)
' start value is Row(0) + 1
Dim startSeq As Integer = Convert.ToInt32(tbl.Rows(0).Item(ColIndx )) + 1
For n As Integer = 0 to Dupes -1
dr = tbl.NewRow
For c As Integer = 0 To tbl.Columns.Count - 1
If c = ColIndx Then ' if this is the SEQ column,
' add the current seq val to the seq column
dr.Item(c) = (startSeq + n).ToString("00000000")
Else
' otherwise copy the data from Row(0)
dr.Item(c) = tbl.Rows(0).Item(c)
End If
Next c
tbl.Rows.Add(dr)
Next n
End Sub
This should initialize each new row with an incremented counter. Is there a better/cleaner way of doing this
a) you should be adding to the DataTable, not the DGV if it is bound
b) (startSeq + n).ToString("00000000") should work to do the padding etc instead of that ugly block of code.
c) Use Option Strict On. If c = "73" ... is nonsense which makes the compiler guess at your intentions. Its is bug food.
d) Hardcoding "73" may work this time, but previously you said it could be anywhere. The code below finds the sequence column based on the name so it can appear anywhere. Rather than a form level var, you could find it just before you make the dupes or even in the Dupes procedure.
e) Dim startSeq As Integer = Convert.ToInt32(tbl.Rows(0).Item(73)) if you examine the answer above, this should be ... + 1 to increment the first value.
Usage:
Private tbl As DataTable ' table loaded from flat file
Private SeqColIndex As Integer ' assigned somewhere to
' point to the "sequence" column
' method to load data
Dim connstr = "Provider=Microsoft.ACE.OLEDB.12.0;..."
Using cn As New OleDbConnection(connstr)
...
End Using
' FIND the sequence column for this session
For n = 0 To tbl.Columns.Count - 1
If tbl.Columns(n).ColumnName.ToLowerInvariant = "sequence" Then
SeqColIndex = n
Exit For
End If
Next
' later to add some rows
Private Sub ButtonAddRow_Click(...
DuplicateRows(SeqColIndex, NumericUpDown1.Value)
End Sub

What could be slowing down my Excel VBA Macro?

This function goes through all integers and picks out binary values with only five ones and writes them to the spreadsheet.
To run this For x = 1 To 134217728 would take 2.5 days!!!! Help!
How could I speed this up?
Function D2B(ByVal n As Long) As String
n = Abs(n)
D2B = ""
Do While n > 0
If n = (n \ 2) * 2 Then
D2B = "0" & D2B
Else
D2B = "1" & D2B
n = n - 1
End If
n = n / 2
Loop
End Function
Sub mixtures()
Dim x As Long
Dim y As Integer
Dim fill As String
Dim mask As String
Dim RowOffset As Integer
Dim t As Date
t = Now
fill = ""
For x = 1 To 134217728
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
Debug.Print mask
If x > 100000 Then Exit For
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
RowOffset = RowOffset + 1
For y = 1 To Len(mask)
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
Range("mix").Offset(RowOffset).Cells(y) = Mid(mask, y, 1)
Next
Next
Debug.Print DateDiff("s", Now, t)
End Sub
By first sight guess, I think the problem lies in the fact that you do that cell by cell, which causes many read and write accesses.
You should do it range by range, like
vArr = Range("A1:C1000").Value
' it is array now, do something here effeciently
Range("A1:C1000").Value = vArr
You want find all 28bit numbers with 5 1s
There are 28*27*26*25*24/5/4/3/2=98280 such numbers
The following code took ~10 seconds on my PC:
lineno = 1
For b1 = 0 To 27
For b2 = b1 + 1 To 27
For b3 = b2 + 1 To 27
For b4 = b3 + 1 To 27
For b5 = b4 + 1 To 27
Cells(lineno, 1) = 2 ^ b1 + 2 ^ b2 + 2 ^ b3 + 2 ^ b4 + 2 ^ b5
lineno = lineno + 1
Next
Next
Next
Next
Next
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
The above line of code does the same thing (CStr(D2B(x))) twice.
Store the result of CStr(D2B(x)) in a variable & use that variable in the above line of code.
I've got 2 suggestions:
Get rid of the substitution command by counting the ones/zeroes in D2B and return an empty string if the count does not equal 5
Write these pre-filtered bitstrings to an array first and copy the array directly to the cells when finished.
Something like
ws.Range(ws.cells(1, 1), ws.cells(UBound(dstArr, 1) + 1, UBound(dstArr, 2) + 1)) = dstArr
The array-copy-trick greatly improves performance!