Sets in Mavericks

One of the useful classes in Cocoa (and other languages) is the set, which is like a list except each item can only appear once. Sets are not normally ordered like lists, but Cocoa also has ordered lists, which maintain their order. Anyway, they are a good class to show the convenience of using ASObjC-based script libraries in Mavericks.

Save this script as a .scptd file in ~/Library/Script Libraries/ or /Library/Script Libraries/, following the instructions here: macscripter.net/viewtopic.php?id=41638

use framework "Foundation"

script BaseObject -- the parent script object, required for AppleScriptObjC inheritance
end script

on smartSetWith:aList -- call this to make a new smartList
	script SmartSet
		property parent : BaseObject -- for inheritance
		property theSetStore : missing value -- where the array is stored
		
		on countOfSet() -- get count of items
			return theSetStore's |count|() as integer
		end countOfSet
		
		-- get objects and indexes
		on objectAtIndex:anInteger
			set anInteger to my correctIndex:anInteger
			set theResult to theSetStore's objectAtIndex:anInteger
			return my coerceToASClass:theResult
		end objectAtIndex:
		
		on objectsFrom:anInteger toIndex:endInteger -- get range of objects
			set anInteger to my correctIndex:anInteger
			set endInteger to my correctIndex:endInteger
			set theIndexSet to current application's NSMutableIndexSet's alloc()'s init()
			theIndexSet's addIndexesInRange:(current application's NSMakeRange(anInteger, endInteger - anInteger + 1))
			return (theSetStore's objectsAtIndexes:theIndexSet) as list
		end objectsFrom:toIndex:
		
		on indexOfObject:anObject
			try -- will error if not found because NSNotFound is too big to coerce to an integer
				set theResult to ((theSetStore's indexOfObject:anObject) as integer) + 1
			on error
				return 0
			end try
			return theResult
		end indexOfObject:
		
		-- add objects
		on addObject:anObject -- adds to end
			theSetStore's addObject:anObject
		end addObject:
		
		on addObjectsFromArray:anObject -- adds to end
			theSetStore's addObjectsFromArray:anObject
		end addObjectsFromArray:
		
		on insertObject:anObject atIndex:anInteger -- insert object
			set anInteger to my correctIndex:anInteger
			theSetStore's insertObject:anObject atIndex:anInteger
		end insertObject:atIndex:
		
		-- remove objects
		on removeObject:anObject
			theSetStore's removeObject:anObject
		end removeObject:
		
		on removeObjects:newList
			theSetStore's removeObjectsInArray:newList
		end removeObjects:
		
		on removeObjectAtIndex:anInteger
			set anInteger to my correctIndex:anInteger
			theSetStore's removeObjectAtIndex:anInteger
		end removeObjectAtIndex:
		
		on removeObjectsFrom:anInteger toIndex:endInteger
			set anInteger to my correctIndex:anInteger
			set endInteger to my correctIndex:endInteger
			theSetStore's removeObjectsInRange:(current application's NSMakeRange(anInteger, endInteger - anInteger + 1))
		end removeObjectsFrom:toIndex:
		
		-- replace objects
		on replaceObjectAtIndex:anInteger withObject:anObject
			set anInteger to my correctIndex:anInteger
			theSetStore's replaceObjectAtIndex:anInteger withObject:anObject
		end replaceObjectAtIndex:withObject:
		
		on setObject:anObject atIndex:anInteger -- replace or add, depending on index
			set anInteger to my correctIndex:anInteger
			theSetStore's setObject:anObject atIndex:anInteger
		end setObject:atIndex:
		
		-- swap objects
		on swapObjectAtIndex:anInteger withObjectAtIndex:endInteger
			set anInteger to my correctIndex:anInteger
			set endInteger to my correctIndex:endInteger
			theSetStore's exchangeObjectAtIndex:anInteger withObjectAtIndex:endInteger
		end swapObjectAtIndex:withObjectAtIndex:
		
		-- sort objects
		on sortIgnoringCase()
			my sortUsingSelector:"localizedCaseInsensitiveCompare:"
		end sortIgnoringCase
		
		on sortConsideringCase()
			my sortUsingSelector:"localizedCompare:"
		end sortConsideringCase
		
		on sortLikeFinder()
			my sortUsingSelector:"localizedStandardCompare:"
		end sortLikeFinder
		
		on standardSort() -- use for other than strings
			my sortUsingSelector:"compare:"
		end standardSort
		
		-- query the set
		on containsObject:anObject -- whether set contains an object
			return ((theSetStore's containsObject:anObject) as integer = 1)
		end containsObject:
		
		on containsObjects:listOrArray -- whether set contains all objects in the list
			set newSet to current application's NSSet's setWithArray:listOrArray
			return ((newSet's isSubsetOfSet:(theSetStore's |set|())) as integer = 1)
		end containsObjects:
		
		on intersects:listOrArray -- whether list items and set intersect
			set theSet to current application's NSSet's setWithArray:listOrArray
			return ((theSetStore's intersectsSet:theSet) as integer = 1)
		end intersects:
		
		on intersectsInOrder:listOrArray -- whether list in order and set intersect
			set theSet to current application's NSOrderedSet's orderedSetWithArray:listOrArray
			return ((theSetStore's intersectsOrderedSet:theSet) as integer = 1)
		end intersectsInOrder:
		
		on isSubsetOf:listOrArray -- whether set is a subset of the list
			set theSet to current application's NSSet's setWithArray:listOrArray
			return ((theSetStore's isSubsetOfSet:theSet) as integer = 1)
		end isSubsetOf:
		
		on isSubsetInOrderOf:listOrArray -- whether set is a subset of the list in matching order
			set theSet to current application's NSOrderedSet's orderedSetWithArray:listOrArray
			return ((theSetStore's isSubsetOfOrderedSet:theSet) as integer = 1)
		end isSubsetInOrderOf:
		
		on minusSet:listOrArray -- subtract objects in list from set
			set theSet to current application's NSSet's setWithArray:listOrArray
			theSetStore's minusSet:theSet
		end minusSet:
		
		on unionSet:listOrArray -- add objects in list to set if they are not in it already
			set theSet to current application's NSOrderedSet's orderedSetWithArray:listOrArray
			theSetStore's unionOrderedSet:theSet
		end unionSet:
		
		on intersectSet:listOrArray -- remove objects not in list from set
			set theSet to current application's NSSet's setWithArray:listOrArray
			theSetStore's intersectSet:theSet
		end intersectSet:
		
		-- return as list/array
		on asArray() -- return as array for further use
			return theSetStore's array()
		end asArray
		
		on asList() -- return as list
			return theSetStore's array() as list
		end asList
		
		-- handlers for script's use
		on correctIndex:anInteger -- for script's use; convert AS index to Cocoa index
			if anInteger < 0 then
				return anInteger + (my countOfSet())
			else
				return anInteger - 1
			end if
		end correctIndex:
		
		on coerceToASClass:anObject -- for script's use; coerce to AS class for return
			if ((anObject's isKindOfClass:(current application's NSArray)) as integer = 1) then
				return anObject as list
			else -- coerce to list and return item 1; workaround to coerce item of unknown class
				set anObject to anObject as list
				return item 1 of anObject
			end if
		end coerceToASClass:
		
		on sortUsingSelector:theSel -- for script's use
			set theDesc to current application's NSSortDescriptor's sortDescriptorWithKey:"self" ascending:true selector:theSel
			theSetStore's sortUsingDescriptors:{theDesc}
		end sortUsingSelector:
		
	end script
	
	set theSetStore of SmartSet to current application's NSMutableOrderedSet's orderedSetWithArray:aList -- set initial value
	return SmartSet
end smartSetWith:

The idea is that you create a SmartSet, manipulate it, then return it as a list using asList(). So for example:

use theLib : script "SmartSet lib" -- or whatever you called it

set mySet to theLib's smartSetWith:{4, 6, 1, 2, 3, 7, 1, 4, 3, 3, 7}
set uniqueList to mySet's asList()

This returns the list with duplicates removed. Or:

set mySet to theLib's smartSetWith:{4, 6, 1, 2, 3, 7, 1, 4, 3, 3, 7}
mySet's standardSort()
set sortedList to mySet's asList()

If you run the following and look at the log, you’ll probably get more idea:

set mySet to theLib's smartSetWith:{4, 6, 1, 2, 3, 7, 1, 4, 3, 3, 7}
log mySet's asList()
log (mySet's containsObject:8)
log (mySet's containsObject:1)
mySet's standardSort()
log mySet's asList()
mySet's insertObject:"hello" atIndex:2
log mySet's asList()
mySet's swapObjectAtIndex:2 withObjectAtIndex:-2
log mySet's asList()
mySet's replaceObjectAtIndex:-1 withObject:"blah"
log mySet's asList()
log (mySet's objectsFrom:3 toIndex:5)
mySet's removeObjectsFrom:3 toIndex:5
log mySet's asList()
mySet's unionSet:{6, 4, 8, 1, 4}
log mySet's asList()
mySet's minusSet:{6, 4, 9, 11}
log mySet's asList()
log (mySet's isSubsetOf:{6, "blah", 9, 1, 8})
log (mySet's containsObjects:{1, 8})