Align cells with same value - vba almost working

  • Thread starter Thread starter bpascal123
  • Start date Start date
Hi cyberspace,

I have spent quite some time trying to make this work but at this
point from  adding many msgbox checks, using the watch window for
variables values everything seems coherent  to me.

I have 2 columns with sorted identical and not identical numercial
values in both columns :

col.A       col.B
251120  251130
251140  272505
251145  291101
272505  292100
272535
291130
292100

I need to align identical value and to place single value alone on
one
row just like below :

251120
                251130
251140
251145
272505  272505
272535
                291101
291130
292100  292100

Now with the vba code, I get this :

Option Explicit
Option Base 1

Public Sub RowMatching()

  Dim wkb As Workbook
  Dim wks As Worksheet
  Set wkb = Workbooks("code_row_v2.xls")
  Set wks = wkb.Worksheets("Sheet1")
  Dim trouve As Boolean
  Dim LigCol1 As Integer  'numéro de ligne pour la premiere colonne
  Dim LigCol2 As Integer  'numéro de ligne pour la seconde colonne
  Dim LastRow As Long
  Dim tmp
  Dim Numligne(256) As Long
  Dim marchehaute As Integer
  Dim marchebasse As Integer
  Dim marche As Integer

  wks.Cells(1, 1).Select
  LastRow = 0
  LigCol1 = 1
  While wks.Cells(LigCol1, 1) <> ""
    LastRow = LastRow + 1
    LigCol1 = LigCol1 + 1
  Wend
  LigCol1 = 1
  wks.Cells(LigCol1, 1).Select

  While LigCol1 <= LastRow  '''MAIN LOOP

    Numligne(LigCol1) = wks.Cells(LigCol1, 1)
    'MsgBox wks.Cells(LigCol1, 1)

    For LigCol2 = 1 To LastRow
      If Numligne(LigCol1) = wks.Cells(LigCol2, 2) Then '2a-IF7
        If LigCol2 < LigCol1 Then                      '3a-IF9
          Cells(LigCol2, 2).Select
          marchehaute = LigCol1 - LigCol2
          marche = 1
          While marche <= marchehaute
            Selection.Insert shift:=xlDown
            marche = marche + 1
          Wend
        ElseIf LigCol2 > LigCol1 Then
          Cells(LigCol1, 1).Select
          marchebasse = LigCol2 - LigCol1
          marche = 1
          While marche <= marchebasse
            Selection.Insert shift:=xlDown
            marche = marche + 1
            LastRow = LastRow + 1
          Wend
        End If                                          '3a-IF9
      End If                                            '2a-IF7
    Next LigCol2
    LigCol1 = LigCol1 + 1

  Wend '''END MAIN LOOP

  LigCol1 = 1
  wks.Cells(LigCol1, 1).Select

''SECONDARY LOOP TO INSERT ROW FOR REMAINING NON IDENTICAL VALUES
FOUND ONTO SAME ROWS

  For LigCol1 = 1 To LastRow '

    MsgBox wks.Cells(LigCol1, 1) & " - " & wks.Cells(LigCol1, 2)
    If Not IsEmpty(wks.Cells(LigCol1)) Then
      If wks.Cells(LigCol1, 1).Value <> wks.Cells(LigCol1, 2).Value
