Mavericks SmartList

This is a script object that encompasses a Cocoa array, for use from Mavericks scripts via an ASObjC-based library. It is similar to the one for sets. See macscripter.net/viewtopic.php?id=41638 for how to set up and use a script library.

use framework "Foundation"

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

on smartListWith:aList -- call to make a new smartList
	script SmartList
		property parent : BaseObject -- for inheritance
		property arrayStore : missing value -- where the array is stored
		
		on countOfArray() -- get count of items
			return arrayStore's |count|() as integer
		end countOfArray
		
		-- get objects and indexes
		on objectAtIndex:anInteger
			set anInteger to my correctIndex:anInteger
			set theResult to arrayStore'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
			return (arrayStore's subarrayWithRange:(current application's NSMakeRange(anInteger, endInteger - anInteger + 1))) 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 ((arrayStore's indexOfObject:anObject) as integer) + 1
			on error
				return 0
			end try
			return theResult
		end indexOfObject:
		
		-- add objects
		on addObject:anObject -- adds to end
			arrayStore's addObject:anObject
		end addObject:
		
		on addObjectsFromArray:anObject -- adds to end
			arrayStore's addObjectsFromArray:anObject
		end addObjectsFromArray:
		
		on insertObject:anObject atIndex:anInteger
			set anInteger to my correctIndex:anInteger
			arrayStore's insertObject:anObject atIndex:anInteger
		end insertObject:atIndex:
		
		-- remove objects
		on removeObject:anObject
			arrayStore's removeObject:anObject
		end removeObject:
		
		on removeObjects:newList
			arrayStore's removeObjectsInArray:newList
		end removeObjects:
		
		on removeObjectAtIndex:anInteger
			set anInteger to my correctIndex:anInteger
			arrayStore's removeObjectAtIndex:anInteger
		end removeObjectAtIndex:
		
		on removeObjectsFrom:anInteger toIndex:endInteger
			set anInteger to my correctIndex:anInteger
			set endInteger to my correctIndex:endInteger
			arrayStore's removeObjectsInRange:(current application's NSMakeRange(anInteger, endInteger - anInteger))
		end removeObjectsFrom:toIndex:
		
		-- replace objects
		on replaceObjectAtIndex:anInteger withObject:anObject
			set anInteger to my correctIndex:anInteger
			arrayStore's replaceObjectAtIndex:anInteger withObject:anObject
		end replaceObjectAtIndex:withObject:
		
		-- swap objects
		on swapObjectAtIndex:anInteger withObjectAtIndex:endInteger
			set anInteger to my correctIndex:anInteger
			set endInteger to my correctIndex:endInteger
			arrayStore'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
		
		-- misc functions
		on containsObject:anObject -- whether array contains an object
			return ((arrayStore's containsObject:anObject) as integer = 1)
		end containsObject:
		
		on firstObjectCommonWithArray:newArray
			set theResult to arrayStore's firstObjectCommonWithArray:newArray
			return my coerceToASClass:theResult
		end firstObjectCommonWithArray:
		
		on componentsJoinedByString:aString
			return (arrayStore's componentsJoinedByString:aString) as text
		end componentsJoinedByString:
		
		on pathsMatchingExtensions:aList -- no "." in extensions
			return (arrayStore's pathsMatchingExtensions:aList) as list
		end pathsMatchingExtensions:
		
		on valueForKey:aString -- calls valueForKey: on every item in array
			return (arrayStore's valueForKey:aString) as list
		end valueForKey:
		
		on valueForKeyPath:aString -- calls valueForKey: on every item in array, one key after the other (keys separated by ".")
			return (arrayStore's valueForKeyPath:aString) as list
		end valueForKeyPath:
		
		on sumAndAverage() -- return {sum, average} if list is numbers
			return {(arrayStore's valueForKeyPath:"@sum.self") as real, (arrayStore's valueForKeyPath:"@avg.self") as real}
		end sumAndAverage
		
		on maxAndMin() -- return {max, min} 
			return {(arrayStore's valueForKeyPath:"@max.self") as real, (arrayStore's valueForKeyPath:"@min.self") as real}
		end maxAndMin
		
		on setValue:aValue forKey:aString
			arrayStore's setValue:aValue forKey:aString
		end setValue:forKey:
		
		-- filtering; requires valid predicate string		
		on filteredArrayUsingPredicateString:aString -- return filtered list
			set thePredicate to current application's NSPredicate's predicateWithFormat:aString
			return (arrayStore's filteredArrayUsingPredicate:thePredicate) as list
		end filteredArrayUsingPredicateString:
		
		on filterUsingPredicateString:aString -- filter in place
			set thePredicate to current application's NSPredicate's predicateWithFormat:aString
			arrayStore's filterUsingPredicate:thePredicate
		end filterUsingPredicateString:
		
		-- return as list/array
		on asArray()
			return arrayStore
		end asArray
		
		on asList()
			return arrayStore 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
			arrayStore's sortUsingDescriptors:{theDesc}
		end sortUsingSelector:
		
	end script
	
	set arrayStore of SmartList to current application's NSMutableArray's arrayWithArray:aList
	return SmartList
end smartListWith:

Some example code:

use theLib : script "<name of library>"
use scripting additions

set theList to theLib's smartListWith:{1, 2, 3, 5, 3, 2, 6}
log (theList's indexOfObject:5)
theList's insertObject:"blah" atIndex:3
log theList's asList()
log (theList's firstObjectCommonWithArray:{4, 5, 3})
theList's removeObjectAtIndex:3
log theList's asList()
log (theList's filteredArrayUsingPredicateString:"SELF > 2")
log (theList's componentsJoinedByString:" and ")
log (theList's valueForKey:"doubleValue")
(theList's valueForKey:"stringValue")

Edited to add handlers valueForKeyPath:, sumAndAverage(), and maxAndMin().