VB code needed for sorting function - please help!

monica S

New Member
Joined
Apr 16, 2004
Messages
39
Hi,

This has been driving me crazy... hopefully someone out there can help.

If I have an ORIGINAL range of dates in cells A1:A5:

22-Apr-04
30-Apr-04
22-May-04
22-Aug-04
22-Sep-04

and a TARGET date of say 16-Jun-04 in cell B1.

I would like to have a FUNCTION that will 'read' the OriginalDates, take the TargetDate into account, and then just INCLUDE or INSERT the target date among this original range of dates. The function would return the new dates in ascending order (i.e. with dates closest to now coming first and dates furthest from now coming last). The function would have to be typed in as an ARRAY function (with the { } bracketing the formula and closing it using Ctrl+Shift+Enter) and would return 1 more row than what is passed through OriginalDates argument.

so, the function would have two arguments and go something like this:

{=IncludeDate(OriginalDates,TargetDate)}

or;

{=(A1:A5,B1)}

In our example, the function would return the following sorted set of dates (with the June 16, 2004 date included and SORTED among the original set of dates):

22-Apr-04
30-Apr-04
22-May-04
*16-Jun-04*
22-Aug-04
22-Sep-04

in cells C1:C6 as a NEW OUTPUT RANGE of dates.

In this case, we would copy our function down 6 rows (one more than the OriginalDates) and close it with the Ctrl+Shift+Enter to make it an ARRAY of 6 rows.

A MACRO WILL NOT HELP IN THIS CASE. ALSO, USING THE DATA --> SORTING FUNCTIONALITY WILL NOT HELP IN THIS CASE. TRUE, BOTH WILL WORK, BUT THIS PROJECT CALLS FOR A **FUNCTION** TO HANDLE THE TASK.

Any ideas on how to go about doing this?

Thank you very much!
~ Monica
 
Ponsy,

I needed to leave for a Cub Scout's meeting (it's hard to convince my lad of the virtue of punctuality when I'm not) so's I took Nate's word for it. But you are correct. For me as well this
<font face=Courier New>

<SPAN style="color:#00007F">Sub</SPAN> ImaSpecialDeleter()
    <SPAN style="color:#00007F">Dim</SPAN> r <SPAN style="color:#00007F">As</SPAN> Range
    <SPAN style="color:#00007F">Set</SPAN> r = [A1:A10]
    r.SpecialCells(xlCellTypeConstants, XlSpecialCellsValue.xlNumbers).Delete shift:=xlShiftUp
<SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">Sub</SPAN>
</FONT>
only deletes out of column A, leaving the contents of column B intact. If XL2000 does indeed behave differently (I can't test this without pesterin' coworkers) its a point we should all keep in mind if we'd use this combination of methods in a macro that'd be run on XL2000.

Nate/Monica,

I tried editing the tail end of this (old name preserved, I'm sentimental) but it's not solving the problem. If you breakpoint at End Function and View Locals, newArr is an array of Variant/Date but somehow the Tranpose function forces Fubar2 to be Variant/String. Set the breakpoint before the last loop and view locals and you can see Swap1 switch from Variant/String to Variant/Date, but the change doesn't "stick".
This whole thing is even more puzzling because using the Transpose() function in worksheet cells directly on cells with dates does not result in conversion to string. Monica, if your use of DateValue is working, I'd say keep using that. Unless someone else around here can fix the code, I must hang my head in defeat.

Attempted solution (I recycled Swap1 since it was handy):
<font face=Courier New>    <SPAN style="color:#00007F">ReDim</SPAN> newArr(0 <SPAN style="color:#00007F">To</SPAN> (oldDates.Count + newDates.Count - 1))
    <SPAN style="color:#00007F">For</SPAN> i = 0 <SPAN style="color:#00007F">To</SPAN> myCol.Count - 1
        newArr(i) = myCol(i + 1)
    <SPAN style="color:#00007F">Next</SPAN> i
    <SPAN style="color:#00007F">For</SPAN> i = myCol.Count <SPAN style="color:#00007F">To</SPAN> <SPAN style="color:#00007F">UBound</SPAN>(newArr)
        newArr(i) = 0
    <SPAN style="color:#00007F">Next</SPAN> i
    <SPAN style="color:#00007F">Set</SPAN> myCol = <SPAN style="color:#00007F">Nothing</SPAN>
    Fubar2 = Application.WorksheetFunction.Transpose(newArr)
    <SPAN style="color:#00007F">For</SPAN> <SPAN style="color:#00007F">Each</SPAN> Swap1 <SPAN style="color:#00007F">In</SPAN> Fubar2
        Swap1 = <SPAN style="color:#00007F">CDate</SPAN>(Swap1)
    <SPAN style="color:#00007F">Next</SPAN> Swap1</FONT>
 
Upvote 0

Excel Facts

What does custom number format of ;;; mean?
Three semi-colons will hide the value in the cell. Although most people use white font instead.
Ladies and Gentlemen,

I am pleased to announce that in spite of her preoccupation with lumberjacks, Ponsy Nob. has managed to stumble into something reasonable with respect to a semantic in this thread (while it has broader implications). My statement about Delete and Special Cells was too general. Here's the deal, take the following:

<font face=Courier New><SPAN style="color:darkblue">Sub</SPAN> tester()
[a1:a10].SpecialCells(xlVisible).Delete xlUp
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">Sub</SPAN></FONT>

1) Works fine when cells are hidden normally, as they were in my improved process.

