Fast access to my List vs NSDictionary

Hi, I have created an app to compare two directories (and its contents), something like diff.
The two directories tree can be huge (about 35K files).
So I stored the two list of pathnames in two variable where I access them using the “magic word” my for faster performances using vanilla AS.
So basically I iterate two times from 1 to 35,000 files looking if the path exists in arrayDirOne and vice-versa.
The performance decrease and when counter is about at 10,000 every 1000 new items the time to check next 1000 path take longer and longer…

So I decided to generate a dictionary of 35,000 string with these commands:

set dict to current application's NSMutableDictionary's dictionaryWithCapacity:35000
repeat with i from 1 to 35000
	dict's setObject:"" forKey:(item j of my arrayOne)
end repeat

And using to check if item exists:

repeat with j from 1 to count arrayTwo
	set objectFound to (dictOne's objectForKey:(item j of my arrayTwo))
	if objectFound is missing value then
		copy {item j of my arrayTwo, "NO", ""} to end of my arrayComparazione
	end if
end repeat

Each of the 35,000 key contains full path of file like A/B/C/London.jpg

Accessing items using NSMutableDictionary take about 10 seconds to perform 70K (70,000 thousand) compared to many many many minutes EVEN using access using reference my to property variable in AS.

The question is: OK ASOC use Cocoa command so performances are of course better but I don’t understand why the decreasing of performances checking using pure AS if every item of my second array (total 35,000 path) exists in first array (that contains about the same 35,000 path).

Any tech explanation?

Stefano

Hi.

I can’t answer your query, but since you’re OK with using ASObjC, you may find NSSet faster and more convenient than checking individual items. Here’s a demo:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

main()

on main()
	(* Set up two identical 35,000-path lists for demo purposes. *)
	script o
		property array1 : {}
		property array2 : {}
	end script
	
	set rootPath to (path to desktop as text)
	repeat with i from 100001 to 135000
		set end of o's array1 to rootPath & i & ".jpg"
	end repeat
	set o's array2 to o's array1's items
	
	-- If desired, add unique items to each list to see what happens.
	-- set end of o's array1 to "I'm only in array1"
	-- set end of o's array1 to "So am I"
	-- set end of o's array2 to "and I'm only in array2"
	
	
	(* Do the business. *)
	
	-- Make NSSet versions of each list.
	set set1 to current application's class "NSSet"'s setWithArray:(o's array1)
	set set2 to current application's class "NSSet"'s setWithArray:(o's array2)
	
	-- Subtract set2 from a mutable version of set1 and convert what's left back to a list.
	-- The result contains all the items in array1 which aren't in array2.
	set mutableSet1 to set1's mutableCopy()
	tell mutableSet1 to minusSet:(set2)
	set arrayComparazione1 to mutableSet1's allObjects() as list
	
	-- Subtract set1 from a mutable version of set2 and convert what's left back to a list.
	-- The result contains all the items in array2 which aren't in array1.
	set mutableSet2 to set2's mutableCopy()
	tell mutableSet2 to minusSet:(set1)
	set arrayComparazione2 to mutableSet2's allObjects() as list
	
	return {arrayComparazione1, arrayComparazione2}
end main

Or if you need to preserve the original order of the items, you can use NSOrderedSet instead:


	-- Make NSOrderedSet versions of each list.
	set orderedSet1 to current application's class "NSOrderedSet"'s orderedSetWithArray:(o's array1)
	set orderedSet2 to current application's class "NSOrderedSet"'s orderedSetWithArray:(o's array2)
	
	-- Subtract orderedSet2 from a mutable version of orderedSet1 and convert what's left back to a list.
	-- The result contains all the items in array1 which aren't in array2.
	set mutableOrderedSet1 to orderedSet1's mutableCopy()
	tell mutableOrderedSet1 to minusOrderedSet:(orderedSet2)
	set arrayComparazione1 to mutableOrderedSet1's array() as list
	
	-- Subtract orderedSet1 from a mutable version of orderedSet2 and convert what's left back to a list.
	-- The result contains all the items in array2 which aren't in array1.
	set mutableOrderedSet2 to orderedSet2's mutableCopy()
	tell mutableOrderedSet2 to minusOrderedSet:(orderedSet1)
	set arrayComparazione2 to mutableOrderedSet2's array() as list

One common misconception is that AppleScriptObjC is a programming language or an superset of AppleScript that allows you to run Objective-C code in-line with AppleScript. AppleScriptObjC is an framework that creates an interface for AppleScript allowing scripts to tell the framework (read: indirectly) to run Objective-C code. This interface is actually translating AppleEvents into Objective-C code in both directions. So every time you use an variable whose content is returned from the Objective-C runtime, an AppleEvent descriptor is made by the AppleScriptObjC.framework and returned to your script. Doing that 35,000 times in 10 seconds isn’t that bad considering that AppleEvents doesn’t care much for high performance.

Hi Nigel,

thanks for the example.
I’m not AS beginner but I’m ASOC beginner :wink:

Thanks again.

Stefano