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
 
Monica,

You'll get an N/A return once the number of cells with the array-formula in it exceeds the number of elements being returned. While you input six cells, you only get five outputs due to the duplicate entry.
<hr>
Nate,
I ain't a sayin' this is the most efficient way-a doin' things. Yer knowledge in that area well exceeds mine. All's I'm a sayin' is that most efficient ain't always preferred. If'n 'twas we'd not see nary an SUV on the road and every hotel would be a-la Motel 6. Now seein's ta how you've got a bee in yer bonnet about this, how's 'bout doffing that bonnet; donning yer "professor cap" and explaining just how big of a hit this approach costs us. :hungry: Does "...marshaling a variant variable containing an array of x elements per call..." grow expontially? If ten inputs and ten outputs, are we burning 100 addresses? And if we step to 20 inputs and 20 outputs we consume 400 addresses?
 
Upvote 0

Excel Facts

How can you turn a range sideways?
Copy the range. Select a blank cell. Right-click, Paste Special, then choose Transpose.
Greg,

Do you know why the formula:

{=IF(ISNA(Fubar2(B3:B6,C3)),"",Fubar2($B$3:$B$6,$C$3))}

copied down from D2:D7

does not work in cleaning up that #N/A error value that I referred to in cell D7?

Shouldn't the "ISNA" part of the formula see that there is an #N/A in cell D7 and just leave that cell blank? Or, am I missing something?

Thank you!
~ Monica :)
 
Upvote 0
hi monica,

i'm not tryin to get between Greb and Nate, they're waay outta my league here. but have you tried replacing your ISNA with ISERROR to blanket all errors? i've done that with success (mirroring an equation like yours, not the fubar though).
 
Upvote 0
Monica,

I can't cook up a way formulaically to suppress the #N/A. What we can do it alter the function a bit. You could edit the tail-end of the function to look as follows:
<font face=Courier New><SPAN style="color:#007F00">'...</SPAN>
    strFormat = oldDates.Cells(1, 1).NumberFormat
    <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) = Format(myCol(i + 1), str<SPAN style="color:#00007F">For</SPAN>mat)
    <SPAN style="color:#00007F">Next</SPAN> i
    For i = myCol.Count <SPAN style="color:#00007F">To</SPAN> <SPAN style="color:#00007F">UBound</SPAN>(newArr)
        newArr(i) = ""
    <SPAN style="color:#00007F">Next</SPAN> i
    
    <SPAN style="color:#00007F">Set</SPAN> myCol = <SPAN style="color:#00007F">Nothing</SPAN>
    Fubar2 = Application.Transpose(newArr)
<SPAN style="color:#00007F">End</SPAN> <SPAN style="color:#00007F">Function</SPAN>
</FONT>

This will now return empty strings when all unique outputs are exhausted. Note however that this will still produce an #N/A return error if the number of cells in your output range exceeds the number of cells put into the formula. Examples:
C1:C10 = {Fubar2(A1:A5,B1:B5)} and there are two duplicates - C9:C10 would contain null strings.
But if C1:C12 = {Fubar2(A1:A5,B1:B5)} then C9:C10 still nullstrings, but C11:C12 will return #N/A.
 
Upvote 0
I would like to send a most sincere "THANK YOU" to Greg, Nate, Yogi, Firefytr and Aladin. I really appreciate the time that you spent contributing to this question. I tried all of your suggestions, but in the end decided that the user-defined function most suited my needs. I certainly learned a lot from all of your posts and I am very impressed by the depth of your knowledge. I will be sure to save all of your contributions for future reference.

Thank you again,
~ Monica :-D :-D
 
Upvote 0
Greg Truby said:
Nate,
I ain't a sayin' this is the most efficient way-a doin' things.
Great then we agree. I'm going to take it one step further though, and say that you, Aladin and I should be taken out back to the woodshed and be flogged for our UDF propositions. Sorry gentlemen, they're not just inefficient, they're bordering on workbook corruption.

Any innocent person reading this thread should be informed that while all three of these propositions make for interesting mental masturbation, they should not be implemented in a workbook where reasonable performance is expected.

If'n 'twas we'd not see nary an SUV on the road and every hotel would be a-la Motel 6.
There is almost no such thing as an efficient automobile based on what we know to be possible.

http://www.bmwusa.com/Joy/Drive/Technology/Hydrogen.htm

I drive a Grand Cherokee, and come January in Minnesota, it's a lot more efficient than an S-Type Jag. :wink:

Now seein's ta how you've got a bee in yer bonnet about this, how's 'bout doffing that bonnet; donning yer "professor cap" and explaining just how big of a hit this approach costs us. :hungry:
First things first. Let's remove a major drag:

<font face=Courier New><SPAN style="color:darkblue">ReDim</SPAN> newArr(0 <SPAN style="color:darkblue">To</SPAN> myCol.Count - 1, 1 <SPAN style="color:darkblue">To</SPAN> 1)
<SPAN style="color:darkblue">For</SPAN> i = 0 <SPAN style="color:darkblue">To</SPAN> myCol.Count - 1
    newArr(i, 1) = Format(myCol(i + 1), strFormat)
<SPAN style="color:darkblue">Next</SPAN> i

<SPAN style="color:darkblue">Set</SPAN> myCol = <SPAN style="color:darkblue">Nothing</SPAN>
Fubar2 = newArr
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">Function</SPAN></FONT>

Still, you're taking something really bad and making it better, yet still, it's really bad. You don't even need to time it to come to this conclusion. Sort the range and use a filter. ;)
 
Upvote 0
monica S said:
I would like to send a most sincere "THANK YOU" to Greg, Nate, Yogi, Firefytr and Aladin.
You're welcome.