2) Does not work when the cells are visible due to filtering.

So, be on alert as to why your cells are visble. I over applied my findings.

Next.

Greg,

1) There is no reason to transpose the array, sometimes it is necessary, this is not one of those times. This is bollox expensive. Stack a 2d array, I have shown the preferred approach in a prior post to this thread.

2) Another loop?! Lord have mercy. How many loops can throw into this ugly process?

3) Transpose has nothing to do with the elements coming across as strings. E.g.,

<font face=Courier New><SPAN style="color:darkblue">Sub</SPAN> fubar3()
<SPAN style="color:darkblue">Dim</SPAN> i <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Variant</SPAN>
<SPAN style="color:darkblue">Let</SPAN> i = [{1;2;3}]
<SPAN style="color:darkblue">Let</SPAN> i = WorksheetFunction.Transpose(i)
MsgBox TypeName(i(1))
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">Sub</SPAN></FONT>

Also posted in a previous post to this thread, the culprit is your use of the the Format Function. See the help file.

Monica, please do not use the UDFs in this thread. I feel very guilty about this, as we all should. You have a UDF that performs very badly and to make matters worse, you neither understand the semantics of it nor know how to maintain it, a bad situation all around. At the very least, please pull my name from the comments, I want nothing to do with it. The name of the function, in all seriousness, should be the least of your worries.
 
Upvote 0
Monica,

Nate's chastising notwithstanding another loop (:lookaway:) will actually allow us to flip these back. Add a new Dim up top...

<font face=Courier New>    <SPAN style="color:#00007F">Dim</SPAN> varTemp <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
    <SPAN style="color:#007F00">'...</SPAN>
    varTemp = Application.WorksheetFunction.Transpose(newArr)
    <SPAN style="color:#00007F">For</SPAN> i = <SPAN style="color:#00007F">LBound</SPAN>(varTemp) <SPAN style="color:#00007F">To</SPAN> <SPAN style="color:#00007F">UBound</SPAN>(varTemp)
        <SPAN style="color:#00007F">If</SPAN> varTemp(i, 1) <> "" <SPAN style="color:#00007F">Then</SPAN> varTemp(i, 1) = <SPAN style="color:#00007F">CDate</SPAN>(varTemp(i, 1))
    <SPAN style="color:#00007F">Next</SPAN> i
    Fubar2 = varTemp</FONT>
<hr>
Nate:
<font face=Courier New>
<SPAN style="color:#00007F">Sub</SPAN> fubar4()
    <SPAN style="color:#00007F">Dim</SPAN> i <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
    <SPAN style="color:#00007F">Let</SPAN> i = Array(#1/1/2000#, #2/1/2000#, #3/1/2000#)
    MsgBox TypeName(i(1))       <SPAN style="color:#007F00">' date</SPAN>
    <SPAN style="color:#00007F">Let</SPAN> i = WorksheetFunction.Transpose(i)
    MsgBox TypeName(i(1, 1))    <SPAN style="color:#007F00">' string</SPAN>
<SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">Sub</SPAN>

</FONT>
 
Upvote 0
Greg, I'm trying to get people to focus on how nasty this sordid business is. Nice obviously wasn't working. So, I'll continue with the new theme of my postings.

Ugly. Still, you should:

1) Not be transposing. Please explain why are you doing this?
2) Using longs
3) Not be adding loops to a function which loops, then has a double loop, then another loop in your addendum to my example which should not be implemented here, and yet another loop on your most recent addendum.

