I need to speed up this handler please

G’day all helpers.

Ric, that does not work. I get an error,

Unable to parse the format string “Brian Christmas OzSanta@gmail.com.au” -10000

my name substituted for real address.

Darn it, I’m sorry to bother you again, but is it fixable?

Regards

Santa

Hi Santa,

With all the suggestions that were provided, which way did you go? Did you use the ObjC code provided by Ric? If you did go the objc path, did you include Stefan’s modification?

Also, remember to coerce your variables, going to objc and coming back to asoc. This makes a huge difference.

G’day Leon

I’m trying Rics suggestion first. His method should be the fastest I think.

I’ve added Stefans change, but it still crashes.

Just got to get it to work.

Ummm, what exactly do you mean by co-erce your variables?

I’m adding items to the arrays as text items if that makes any difference?

Regards

Santa



on ClientReport()
		set DataClientYear to theYear's titleOfSelectedItem() as integer
		set TallyName2 to ((path to desktop) & "Mail Manager Folder:Mail Data " & DataClientYear) as text
		set TestClientDate to current date
		set EndClientDate to current date
		set year of TestClientDate to DataClientYear
		set month of TestClientDate to (startingMonth's indexOfSelectedItem()) + 1
		set day of TestClientDate to 1
		set the hours of TestClientDate to 0
		set the minutes of TestClientDate to 0
		set the seconds of TestClientDate to 0
		set year of EndClientDate to DataClientYear
		set month of EndClientDate to (endingMonth's indexOfSelectedItem()) + 1
		set day of EndClientDate to 1
		set the hours of EndClientDate to 0
		set the minutes of EndClientDate to 0
		set the seconds of EndClientDate to 0
		set HourlyString to {}
		set tempStartingMonth to month of TestClientDate as integer
		set tempendingmonth to month of EndClientDate as integer
		set theClientListTEMP to {}
		set theClientList2 to {}
		tell application "Finder"
			set theFiles to files of folder TallyName2 as alias list
			set xx to 0
			set TheBarIncrement to 100 / (count of theFiles)
		end tell
		Progress's setMaxValue_(100)
		Progress's setDoubleValue_(0)
		set BarCount to 0
		tell current application's NSMutableArray to set theClientListArray to arrayWithCapacity_(1)
		tell current application's NSMutableArray to set theClientList to arrayWithCapacity_(1)
		set speedUp0's ClientList0 to theFiles
		repeat with individualFile in speedUp0's ClientList0
			set BarCount to BarCount + 0.25
			Progress's setDoubleValue_(TheBarIncrement * BarCount)
			tell application "Finder"
				set theCreationDate to name of individualFile
			end tell
			set theCMonth to word 2 of theCreationDate as integer
			if theCMonth ≥ tempStartingMonth and theCMonth ≤ tempendingmonth then
				set tempWholeList to my ReadFile2(individualFile as text) as list
				repeat with xxx from 1 to count of paragraphs of item 1 of tempWholeList -- run through the clients in .dat
					try
						set paragraphCycle to paragraph xxx of item 1 of tempWholeList as text
						set Tild to AppleScript's text item delimiters
						set AppleScript's text item delimiters to ","
						set the item_list to every text item of paragraphCycle
						try
							set tempClientString to item 2 of item_list
						end try
						set AppleScript's text item delimiters to Tild
						set x to offset of "<" in tempClientString
						if x = 0 then
							set x to offset of "," in paragraphCycle
							set y to offset of ">" in paragraphCycle
							if x = 0 then set x to -1
							if y = 0 then set y to count of paragraphCycle
							if y > 10 then
								set tempClientString to characters (x + 1) thru y of paragraphCycle as text
								--if character 1 of tempClientString = " " then set tempClientString to characters 2 thru -1 of tempClientString
								set y to offset of ">" in tempClientString
								if y = 0 then set y to (offset of "," in tempClientString) - 1
								if y ≠ 0 then
									set tempClientString to characters 1 thru y of tempClientString as text
									repeat while character 1 of tempClientString is in {" ", "\""}
										set tempClientString to characters 2 thru -1 of tempClientString as text
									end repeat
								end if
							end if
						end if
						if tempClientString ≠ "" and tempClientString is not in {","} then
							theClientListArray's addObjectsFromArray_({tempClientString}) --< Make VERY long list
							if tempClientString is not in items of theClientListTEMP then
								theClientList's addObjectsFromArray_({tempClientString}) --< Make short list
								set end of theClientListTEMP to tempClientString
								--set end of theClientList2 to 0
							end if
						end if
					on error errmsg number errnum
						display dialog errmsg & " " & errnum
					end try
				end repeat
			end if
		end repeat
		set yy to count of theClientListTEMP
		set temp to Progress's doubleValue()
		set MinorIncrement to (140 - temp) / (yy)
		set temp2 to current date
		set y to 1
		say "here"
		
		try
			set theClientList2 to current application's ObjCFile's countStuffFrom_in_(theClientList, theClientListArray)
		on error errmsg number errnum
			display dialog errmsg & " " & errnum
		end try
		say (count of theClientList2)
		(*repeat with xx in theClientList
			set BarCount to BarCount + MinorIncrement
			Progress's setDoubleValue_(TheBarIncrement * BarCount)
			--tell current application's NSPredicate to set thePredicate to predicateWithFormat_("SELF like[cd] %@", xx)
			try
				(*tell current application's NSPredicate to set thePredicate to predicateWithFormat_("SELF matches %@", xx)
				set theFilteredArray to theClientListArray's filteredArrayUsingPredicate_(thePredicate)
				set item y of theClientList2 to (theFilteredArray's |count|()) as integer*)
			on error errmsg number errnum
				if errnum is not in {-10000} then display dialog errmsg & return & errnum
			end try
			set y to y + 1
		end repeat*)
		say (current date) - temp2
		set thecount to yy
		set DisplayString to {"There were no clients for this period."}
		set DisplayAlphabetString to {"There were no clients for this period."}
		set temp1 to {}
		if thecount > 0 then --<-- This will crap itself on empty month
			set temp to Progress's doubleValue()
			set MinorIncrement to (100 - temp) / (4 * thecount)
			set speedUp1's ClientList1 to theClientList
			set x to 0
			repeat with t in speedUp1's ClientList1
				set x to x + 1
				set BarCount to BarCount + MinorIncrement
				Progress's setDoubleValue_(TheBarIncrement * BarCount)
				set t to t as text
				repeat while character 1 of t is in {"\"", " ", "?"}
					set t to characters 2 thru (count of t) of t as text
				end repeat
				if character 1 of t = "<" then
					try
						set t to characters 2 thru -1 of t & "<" as text
					end try
				end if
				set item x of speedUp1's ClientList1 to t
			end repeat
			set {temp4, temp3} to my sort2Lists(speedUp1's ClientList1, theClientList2)
			set temp1 to {}
			set temp2 to {}
			set speedUp2's ClientList2 to temp4
			set x to (count of temp4)
			repeat with xx in reverse of speedUp2's ClientList2
				set xx to xx as text
				set BarCount to BarCount + MinorIncrement
				Progress's setDoubleValue_(TheBarIncrement * BarCount)
				set end of temp1 to item x of temp3
				try
					if character -1 of xx = "<" then
						set xx to "<" & characters 1 thru -2 of xx as text
					end if
				end try
				set end of temp2 to xx -- ClientList
				set x to x - 1
			end repeat
			set DisplayAlphabetString to {}
			repeat with x from (count of temp1) to 1 by -1
				set BarCount to BarCount + MinorIncrement
				Progress's setDoubleValue_(TheBarIncrement * BarCount)
				set tempstring to "        "
				set tempNum to item x of temp1
				if tempNum > 9 then set tempstring to "      "
				if tempNum > 99 then set tempstring to "    "
				if tempNum > 999 then set tempstring to "  "
				set end of DisplayAlphabetString to (item x of temp1 as text) & tempstring & item x of temp2
			end repeat
			set {temp1, temp2} to my sort2Lists(temp1, temp2)
			set DisplayString to {}
			repeat with x from (count of temp1) to 1 by -1
				set BarCount to BarCount + MinorIncrement
				Progress's setDoubleValue_(TheBarIncrement * BarCount)
				set tempstring to "        "
				set tempNum to item x of temp1
				if tempNum > 9 then set tempstring to "      "
				if tempNum > 99 then set tempstring to "    "
				if tempNum > 999 then set tempstring to "  "
				
				--Set email
				set end of DisplayString to (item x of temp1 as text) & tempstring & item x of temp2
			end repeat
		end if
		set GraphData to {}
		--ClientToGraph's removeAllItems()
		ClientListAlpha's removeAllItems()
		ClientListAlpha's addItemsWithTitles_(DisplayAlphabetString)
		ClientListNumer's removeAllItems()
		ClientListNumer's addItemsWithTitles_(DisplayString)
		Progress's setDoubleValue_(0)
	end ClientReport

After Edit: Sorry about that, I found a syntax error in my method.

 NSPredicate *pred =[NSPredicate predicateWithFormat:(@"SELF matches[cd] %@",stringToFind)];

should be:

 NSPredicate *pred =[NSPredicate predicateWithFormat:@"SELF matches[cd] %@",stringToFind];

Notice, I took out the parentheses around the predicate string.

Ric

G’day, and thanks Ric.

That is very fast.

However, on my large test file I have some seemingly funny entries that cause a freeze.

Is it possible to error trap your routine, so any particular entry that crashes gets ignored?

The error is…

Can’t do regex matching, reason: (Can’t open pattern U_REGEX_RULE_SYNTAX (string , pattern ??? , case0, canon0)) -10000

Very, very close to success!

Regards

Santa

I mean whatever you get back from another class (either ASOC or ObjC) has to be coerced to an AS-formatted variable. Everything passes through Cocoa, and except for certain things like dates, you only need to coerce strings, texts, reals, integers and lists. When Ric’s ObjC method will return you the results of it’s process, you will have to coerce each item in the array to something AS will understand. For example:

repeat with anItem in returnArrayFromObjC
set anItem to anItem as string --or integer, or real, etc. depending on what is returned
--process anItem
end

This is mandatory, most of the time you will not get an error message and it won’t work or sometimes crash. Same thing if you had a separate class in ASOC, since Cocoa does the “transmission” of data between classes and languages.

You app is a bit difficult to read. If I can make a suggestion, here’s how you could simplify the first part. It could apply somewhere else:

       set DataClientYear to theYear's titleOfSelectedItem() as integer
       set TallyName2 to ((path to desktop) & "Mail Manager Folder:Mail Data " & DataClientYear) as text

       set {TestClientDate, EndClientDate} to {current date, current date}
       set {year of TestClientDate, month of TestClientDate, day of TestClientDate} to {DataClientYear, (startingMonth's indexOfSelectedItem()) + 1, 1}

       set {hours of TestClientDate, minutes of TestClientDate, seconds of TestClientDate} to {0, 0, 0}
       set {year of EndClientDate, month of EndClientDate, day of EndClientDate} to {DataClientYear, (endingMonth's indexOfSelectedItem()) + 1, 1}
       set {hours of EndClientDate, minutes of EndClientDate, seconds of EndClientDate} to {0, 0, 0}

       set {HourlyString, tempStartingMonth, tempendingmonth} to {{}, (month of TestClientDate) as integer, (month of EndClientDate) as integer
       set {theClientListTEMP, theClientList2} to {{}, {}}

Also, for the sake of performance, when you tell an application to do something, only ask it to do what it is best at, like here:

tell application "Finder"
           set theFiles to files of folder TallyName2 as alias list
           set xx to 0
           set TheBarIncrement to 100 / (count of theFiles)
end tell

should be changed to:

tell application "Finder" to set theFiles to files of folder TallyName2 as alias list
set {xx, TheBarIncrement} to {0, 100 / (count of theFiles)}

You shouldn’t ask the Finder to set variables, or do math. Your app can and should.

And a small simplification:

tell application "Finder"
        set theCreationDate to name of individualFile
end tell

can be used like this:

tell application "Finder" to set theCreationDate to name of individualFile

One line instead of 3. :slight_smile:

I just saw this near the end:

set tempstring to " "
set tempNum to item x of temp1
if tempNum > 9 then set tempstring to " "
if tempNum > 99 then set tempstring to " "
if tempNum > 999 then set tempstring to " "

Why do you set tempstring to " " first then after 3 if…then set the same variable to the exact same value? Is it on purpose or is it a leftover? I’m not sure, but it takes up cpu cycles for nothing.

Hope this helps! :wink:

Wow, thanks Leon

The last bit is deliberate.

They are actually spacers, cause I’m listing data in pop-up buttons as (Number) (spaces) (Client email), and I want the email addresses to line up.

The extra spaces were parsed out in MacScripters lister unfortunately.

Regards

Santa

I get it now. And you’re welcome. If I may give you another word of advice, you should name your variables with a meaningful name. Like instead of tempstring, you could call it theSpacerString. There is no limit to how many characters there can be, and believe me, when your code gets very long and complex, having meaningful variable names helps. You always have to remember what temp1 means, but not theDateToModify, or theEmailAddressToFix.

And i’m glad to know that it’s much faster now. The power of many! :slight_smile:

I’m not sure I know how to do that --you’re definitely testing the limits of my Obj-C knowledge. This might be a question for StefanK.

You can try replacing the .m code I gave you earlier with the following. I have no idea whether it will work since I have no way to test it. I’m just wingin’ it here:

#import "ObjCFile.h"

@implementation ObjCFile

+(id)countStuffFrom:(NSArray *)theClientList in:(NSArray *)theClientListArray{
    NSMutableArray *theClientList2 = [[NSMutableArray alloc]init];
    for (id stringToFind in theClientList){
        @try{
        NSPredicate *pred =[NSPredicate predicateWithFormat:@"SELF matches[cd] %@",stringToFind];
        NSArray *theFilteredArray = [theClientListArray filteredArrayUsingPredicate:pred];
        [theClientList2 addObject:[NSNumber numberWithInt:[theFilteredArray count]]];
        }
        @catch (NSException *exception){
            NSLog(@"Caught %@: %@", [exception name], [exception reason]);
        }
        @finally {}
    }
    return theClientList2;
}
@end

Ric

G’day again Ric

That definately catches the error, but it leaves a blank in the Clientlist2 list.

Is it possible to insert a zero in the list when it catches an error?

With Stefans alteration, the code is below.

Regards, and many, MANY thanks!

Santa



#import "ObjCFile.h"

@implementation ObjCFile

+(id)countStuffFrom:(NSArray *)theClientList in:(NSArray *)theClientListArray{
	NSMutableArray *theClientList2 = [[NSMutableArray alloc]init];
	for (id stringToFind in theClientList){
		@try{
			NSPredicate *pred =[NSPredicate predicateWithFormat:@"SELF matches %@",stringToFind];
			NSArray *theFilteredArray = [theClientListArray filteredArrayUsingPredicate:pred];
			[theClientList2 addObject:[NSNumber numberWithUnsignedInteger:[theFilteredArray count]]];
		}
		@catch (NSException *exception){
			NSLog(@"Caught %@: %@", [exception name], [exception reason]);
		}
		@finally {}
	}
	return theClientList2;
}
@end


I was just thinking about the problem, of the missing value in theClientList2. I managed to make a query that caused an exception, so I test the following code, and it seemed to work:

#import "ObjCFile.h"

@implementation ObjCFile

+(id)countStuffFrom:(NSArray *)theClientList in:(NSArray *)theClientListArray{
    NSMutableArray *theClientList2 = [[NSMutableArray alloc]init];
    for (id stringToFind in theClientList){
        @try{
        NSPredicate *pred =[NSPredicate predicateWithFormat:@"SELF matches[cd] %@",stringToFind];
        NSArray *theFilteredArray = [theClientListArray filteredArrayUsingPredicate:pred];
        [theClientList2 addObject:[NSNumber numberWithUnsignedInteger:[theFilteredArray count]]];
        }
        @catch (NSException *exception){
            NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
            [theClientList2 addObject:[NSNumber numberWithUnsignedInteger:0]];
        }
    }
    return theClientList2;
}
@end

The @finally wasn’t doing anything and it appears that it can be removed. I added the addObject command within the @catch block, to keep things in sync. I think this will work now.

Ric

Thanks Ric

It takes 128 seconds to list 2493 clients out of 29,067 entries, using just ‘SELF matches’. Using ‘SELF matches’[cd]’ took 191 seconds

The method of calling your first script in an Applescript loop took 160 seconds for a ‘SELF matches’ and 243 seconds for a ‘SELF matches[cd]’.

Regards

Santa

In leonsimard’s post:

Actually, this is not necessary. If you coerce an NSArray to an applescript list, the items in the list are automatically coerced to their applescript equivalents (if they have equivalents).

Ric

That’s what I tried and it worked.

I used the lines…

		set speedUp1's ClientList1 to theClientList as list
		set theClientList3 to theClientList2 as list

not knowing what the heck I was doing, but trying things out, and it worked.

Regards

Santa

G’day once more in this saga

Ric (or whoever can help)

I was getting a lot of hits with zero count, so I altered the 0 to 1.

@catch (NSException *exception){
NSLog(@“main: Caught %@: %@”, [exception name], [exception reason]);
[theClientList2 addObject:[NSNumber numberWithInt:1]];

But, i still got some entries showing zero matches.

Turns out they all have brackets in the name eg Kagi (initials) admin@kagi.com

Does this need something special to find?

Regards

Santa

Yes, the “(” and “)” characters (among others) are special characters in a regex search, so you have to escape them with the \ character. But since the \ character is treated as a special character in Obj-C, you have to escape it too, so to search for “(” you would do “\(” . Try out this code, and see what you get:

#import "ObjCFile.h"

@implementation ObjCFile

+(id)countStuffFrom:(NSArray *)theClientList in:(NSArray *)theClientListArray{
    NSMutableArray *theClientList2 = [[NSMutableArray alloc]init];
    for (id stringToFind in theClientList){
        //NSRange range = [stringToFind rangeOfString:@"("];
       // if(range.length > 0){
        stringToFind = [stringToFind stringByReplacingOccurrencesOfString:@"(" withString:@"\\("];
        stringToFind = [stringToFind stringByReplacingOccurrencesOfString:@")" withString:@"\\)"];
       // }
        @try{
        NSPredicate *pred =[NSPredicate predicateWithFormat:@"SELF matches[cd] %@",stringToFind];
        NSArray *theFilteredArray = [theClientListArray filteredArrayUsingPredicate:pred];
        [theClientList2 addObject:[NSNumber numberWithUnsignedInteger:[theFilteredArray count]]];
        }
        @catch (NSException *exception){
            NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
            [theClientList2 addObject:[NSNumber numberWithUnsignedInteger:0]];
        }
    }
    return theClientList2;
}
@end

I’ve commented out three lines (those with the // at the beginning). That if statement tests to see if the “(” character is in the string before adding the escape characters. I don’t know if that will speed up the process or not – try it the way it’s written and then try it after uncommenting those lines and see which is faster (the stringByReplacingOccurrencesOfString: method won’t do anything to stringToFind if it doesn’t find a parenthesis).

Ric

Many thanks once again Rick

I let it loose on a full years worth of artificial data, 365 files, with 47912 items, and 2497 clients; 312 seconds with the [cd], and 239 seconds without it. I had to add square brackets as well.

With your commented sections uncommented, the time dropped fractionally to 231 seconds without the [cd].

Great job!

I’ve got another project on the same data that I’ll use this on too!

Thanks to Stefan as well!

Regards

Santa


#import "ObjCFile.h"


@implementation ObjCFile

+(id)countStuffFrom:(NSArray *)theClientList in:(NSArray *)theClientListArray{
	NSMutableArray *theClientList2 = [[NSMutableArray alloc]init];
	for (id stringToFind in theClientList){
		//NSRange range = [stringToFind rangeOfString:@"("];
		// if(range.length > 0){
		stringToFind = [stringToFind stringByReplacingOccurrencesOfString:@"(" withString:@"\\("];
		stringToFind = [stringToFind stringByReplacingOccurrencesOfString:@")" withString:@"\\)"];
		stringToFind = [stringToFind stringByReplacingOccurrencesOfString:@"[" withString:@"\\["];
		stringToFind = [stringToFind stringByReplacingOccurrencesOfString:@"]" withString:@"\\]"];
		// }
		@try{
			NSPredicate *pred =[NSPredicate predicateWithFormat:@"SELF matches %@",stringToFind];
			NSArray *theFilteredArray = [theClientListArray filteredArrayUsingPredicate:pred];
			[theClientList2 addObject:[NSNumber numberWithUnsignedInteger:[theFilteredArray count]]];
		}
		@catch (NSException *exception){
			NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
			[theClientList2 addObject:[NSNumber numberWithUnsignedInteger:0]];
		}
	}
	return theClientList2;
}
@end