I tried all of your suggestions, but in the end decided that the user-defined function most suited my needs.
For the sake of innocent readers, this isn't accurate, but so be it. :-D

Have a nice day. :)
 
Upvote 0
Nate, I forgot yer up North where SUV's may indeed be pragmatic. :) As an illustration to any innocent bystanders that may read this thread and be lured into trying this I did test the performance on this rascal and at least on my machine things were fine under about 500 cells in the combined ranges. After that things go downhill in a hurry. Get over 1,000 cells and the re-calc times would really become onerous. Scatter graph the following and Nate's point become very clear - this approach gets expensive quickly.
dateaddfunction.xls
CDEF
1RunCellCountTime
2110000:00:00
3120000:00:00
4130000:00:01
5140000:00:02
6150000:00:02
7160000:00:04
8170000:00:06
9180000:00:08
10190000:00:11
111100000:00:15
121110000:00:19
131120000:00:22
14210000:00:00
15220000:00:00
16230000:00:01
17240000:00:01
18250000:00:02
19260000:00:04
20270000:00:06
21280000:00:09
22290000:00:11
232100000:00:15
242110000:00:18
252120000:00:22
Sheet5


There -- did I escape the woodshed? Or should I pad my britches?
 
Upvote 0
Now we're making progress! :) :beerchug:

1,000 rows of data, sorting and filtering averages 0.0884 seconds on my machine (64 trials).

<font face=Courier New><SPAN style="color:darkblue">Declare</SPAN> <SPAN style="color:darkblue">Function</SPAN> QueryPerformanceCounter _
    <SPAN style="color:darkblue">Lib</SPAN> "kernel32" ( _
    X <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Currency</SPAN>) <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Boolean</SPAN>

<SPAN style="color:darkblue">Declare</SPAN> <SPAN style="color:darkblue">Function</SPAN> QueryPerformanceFrequency _
    <SPAN style="color:darkblue">Lib</SPAN> "kernel32" ( _
    X <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Currency</SPAN>) <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Boolean</SPAN>

<SPAN style="color:darkblue">Private</SPAN> <SPAN style="color:darkblue">Sub</SPAN> Timer()
<SPAN style="color:darkblue">Dim</SPAN> Ctr1 <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Currency</SPAN>, Ctr2 <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Currency</SPAN>, Freq <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Currency</SPAN>
<SPAN style="color:darkblue">Dim</SPAN> Overhead <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Currency</SPAN>, A <SPAN style="color:darkblue">As</SPAN> <SPAN style="color:darkblue">Long</SPAN>
QueryPerformanceFrequency Freq
QueryPerformanceCounter Ctr1
QueryPerformanceCounter Ctr2
Overhead = Ctr2 - Ctr1
QueryPerformanceCounter Ctr1
    
<SPAN style="color:darkblue">Call</SPAN> Tester

QueryPerformanceCounter Ctr2
<SPAN style="color:darkblue">Debug</SPAN>.<SPAN style="color:darkblue">Print</SPAN> (Ctr2 - Ctr1 - Overhead) / Freq
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">Sub</SPAN>

<SPAN style="color:darkblue">Sub</SPAN> Tester()
Application.ScreenUpdating = <SPAN style="color:darkblue">False</SPAN>
Columns("A:A").Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _
    OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom
Range([a1], [a65536].End(3)).AdvancedFilter Action:=xlFilterInPlace, Unique:=<SPAN style="color:darkblue">True</SPAN>
<SPAN style="color:darkblue">Set</SPAN> myRng = Range([a1], [a65536].End(3)).SpecialCells(xlVisible)
<SPAN style="color:darkblue">On</SPAN> <SPAN style="color:darkblue">Error</SPAN> <SPAN style="color:darkblue">Resume</SPAN> <SPAN style="color:darkblue">Next</SPAN>
Sheets(3).ShowAllData
<SPAN style="color:darkblue">On</SPAN> <SPAN style="color:darkblue">Error</SPAN> <SPAN style="color:darkblue">GoTo</SPAN> 0
<SPAN style="color:darkblue">If</SPAN> <SPAN style="color:darkblue">Not</SPAN> <SPAN style="color:darkblue">CBool</SPAN>(Err.Number) <SPAN style="color:darkblue">Then</SPAN>
    Err.Clear
    Application.EnableEvents = <SPAN style="color:darkblue">False</SPAN>
    myRng.EntireRow.Hidden = <SPAN style="color:darkblue">True</SPAN>
    Range([a1], [a65536].End(3)).SpecialCells(xlVisible).Delete
    [a:a].EntireRow.Hidden = <SPAN style="color:darkblue">False</SPAN>
    Application.EnableEvents = <SPAN style="color:darkblue">True</SPAN>
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">If</SPAN>
Application.ScreenUpdating = <SPAN style="color:darkblue">True</SPAN>
<SPAN style="color:darkblue">End</SPAN> <SPAN style="color:darkblue">Sub</SPAN></FONT>

Not only is it very fast, but far less opaque in nature. Speed and maintainability, not bad concepts in application development. ;)
 
Upvote 0
Only footnotes I'd tack on for the benefit of any readers who have followed along this far is (a) remember that in order to use this more efficient Sub, you'd need something to make it happen, most likely candidate is the Worksheet_Change() event that Nate used in a much earlier post. I don't think you could put this code into a function. Damon did a very nice job of explaining why. And (b) I'd probably edit one line of Nate's code (new part in blue) in order prevent Excel from guessing which direction to delete, because if you had a single-cell range to delete it might decide to shift left:
Range([a1], [a65536].End(3)).SpecialCells(xlVisible).Delete Shift:=xlShiftUp
 
Upvote 0

Forum statistics

Threads
1,225,317
Messages
6,184,250
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