Ignorance is bliss; otherwise, this numbs the brain.
 
Upvote 0
Nate,

Used the ruddy Transpose() because I'm still getting used to moving items back and forth from cells to arrays and vice-versa, especially via an array-entered UDF. The way Excel kicks this stuff to a 2-D array is taking me some getting-used to, but I am learning plenty in this one. So, yeah, I guess it's ignorance. But your chiding is not falling on deaf ears. I think this is at least moderately better. Does away with that last loop and with the Transpose by doing what you suggested - stacking directly into a 2-D array. The part that took me some trial and error while viewing locals was figuring out how to dimension the 2-D array so that it would return what Excel wanted to dump properly into the cells.

<font face=Courier New>

<SPAN style="color:#00007F">Function</SPAN> udfSortDate2(<SPAN style="color:#00007F">ByVal</SPAN> oldDates <SPAN style="color:#00007F">As</SPAN> Range, _
                      <SPAN style="color:#00007F">ByVal</SPAN> newDates <SPAN style="color:#00007F">As</SPAN> Range) <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
                
    <SPAN style="color:#00007F">Dim</SPAN> oldArr <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>, newArr <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
    <SPAN style="color:#00007F">Dim</SPAN> myCol <SPAN style="color:#00007F">As</SPAN> Collection, i <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Long</SPAN>, j <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Long</SPAN>
    <SPAN style="color:#00007F">Dim</SPAN> Swap1 <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>, Swap2 <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
    <SPAN style="color:#00007F">Dim</SPAN> varTemp <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
    
    <SPAN style="color:#00007F">Let</SPAN> oldArr = oldDates.Value
    <SPAN style="color:#00007F">Let</SPAN> newArr = newDates.Value
    <SPAN style="color:#00007F">Set</SPAN> myCol = <SPAN style="color:#00007F">New</SPAN> Collection
    
    <SPAN style="color:#00007F">If</SPAN> <SPAN style="color:#00007F">Not</SPAN> IsArray(oldArr) <SPAN style="color:#00007F">Then</SPAN>
       myCol.Add oldArr, <SPAN style="color:#00007F">CStr</SPAN>(oldArr)
    <SPAN style="color:#00007F">Else</SPAN>
        <SPAN style="color:#00007F">For</SPAN> i = <SPAN style="color:#00007F">LBound</SPAN>(oldArr, 1) <SPAN style="color:#00007F">To</SPAN> <SPAN style="color:#00007F">UBound</SPAN>(oldArr, 1)
            <SPAN style="color:#00007F">On</SPAN> <SPAN style="color:#00007F">Error</SPAN> <SPAN style="color:#00007F">Resume</SPAN> <SPAN style="color:#00007F">Next</SPAN>
            myCol.Add oldArr(i, 1), <SPAN style="color:#00007F">CStr</SPAN>(oldArr(i, 1))
            <SPAN style="color:#00007F">On</SPAN> <SPAN style="color:#00007F">Error</SPAN> <SPAN style="color:#00007F">GoTo</SPAN> 0
        <SPAN style="color:#00007F">Next</SPAN>
    <SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">If</SPAN>
    
    <SPAN style="color:#00007F">If</SPAN> <SPAN style="color:#00007F">Not</SPAN> IsArray(newArr) <SPAN style="color:#00007F">Then</SPAN>
       <SPAN style="color:#00007F">On</SPAN> <SPAN style="color:#00007F">Error</SPAN> <SPAN style="color:#00007F">Resume</SPAN> <SPAN style="color:#00007F">Next</SPAN>
       myCol.Add newArr, <SPAN style="color:#00007F">CStr</SPAN>(newArr)
       <SPAN style="color:#00007F">On</SPAN> <SPAN style="color:#00007F">Error</SPAN> <SPAN style="color:#00007F">GoTo</SPAN> 0
    <SPAN style="color:#00007F">Else</SPAN>
        <SPAN style="color:#00007F">For</SPAN> i = <SPAN style="color:#00007F">LBound</SPAN>(newArr, 1) <SPAN style="color:#00007F">To</SPAN> <SPAN style="color:#00007F">UBound</SPAN>(newArr, 1)
            <SPAN style="color:#00007F">On</SPAN> <SPAN style="color:#00007F">Error</SPAN> <SPAN style="color:#00007F">Resume</SPAN> <SPAN style="color:#00007F">Next</SPAN>
            myCol.Add newArr(i, 1), <SPAN style="color:#00007F">CStr</SPAN>(newArr(i, 1))
        <SPAN style="color:#00007F">Next</SPAN>
    <SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">If</SPAN>

    <SPAN style="color:#00007F">For</SPAN> i = 1 <SPAN style="color:#00007F">To</SPAN> myCol.Count - 1
        <SPAN style="color:#00007F">For</SPAN> j = i + 1 <SPAN style="color:#00007F">To</SPAN> myCol.Count
            <SPAN style="color:#00007F">If</SPAN> myCol(i) > myCol(j) <SPAN style="color:#00007F">Then</SPAN>
                Swap1 = myCol(i)
                Swap2 = myCol(j)
                myCol.Add Swap1, before:=j
                myCol.Add Swap2, before:=i
                myCol.Remove i + 1
                myCol.Remove j + 1
            <SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">If</SPAN>
        <SPAN style="color:#00007F">Next</SPAN> j
    <SPAN style="color:#00007F">Next</SPAN> i
    <SPAN style="color:#00007F">ReDim</SPAN> newArr(1 <SPAN style="color:#00007F">To</SPAN> (oldDates.Count + newDates.Count), 1 <SPAN style="color:#00007F">To</SPAN> 1)
    <SPAN style="color:#00007F">For</SPAN> i = 1 <SPAN style="color:#00007F">To</SPAN> myCol.Count
        newArr(i, 1) = myCol(i)
    <SPAN style="color:#00007F">Next</SPAN> i
    <SPAN style="color:#00007F">For</SPAN> i = myCol.Count + 1 <SPAN style="color:#00007F">To</SPAN> <SPAN style="color:#00007F">UBound</SPAN>(newArr)
        newArr(i, 1) = ""
    <SPAN style="color:#00007F">Next</SPAN> i
    <SPAN style="color:#00007F">Set</SPAN> myCol = <SPAN style="color:#00007F">Nothing</SPAN>
    udfSortDate2 = newArr
<SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">Function</SPAN>
</FONT>
 
Upvote 0
While critical, I am trying to be constructive. Fair enough then. :)

Okay 2 more items, one a question, and the second is a minor tweak.

1) What is the purpose of:

<font face=Courier New>    <SPAN style="color:darkblue">For</SPAN> i = myCol.Count + 1 <SPAN style="color:darkblue">To</SPAN> <SPAN style="color:darkblue">UBound</SPAN>(newArr)
        newArr(i, 1) = ""
    <SPAN style="color:darkblue">Next</SPAN> i</FONT>

?

2) Select the udf -> hit your delete key. Now paste the following event procedure into your worksheet class module:

http://www.mrexcel.com/board2/viewtopic.php?t=86068&start=21
 
Upvote 0
Nate,

1. The reason for that little bit at the end was due to Monica's post where she was asking how to supress #N/A return values. I explained that while one can help this a little by adding the bit of code you cited to the function, if the output cell count exceeds the input cell count you'll still end up with #N/A's.

I did time this new one and stacking the array versus using Transpose() cut the execution time by about 10% (though it still follows the same nasty curve). I didn't use the high-resolution timer you posted here, but I will definitely use it when I need it in the future - thanks for that BTW.

You never did answer the question I posed in post #40 - would this knave of a UDF consume addresses as a square of the number of inputs?

2. "Minor tweak" - I like that.
 
Upvote 0
#40 eh. Let's see. Me thinks it should be related to the number of calls. 10 inputs, 10 additions, all unique, 20, by 20 calls equals 400, so yes. :lookaway:

Also, when you report 22 seconds for 1000 cells, do you mean per call or in total for all calls? I did corrupt a workbook with the udf, but I didn't test it, knowing the result was going to be scary.
 
Upvote 0
For dropping that array formula into x number of cells. Column A & Column B loaded with random dates between 1/1/1970 & 12/31/1979 (generated w/ RandBetween() then pasted values over the top).