Then
        Rows(LigCol1).Select
        Selection.Insert shift:=xlDown
        Cells(LigCol1 + 1, 1).Select
        Selection.Cut
        Cells(LigCol1, 1).Select
        ActiveSheet.Paste
        LastRow = LastRow + 1
      End If
    End If                                                   '2b-IF5

  Next LigCol1   '''END SECONDARY LOOP

  MsgBox LastRow

End Sub

Variable names are in french but it's easy : consider marche is
floor : marchebasse = lowerfloor, marchehaute = upperfloor... in fact
marche means step but steps has many meaning in english and is a vba
keyword as well..., here it would
be stairway.

Ok, this is what I get when i run the code from above :

251120
                251130
251140
251145
272505  272505
272535  291101
291130
292100  292100

Although, the switch is completed for values 251120 and 251130
initialy on the same row, they are now on 2 distinct rows as stated
in  For LigCol1 = 1 To LastRow loop .

But when it comes to values 272535 and 291101, no new rows is added
as
it should for two different values on the same row. msgbox even show
the loop is going though these values as with 251120 and 251130
Could you point where I am missing something?

I would very much appreciate to understand why it's not working as
intended as it seems coherent from the msgbox checks when running it.
I think something is messing in the secondary loop block code.

Thanks,
Cyberuser

From Phillip London UK

This works for me

Sub DoData()
Dim vRng1, vRng2, vEvaluate As Variant
Dim NoMa As Long
Dim Ma As Long
Dim TempRng As Range

vRng1 = Sheet1.Range("A1:A7").Value 'change range as required
vRng2 = Sheet1.Range("B1:B4").Value ''change range as required
Range("B:B").Clear

For z = LBound(vRng2) To UBound(vRng2)
vEvaluate = Application.Evaluate("IF(ISNA(MATCH(" & CLng(vRng2(z,
1))& ",A:A,0)),1,0)")
If vEvaluate = 1 Then
NoMa = Application.Evaluate("Match(" & CLng(vRng2(z, 1)) &
",A:A,1)")
Set TempRng = Range("A1").Offset(NoMa, 0)
TempRng.EntireRow.Insert
TempRng.Offset(-1, 1).Value = CLng(vRng2(z, 1))
Else
Ma = Application.Evaluate("MATCH(" & CLng(vRng2(z, 1)) &
",A:A,0)")
Range("B1").Offset(Ma - 1, 0).Value = CLng(vRng2(z, 1))
End If
Next
End Sub
 
Rick Rothstein used his keyboard to write :
Thanks, but I still think there is a simpler underlying algorithm available
to solve this problem... I'll be looking again at this problem a little bit
later.

Rick Rothstein (MVP - Excel)

I'm interested to see what you come up with.

My offering was spawned by the feeling the the OP's approach was just
way more complicated than need be. I chose a rather simple, straight
forward approach that also was reasonably self-documenting (IMO).

The problem I see with our preferred approach of code brevity is that
it requires more comments so we don't have to 'study' the code to
remember what it's doing later on. Not a problem really because
so-called self-documented code may also require additional comments to
be properly understood.

Now (as you know) I like the brevity, though it's not often the best
approach for helping the OP.
 
--
Clif McIrvin

(clare reads his mail with moe, nomail feeds the bit bucket :-)

Hi,

I take these advices seriously. I'm currently having a difficult time
dealing with range of cells instead of one by one cells... The code in
this discussion helps to understand handling data into arrays
variables. I haven't made it to that level and it seems I need to
practice on specific tasks. I hope to get trought this first step and
not feel discouraged :( ):

Thx
Pascal
 
bpascal123 said:
Hi,

I take these advices seriously. I'm currently having a difficult time
dealing with range of cells instead of one by one cells... The code in
this discussion helps to understand handling data into arrays
variables. I haven't made it to that level and it seems I need to
practice on specific tasks. I hope to get trought this first step and
not feel discouraged :( ):

In my first six months or so of beginning to use VBA and macros I
experienced a lot of frustration. In my case, I had prior programming
experience but I knew next to nothing about either Excel or Visual
Basic. Because of the excellent advice and shared knowledge I found here
in these newsgroups I made it through the frustrations, and now feel
quite comfortable with the object model -- and, I must add, I'm
continually learning new things here! So -- don't expect too much of
yourself too soon, and you _will_ find yourself climbing the slopes of
the "learning curve"!

Here's another slightly different approach to solving your OP using
somewhat of a "brute force" attack in VBA; touching the worksheet itself
only to read in the original data and to write out the result. (I'm
simply working in the active worksheet - workbook and worksheet object
variables could easily be added.)

I tried to use enough line continuation characters so you don't have
problems with line wrap:

Sub AlignData()
'cm 3/17/11 using arrays
Dim C1in As Variant ' Initial Column A Values
Dim C2in As Variant ' Initial Column B Values
Dim Out As Variant ' Final Values
Dim LastC1in As Long ' Last Row
Dim LastC2in As Long
Dim ThisC1in As Long ' 'Current' Input Row
Dim ThisC2in As Long
Dim LastOut As Long ' 'Current' (Last Used) Output Row

LastC1in = Cells(Rows.Count, 1).End(xlUp).Row
LastC2in = Cells(Rows.Count, 2).End(xlUp).Row
With WorksheetFunction
C1in = .Transpose(Range(Cells(1, 1), Cells(LastC1in, 1)))
C2in = .Transpose(Range(Cells(1, 2), Cells(LastC2in, 2)))
End With

ThisC2in = 1
LastOut = 0
ReDim Out(1 To 2, 1 To 1) ' initialize variant array structure; _
'redim preserve fails without this

For ThisC1in = 1 To LastC1in
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works
Select Case C1in(ThisC1in) - IIf(ThisC2in > LastC2in, _
C1in(ThisC1in), C2in(ThisC2in))
Case Is < 0 ' C2 is Larger: Copy C1, C2 = Empty
Out(1, LastOut) = C1in(ThisC1in)
Case Is = 0 ' Same or finished w/ C2, copy both
Out(1, LastOut) = C1in(ThisC1in)
Out(2, LastOut) = C2in(ThisC2in)
ThisC2in = ThisC2in + 1
Case Is > 0 ' C1 is Larger: Copy C2, C1 = Empty
Out(2, LastOut) = C2in(ThisC2in)
ThisC2in = ThisC2in + 1
ThisC1in = ThisC1in - 1 ' C2 advanced, C1 must repeat
End Select 'Case C1in(ThisC1in) - C2in(ThisC2in)
Next ThisC1in '= 1 To LastC1in
Range(Cells(1), Cells(LastOut, 2)) = _
WorksheetFunction.Transpose(Out)

End Sub
 
Clif McIrvin said:
message [...]... those items were left overs from (several) previous attempts
to create the code and resulted from my failure to clean up my code
correctly. Thanks for noticing them.

I like the way you squeeze code until the excess stops dripping out
:-)

I may post back again this evening or sometime with the code I was
thinking up and ask for your thoughts re: comparison of the different
methods.

Well, it's "sometime" <grin>.

Rick, (anyone else who cares to, for that matter!) I'd be much
interested in any comments you have on the merits (or "demerits") of
this approach contrasted with your approach.

Here's a copy of a reply to the OP I just posted in another branch of
this thread:

Here's another slightly different approach to solving your OP using
somewhat of a "brute force" attack in VBA; touching the worksheet itself
only to read in the original data and to write out the result. (I'm
simply working in the active worksheet - workbook and worksheet object
variables could easily be added.)

I tried to use enough line continuation characters so you don't have
problems with line wrap:

Sub AlignData()
'cm 3/17/11 using arrays
Dim C1in As Variant ' Initial Column A Values
Dim C2in As Variant ' Initial Column B Values
Dim Out As Variant ' Final Values
Dim LastC1in As Long ' Last Row
Dim LastC2in As Long
Dim ThisC1in As Long ' 'Current' Input Row
Dim ThisC2in As Long
Dim LastOut As Long ' 'Current' (Last Used) Output Row

LastC1in = Cells(Rows.Count, 1).End(xlUp).Row
LastC2in = Cells(Rows.Count, 2).End(xlUp).Row
With WorksheetFunction
C1in = .Transpose(Range(Cells(1, 1), Cells(LastC1in, 1)))
C2in = .Transpose(Range(Cells(1, 2), Cells(LastC2in, 2)))
End With

ThisC2in = 1
LastOut = 0
ReDim Out(1 To 2, 1 To 1) ' initialize variant array structure; _
'redim preserve fails without this

For ThisC1in = 1 To LastC1in
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works
Select Case C1in(ThisC1in) - IIf(ThisC2in > LastC2in, _
C1in(ThisC1in), C2in(ThisC2in))
Case Is < 0 ' C2 is Larger: Copy C1, C2 = Empty
Out(1, LastOut) = C1in(ThisC1in)
Case Is = 0 ' Same or finished w/ C2, copy both
Out(1, LastOut) = C1in(ThisC1in)
Out(2, LastOut) = C2in(ThisC2in)
ThisC2in = ThisC2in + 1
Case Is > 0 ' C1 is Larger: Copy C2, C1 = Empty
Out(2, LastOut) = C2in(ThisC2in)
ThisC2in = ThisC2in + 1
ThisC1in = ThisC1in - 1 ' C2 advanced, C1 must repeat
End Select 'Case C1in(ThisC1in) - C2in(ThisC2in)
Next ThisC1in '= 1 To LastC1in
Range(Cells(1), Cells(LastOut, 2)) = _
WorksheetFunction.Transpose(Out)

End Sub
 
That looks similar to what I posted the first time. I later retracted it
because, while it worked with the given data, it failed to work with this
set of data...

251120 251111
251140 272222
251145 293333
272505 294444
272535
291130
292100

Rick Rothstein (MVP - Excel)



"Clif McIrvin" wrote in message

Clif McIrvin said:
message [...]... those items were left overs from (several) previous attempts
to create the code and resulted from my failure to clean up my code
correctly. Thanks for noticing them.

I like the way you squeeze code until the excess stops dripping out
:-)

I may post back again this evening or sometime with the code I was
thinking up and ask for your thoughts re: comparison of the different
methods.

Well , it 's "sometime" <grin>.

Rick, (anyone else who cares to, for that matter!) I'd be much
interested in any comments you have on the merits (or "demerits") of
this approach contrasted with your approach.

Here 's a copy of a reply to the OP I just posted in another branch of
this thread:

Here 's another slightly different approach to solving your OP using
somewhat of a "brute force" attack in VBA; touching the worksheet itself
only to read in the original data and to write out the result. (I'm
simply working in the active worksheet - workbook and worksheet object
variables could easily be added.)

I tried to use enough line continuation characters so you don't have
problems with line wrap:

Sub AlignData()
'cm 3/17/11 using arrays
Dim C1in As Variant ' Initial Column A Values
Dim C2in As Variant ' Initial Column B Values
Dim Out As Variant ' Final Values
Dim LastC1in As Long ' Last Row
Dim LastC2in As Long
Dim ThisC1in As Long ' 'Current' Input Row
Dim ThisC2in As Long
Dim LastOut As Long ' 'Current' (Last Used) Output Row

LastC1in = Cells(Rows.Count, 1).End(xlUp).Row
LastC2in = Cells(Rows.Count, 2).End(xlUp).Row
With WorksheetFunction
C1in = .Transpose(Range(Cells(1, 1), Cells(LastC1in, 1)))
C2in = .Transpose(Range(Cells(1, 2), Cells(LastC2in, 2)))
End With

ThisC2in = 1
LastOut = 0
ReDim Out(1 To 2, 1 To 1) ' initialize variant array structure; _
'redim preserve fails without this

For ThisC1in = 1 To LastC1in
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works
Select Case C1in(ThisC1in) - IIf(ThisC2in > LastC2in, _
C1in(ThisC1in), C2in(ThisC2in))
Case Is < 0 ' C2 is Larger: Copy C1, C2 = Empty
Out(1, LastOut) = C1in(ThisC1in)
Case Is = 0 ' Same or finished w/ C2, copy both
Out(1, LastOut) = C1in(ThisC1in)
Out(2, LastOut) = C2in(ThisC2in)
ThisC2in = ThisC2in + 1
Case Is > 0 ' C1 is Larger: Copy C2, C1 = Empty
Out(2, LastOut) = C2in(ThisC2in)
ThisC2in = ThisC2in + 1
ThisC1in = ThisC1in - 1 ' C2 advanced, C1 must repeat
End Select 'Case C1in(ThisC1in) - C2in(ThisC2in)
Next ThisC1in '= 1 To LastC1in
Range(Cells(1), Cells(LastOut, 2)) = _
WorksheetFunction.Transpose(Out)

End Sub
 
Rick Rothstein submitted this idea :
That looks similar to what I posted the first time. I later retracted it
because, while it worked with the given data, it failed to work with this set
of data...

251120 251111
251140 272222
251145 293333
272505 294444
272535
291130
292100

Rick,
With this set of data, your revised version errors out on the line...

Columns("A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete

...and so works if we wrap this in On Error Resume Next and On Error
GoTo 0 statements.
 
GS said:
Rick Rothstein submitted this idea :

Rick,
With this set of data, your revised version errors out on the line...

Columns("A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete

..and so works if we wrap this in On Error Resume Next and On Error
GoTo 0 statements.


??? Rick's revised version as posted works just fine over here.
 
Rick Rothstein said:
That looks similar to what I posted the first time. I later retracted
it because, while it worked with the given data, it failed to work
with this set of data...

251120 251111
251140 272222
251145 293333
272505 294444
272535
291130
292100


Thanks, Rick. I realized that after I'd turned the computer off and
gone home yesterday. In fact, the code I posted only works if both
columns have the same final value.

Staying with the attempt to do all the work inside VBA (I still don't
know if that's a good idea or a bad idea --- I suppose that might depend
on what else is involved. I have read more than once that using
worksheet functions is generally considerably faster than doing the same
thing in VBA.) here's a re-work that I believe handles all cases - I
tested three combinations of last value, also tested with string data
instead of numbers.

Sub AlignData2()
'cm 3/19/11 using arrays
Dim ColAin As Variant ' Initial Column A Values
Dim ColBin As Variant ' Initial Column B Values
Dim Out As Variant ' Final Values
Dim LastColAin As Long ' Last Row
Dim LastColBin As Long
Dim LastOut As Long ' 'Current' (Last Used) Output Row
Dim idxColAin As Long ' 'Current' Input Row Index Pointer
Dim idxColBin As Long
Dim ThisColAin As Variant ' 'Current' Input Value
Dim ThisColBin As Variant

LastColAin = Cells(Rows.Count, 1).End(xlUp).Row
LastColBin = Cells(Rows.Count, 2).End(xlUp).Row
With WorksheetFunction
ColAin = .Transpose(Range(Cells(1, 1), Cells(LastColAin, 1)))
ColBin = .Transpose(Range(Cells(1, 2), Cells(LastColBin, 2)))
End With

LastOut = 0
idxColAin = 1
idxColBin = 1

ReDim Out(1 To 2, 1 To 1) ' initialize variant array structure; _
'redim preserve fails without this

'For idxColAin = 1 To LastColAin
Do
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works

If idxColAin > LastColAin Then
ThisColAin = Empty
Else
ThisColAin = ColAin(idxColAin)
End If

If idxColBin > LastColBin Then
ThisColBin = Empty
Else
ThisColBin = ColBin(idxColBin)
End If

If IIf(IsEmpty(ThisColBin), ThisColAin, ThisColBin) > _
IIf(IsEmpty(ThisColAin), ThisColBin, ThisColAin) Then
' ColB is Larger: Copy ColA, ColB = Empty
Out(1, LastOut) = ThisColAin
idxColAin = idxColAin + 1
ElseIf IIf(IsEmpty(ThisColBin), ThisColAin, ThisColBin) = ThisColAin
Then
' Same, copy both
Out(1, LastOut) = ThisColAin
Out(2, LastOut) = ThisColBin
idxColAin = idxColAin + 1
idxColBin = idxColBin + 1
Else ' ColA is Larger: Copy ColB, ColA = Empty
Out(2, LastOut) = ThisColBin
idxColBin = idxColBin + 1
End If ' ThisColBin <??> ThisColAin
Loop Until IsEmpty(ThisColAin) And IsEmpty(ThisColBin)
Range(Cells(1), Cells(LastOut, 2)) = _
WorksheetFunction.Transpose(Out)

End Sub
 
GS formulated the question :
Clif McIrvin laid this down on his screen :

Did you test using his suggested (non-dupe) data set above?

Well I'll be..! Today it works just fine!
 
GS wrote :
Rick Rothstein submitted this idea :

Rick,
With this set of data, your revised version errors out on the line...

Columns("A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete

..and so works if we wrap this in On Error Resume Next and On Error GoTo 0
statements.

Seems to work fine today! Can't repeat condition I got the error..!
 
Clif McIrvin said:
In my first six months or so of beginning to use VBA and macros I
experienced a lot of frustration. In my case, I had prior programming
experience but I knew next to nothing about either Excel or Visual
Basic. Because of the excellent advice and shared knowledge I found
here in these newsgroups I made it through the frustrations, and now
feel quite comfortable with the object model -- and, I must add, I'm
continually learning new things here! So -- don't expect too much of
yourself too soon, and you _will_ find yourself climbing the slopes of
the "learning curve"!

Here's another slightly different approach to solving your OP using
somewhat of a "brute force" attack in VBA; touching the worksheet
itself


The code I posted earlier only returned the correct result if both
columns contained the same final value.

This has been revised and tightened up somewhat .... still not as
compact as the solution that Rick posted, though. Like Rick's solution,
this will return the expected result regardless of which column contains
more values. Unlike Rick's solution, this does require that the columns
are already sorted (although he did show you how to sort the data at the
beginning of the procedure.)

Sub AlignData()
'cm 3/18/11 using arrays
Dim ColAin As Variant ' Initial Column A Values
Dim ColBin As Variant ' Initial Column B Values
Dim Out As Variant ' Final Values
Dim LastColAin As Long ' Last Row
Dim LastColBin As Long
Dim LastOut As Long ' 'Current' (Last Used) Output Row
Dim idxColAin As Long ' 'Current' Input Row Index Pointer
Dim idxColBin As Long
Dim ThisColAin As Variant ' 'Current' Input Value
Dim ThisColBin As Variant

With WorksheetFunction
ColAin = .Transpose(Range("A1:A" & Cells(Rows.Count, _
"A").End(xlUp).Row + 1))
ColBin = .Transpose(Range("B1:B" & Cells(Rows.Count, _
"B").End(xlUp).Row + 1))
End With

LastOut = 0
idxColAin = 1
idxColBin = 1

ReDim Out(1 To 2, 1 To 1) ' initialize variant array structure; _
'redim preserve fails without this

Do
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works

ThisColAin = ColAin(idxColAin)
ThisColBin = ColBin(idxColBin)
If IIf(IsEmpty(ThisColBin), ThisColAin, ThisColBin) > _
IIf(IsEmpty(ThisColAin), ThisColBin, ThisColAin) Then
' ColB is Larger: Copy ColA, ColB = Empty
Out(1, LastOut) = ThisColAin
idxColAin = idxColAin + 1
ElseIf IIf(IsEmpty(ThisColBin), ThisColAin, ThisColBin) _
= ThisColAin Then
' Same, copy both
Out(1, LastOut) = ThisColAin
Out(2, LastOut) = ThisColBin
idxColAin = idxColAin + 1
idxColBin = idxColBin + 1
Else ' ColA is Larger: Copy ColB, ColA = Empty
Out(2, LastOut) = ThisColBin
idxColBin = idxColBin + 1
End If ' ThisColBin <??> ThisColAin
Loop Until IsEmpty(ThisColAin) And IsEmpty(ThisColBin)
Range(Cells(1), Cells(LastOut, 2)) = _
WorksheetFunction.Transpose(Out)

End Sub
 
GS said:
GS formulated the question :

Well I'll be..! Today it works just fine!


The plot thickens.

Today I am receiving the error you describe with non-repeating data.
 
Clif McIrvin laid this down on his screen :
The plot thickens.

Today I am receiving the error you describe with non-repeating data.

Geez.., does this mean we have a virus?<bg> ;-)
 
Rick Rothstein said:
Thanks, but I still think there is a simpler underlying algorithm
available to solve this problem... I'll be looking again at this
problem a little bit later.


After getting my code working correctly, I still had one pesky question
rattling about inside my skull that just wouldn't go away:

Was there any really significant difference between my approach of doing
all the work "inside VBA" using arrays as contrasted with Rick's
approach of using worksheet methods?

That question bothered me enough I put together a little test to check
execution time. In the process, when I developed a random data generator
that resulted in sample data that contained *no* repeating values I
encountered the same error in Rick's code that GS reported - so I
implemented his solution of On Error Resume Next ... On Error Goto 0.
Likewise, my routine errored out when the array indices exceeded the
upper bound of the array. There, I had the choice of adding code to
place an upper limit on the array indices, or using "Resume Next". I
chose the latter.

Also, I discovered that execution time was fast enough that using the
Time function was useless ... so I borrowed a timeGetTime declared
function that I noticed Wouter post in a different thread recently.
Thanks, Wouter!

Results (looks better in notepad)
The second column is Rick's code, the third is mine:

Trial Elapsed time w/ range methods Elapsed time using arrays
1 27500 31
2 28703 31
3 30906 16
4 33016 31
5 34656 31
Average 30956 28

1200 values in Column A
800 values in Column B
Time in milliseconds
no duplicates (=rand())

After obtaining these results, I realized that my test data algorithm
was not producing any repeating values, so I added a ROUND function to
force duplicates and repeated the test:

Trial Elapsed time w/ range methods Elapsed time using arrays
1 7984 16
2 19531 16
3 25375 16
4 24984 16
5 25109 32
Average 20597 19

1200 values in Column A
800 values in Column B
Time in milliseconds
3 decmal places

The code I used follows. To repeat the test, paste all the following
code into a code module, and execute [ RunTest ].

The test parameters are all Constant declarations at the top of the
module; [ SetupTest ] contains some comment blocks that can be switched
around if you wish to keep copies of the test data worksheets so you can
see the data used in the trials.

As always, watch out for broken (wrapped) lines.

============== begin code ============

Option Explicit

Const TestRows As Long = 12
Const TestRows2 As Long = 8
Const NumberOfTrials As Long = 3
Const numDigits As Long = 3 ' number of places in random value

Declare Function timeGetTime Lib "winmm.dll" () As Long
'timeGetTime thanks to Wouter

Sub RunTest()

Dim elapsedTime(1 To 2) As Long
Dim startTime As Long
Dim stopTime As Long
Dim resultsRow As Long
Dim trialNumber As Long

Dim Results As Worksheet
Dim Test1 As Worksheet
Dim Test2 As Worksheet

Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlManual

Set Results = SetupResults
resultsRow = 2

With Sheets
Set Test1 = .Add(After:=Sheets(.Count))
Set Test2 = .Add(After:=Sheets(.Count))
End With

For trialNumber = 1 To NumberOfTrials
SetupTest Test1, Test2, trialNumber

Test1.Activate
startTime = timeGetTime
AlignColumnData
stopTime = timeGetTime
elapsedTime(1) = (stopTime - startTime)

Test2.Activate
startTime = timeGetTime
AlignData
stopTime = timeGetTime
elapsedTime(2) = (stopTime - startTime)

With Results.Rows(resultsRow)
.Cells(1) = trialNumber
.Cells(2) = elapsedTime(1)
.Cells(3) = elapsedTime(2)
End With
resultsRow = resultsRow + 1

Next trialNumber

With Results.Rows(resultsRow)
.Cells(1) = "Average"
.Cells(2) = "=AVERAGE(B2:B" & .Row - 1 & ")"
.Cells(2).AutoFill Destination:= _
Range(.Cells(2), .Cells(3)), Type:=xlFillDefault
End With

With Results
.Cells(resultsRow + 2, 2) = TestRows & " values in Column A"
.Cells(resultsRow + 3, 2) = TestRows2 & " values in Column B"
.Cells(resultsRow + 4, 2) = "Time in milliseconds"
End With

Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlAutomatic
Results.Activate

End Sub

Function SetupResults() As Worksheet

Set SetupResults = Sheets.Add(After:=Sheets(Sheets.Count))
With SetupResults
.Cells(1).Formula = "Trial"
.Cells(2).Formula = "Elapsed time w/ range methods"
.Cells(3).Formula = "Elapsed time using arrays"
With .Columns("B:C")
.ColumnWidth = 15.43
'.NumberFormat = "0.00000"
.NumberFormat = "0"
.HorizontalAlignment = xlCenter
End With
With .Range("B1:C1")
.WrapText = True
End With
.Columns("A:A").HorizontalAlignment = xlCenter
.Name = "Results"
End With
End Function

Sub SetupTest(ByRef Test1 As Worksheet, _
ByRef Test2 As Worksheet, _
trialNumber As Long)

'''''''''''''''''''''''''''''''''''
' keep each trial worksheet
'Set Test1 = Sheets.Add(After:=Sheets(Sheets.Count))
'''''''''''''''''''''''''''''''''''

With Test1
.Cells(1).CurrentRegion.Clear
.Name = "Trial" & trialNumber
.Range(.Cells(1), .Cells(TestRows, 2)).Formula = _
"=ROUND(RAND()," & numDigits & ")"
.Calculate
With .Cells(1).CurrentRegion
.Copy
.PasteSpecial Paste:=xlPasteValues
End With
Application.CutCopyMode = False
.Range(.Cells(TestRows2 + 1, 2), .Cells(TestRows, 2)).Clear
.Columns(1).Sort .Cells(1, 1), xlAscending
.Columns(2).Sort .Cells(1, 2), xlAscending

'''''''''''''''''''''''''''''''''''
' keep each trial worksheet
' .Copy After:=Sheets(.Index)
' Set Test2 = Sheets(.Index + 1)
'''''''''''''''''''''''''''''''''''

'''''''''''''''''''''''''''''''''''
' keep sheets of final trial only
Test2.Cells(1).CurrentRegion.Clear
.Cells(1).CurrentRegion.Copy _
Destination:=Test2.Cells(1)
'''''''''''''''''''''''''''''''''''

End With
End Sub

Sub AlignData()
'cm 3/18/11 using arrays
Dim ColAin As Variant ' Initial Column A Values
Dim ColBin As Variant ' Initial Column B Values
Dim Out As Variant ' Final Values
Dim LastColAin As Long ' Last Row
Dim LastColBin As Long
Dim LastOut As Long ' 'Current' (Last Used) Output Row
Dim idxColAin As Long ' 'Current' Input Row Index Pointer
Dim idxColBin As Long
Dim ThisColAin As Variant ' 'Current' Input Value
Dim ThisColBin As Variant

With WorksheetFunction
ColAin = .Transpose(Range("A1:A" & Cells(Rows.Count, _
"A").End(xlUp).Row + 1))
ColBin = .Transpose(Range("B1:B" & Cells(Rows.Count, _
"B").End(xlUp).Row + 1))
End With

LastOut = 0
idxColAin = 1
idxColBin = 1

ReDim Out(1 To 2, 1 To 1) ' initialize variant array structure; _
'redim preserve fails without this

Do
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works

On Error Resume Next
ThisColAin = ColAin(idxColAin)
ThisColBin = ColBin(idxColBin)
On Error GoTo 0

If IIf(IsEmpty(ThisColBin), ThisColAin, ThisColBin) > _
IIf(IsEmpty(ThisColAin), ThisColBin, ThisColAin) Then
' ColB is Larger: Copy ColA, ColB = Empty
Out(1, LastOut) = ThisColAin
idxColAin = idxColAin + 1
ElseIf IIf(IsEmpty(ThisColBin), ThisColAin, ThisColBin) _
= ThisColAin Then
' Same, copy both
Out(1, LastOut) = ThisColAin
Out(2, LastOut) = ThisColBin
idxColAin = idxColAin + 1
idxColBin = idxColBin + 1
Else ' ColA is Larger: Copy ColB, ColA = Empty
Out(2, LastOut) = ThisColBin
idxColBin = idxColBin + 1
End If ' ThisColBin <??> ThisColAin
Loop Until IsEmpty(ThisColAin) And IsEmpty(ThisColBin)
Range(Cells(1), Cells(LastOut, 2)) = _
WorksheetFunction.Transpose(Out)

End Sub


Sub AlignColumnData()
' Rick Rothstein Mon, 14 Mar 2011 20:21:08 -0400 [7:21 pm]
'Newsgroups: microsoft.public.Excel.programming
'Subject: Re: Align cells with same value - vba almost working
'Date: Tue, 15 Mar 2011 10:06:04 -0400 [9:06 am]
'comments added by cm
Dim X As Long, Data As Variant, Cell As Range
With Range("B1:B" & Cells(Rows.Count, "B").End(xlUp).Row)
Data = WorksheetFunction.Transpose(.Cells)
.Copy Cells(Rows.Count, "A").End(xlUp).Offset(1) ' copy "B" below
"A"
.Clear
End With
Columns("A").Sort Range("A1"), xlAscending
For X = 2 To Cells(Rows.Count, "A").End(xlUp).Row ' move dupes aligned
to col B
With Cells(X, "A")
If .Value = Cells(X - 1, "A").Value Then
.Offset(-1, 1).Value = Cells(X, "A").Value
.Clear
End If
End With
Next
On Error Resume Next
Columns("A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete ' remove
empty rows
On Error GoTo 0
For X = LBound(Data) To UBound(Data) ' move unmatched B data aligned
to col B
With Columns("A").Find(Data(X), LookAt:=xlWhole)
' if B not empty then found value was (and now is aligned) in both
A and B
' if B is empty this found value was in B not in A so move it back
to B
If Len(.Offset(0, 1).Value) = 0 Then
.Copy .Offset(0, 1)
.Clear
End If
End With
Next
End Sub
 
Clif McIrvin said:
After getting my code working correctly, I still had one pesky
question rattling about inside my skull that just wouldn't go away:

Was there any really significant difference between my approach of
doing all the work "inside VBA" using arrays as contrasted with Rick's
approach of using worksheet methods?

That question bothered me enough I put together a little test to check
execution time.

[ ]

I forgot to add that if anyone wishes to compare any of the other
solutions posted in this thread I constrtucted my [ RunTest ], [
SetupResults ] and [ SetupTest ] procedures so that additional
procedures can be added without too much trouble (I hope! <g> ).
 
[ ]
Geez.., does this mean we have a virus?<bg> ;-)


I did some more testing ... I thought I'd run it against his posted
sample data, but now it fails every time.

(btw; did you happen to see the execution time comparison I posted
elsewhere in this thread?)
 
Clif McIrvin brought next idea :
[ ]
Geez.., does this mean we have a virus?<bg> ;-)


I did some more testing ... I thought I'd run it against his posted sample
data, but now it fails every time.

I added a line to sort colB before loading it into the array so both
cols of data were sorted. I think the error comes with having empty
cells included in the array, but I wouldn't think that should matter
since the loop would just skip over those elements, -right?
(btw; did you happen to see the execution time comparison I posted elsewhere
in this thread?)

Yes, I saw the execution times you posted. Impressive! Not sure whether
I'm interested in testdriving it though. Just can't imagine having that
much data to process in a spreadsheet. Not saying it's not gonna
happen, just not typical of the type of work I do for clients. A MDB
and data controls is a better approach <IMO> for large amounts of data.
Even when the data store is a plain text file, it's easier to use data
controls than read/write cells. <..again, IMO>
 
[ reply inline ]

GS said:
Clif McIrvin brought next idea :
[ ]
??? Rick's revised version as posted works just fine over here.

Did you test using his suggested (non-dupe) data set above?

Well I'll be..! Today it works just fine!



The plot thickens.

Today I am receiving the error you describe with non-repeating
data.

Geez.., does this mean we have a virus?<bg> ;-)


I did some more testing ... I thought I'd run it against his posted
sample data, but now it fails every time.

I added a line to sort colB before loading it into the array so both
cols of data were sorted. I think the error comes with having empty
cells included in the array, but I wouldn't think that should matter
since the loop would just skip over those elements, -right?

In Rick's code, he copies Col B below A, then sorts ... and uses the
..Find method to ID the Col B values ... so there is no advantage to
pre-sorting Col B. As near as I could tell, the error is due to the
fact that there are *no* empty cells (ie, no duplicated values) after
his first loop.

After some thought, I replaced your suggested ' Resume Next ' with a
boolean flag -- that seemed to me to introduce less execution overhead,
but I really don't know. I added one line below the .Clear in the first
loop, then wrapped the line that errors in an IF:

Dim movedDuplicateValues As Boolean
....
movedDuplicateValues = True
....
If movedDuplicateValues Then
Columns("A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete '
remove empty rows
End If


As to the error in my code, it was due to faulty logic in my testing for
end of data. I was already adding an empty cell to the end of my array
to handle running past the end of data, but my attempt at working
regardless of which column had more values wasn't all the way home. I
ended up revising my IF ... ELSEIF construct (reposted below):
Yes, I saw the execution times you posted. Impressive! Not sure
whether I'm interested in testdriving it though. Just can't imagine
having that much data to process in a spreadsheet. Not saying it's not
gonna happen, just not typical of the type of work I do for clients. A
MDB and data controls is a better approach <IMO> for large amounts of
data. Even when the data store is a plain text file, it's easier to
use data controls than read/write cells. <..again, IMO>

I agree on both points. I guess I was just too curious what the
difference was to leave it alone! <g>

Clif

(code snippet)

Do
LastOut = LastOut + 1
ReDim Preserve Out(1 To 2, 1 To LastOut) ' columns, rows _
'because of how preserve works
ThisColAin = ColAin(idxColAin)
ThisColBin = ColBin(idxColBin)
If Not IsEmpty(ThisColAin) And IsEmpty(ThisColBin) Then
' Copy ColA, ColB = Empty
Out(1, LastOut) = ThisColAin
idxColAin = idxColAin + 1
ElseIf Not IsEmpty(ThisColBin) And IsEmpty(ThisColAin) Then
' Copy ColB, ColA = Empty
Out(2, LastOut) = ThisColBin
idxColBin = idxColBin + 1
ElseIf ThisColBin > ThisColAin Then
' ColB is Larger: Copy ColA, ColB = Empty
Out(1, LastOut) = ThisColAin
idxColAin = idxColAin + 1
ElseIf ThisColBin = ThisColAin Then
' Same, copy both
Out(1, LastOut) = ThisColAin
Out(2, LastOut) = ThisColBin
idxColAin = idxColAin + 1
idxColBin = idxColBin + 1
Else ' ColA is Larger: Copy ColB, ColA = Empty
Out(2, LastOut) = ThisColBin
idxColBin = idxColBin + 1
End If ' ThisColBin <??> ThisColAin
Loop Until IsEmpty(ThisColAin) And IsEmpty(ThisColBin)
 
Back
Top