<font face=Courier New><SPAN style="color:#00007F">Sub</SPAN> TimeTest()
    <SPAN style="color:#00007F">Dim</SPAN> varStart   <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>, varEnd <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Variant</SPAN>
    <SPAN style="color:#00007F">Dim</SPAN> rng        <SPAN style="color:#00007F">As</SPAN> Range, intHalfRow <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">Integer</SPAN>
    <SPAN style="color:#00007F">Dim</SPAN> strHalfRow <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">String</SPAN>, strFormula <SPAN style="color:#00007F">As</SPAN> <SPAN style="color:#00007F">String</SPAN>
    <SPAN style="color:#00007F">Dim</SPAN> rngOut     <SPAN style="color:#00007F">As</SPAN> Range
    
    <SPAN style="color:#00007F">For</SPAN> intRows = 100 <SPAN style="color:#00007F">To</SPAN> 1200 <SPAN style="color:#00007F">Step</SPAN> 100
        <SPAN style="color:#00007F">Set</SPAN> rng = [D1]
        rng.EntireColumn.Delete
        <SPAN style="color:#00007F">Set</SPAN> rng = [D1]
        <SPAN style="color:#00007F">Set</SPAN> rng = rng.Resize(intRows)
        intHalfRow = intRows / 2
        strHalfRow = Trim(Str(intHalfRow - 1))
        <SPAN style="color:#007F00">'strFormula = "=fubar2(RC[-3]:R[" & strHalfRow & "]C[-3],RC[-2]:R[" & strHalfRow & "]C[-2])"</SPAN>
        strFormula = "=udfsortdate2(RC[-3]:R[" & strHalfRow & "]C[-3],RC[-2]:R[" & strHalfRow & "]C[-2])"
        Application.StatusBar = intRows
        varStart = Now
        Application.ScreenUpdating = <SPAN style="color:#00007F">False</SPAN>
        rng.FormulaArray = strFormula
        varEnd = Now
        Application.ScreenUpdating = <SPAN style="color:#00007F">True</SPAN>
        [D1].EntireColumn.Delete
        <SPAN style="color:#00007F">Set</SPAN> rngOut = Sheet5.[A50000].End(xlUp).Offset(1)
        rngOut = varStart
        rngOut.Offset(, 1) = varEnd
        rngOut.Offset(, 2) = intRows
        rngOut.Offset(, 3).FormulaR1C1 = "=RC[-2]-RC[-3]"
        rngOut.Offset(, 3).NumberFormat = "hh:mm:ss"
        Debug.Print varStart, varEnd, DateDiff("s", varStart, var<SPAN style="color:#00007F">End</SPAN>)
    <SPAN style="color:#00007F">Next</SPAN> intRows
    
    Application.StatusBar = <SPAN style="color:#00007F">False</SPAN>
End <SPAN style="color:#00007F">Sub</SPAN>
</FONT>
 
Upvote 0
NateO said:
Ladies and Gentlemen,

I am pleased to announce that in spite of her preoccupation with lumberjacks, Ponsy Nob. has managed to stumble into something reasonable with respect to a semantic in this thread (while it has broader implications). My statement about Delete and Special Cells was too general. Here's the deal, take the following:

<font face=Courier New><SPAN style="color:darkblue">Sub</SPAN> tester()
[a1:a10].SpecialCells(xlVisible).Delete xlUp
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">Sub</SPAN></FONT>

1) Works fine when cells are hidden normally, as they were in my improved process.

2) Does not work when the cells are visible due to filtering.

So, be on alert as to why your cells are visble. I over applied my findings.


To avoid possible confusion amongst the tree-felling community, but mainly to point out that you are wrong, this behaviour has nothing to do with SpecialCells - which is what you have been claiming and continue to claim.
(I think "mis-applied" would be a more accurate description than "over applied".)

When there are one or more rows hidden by a filter, it is only possible to delete entire row(s).
This is true whether via SpecialCells, a cell by cell loop, manually without VBA, or whatever.
This, of course, is entirely logical.

With an "active" filter in place, the filter needs to be removed (or set to ShowAll) before Delete/ShiftCells can be used (either manually or via VBA).

However, you did manage to stumble into offering a useful warning (although it was mis-applied) :-
In VBA, using Delete/Shift in a range containing an "active" filter will cause the entire row(s) to be deleted.
 
Upvote 0

Forum statistics

Threads
1,225,315
Messages
6,184,236
Members
453,223
Latest member
Ignition04

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top