Is it possible to remove items efficiently with commands? A repeat on a list of 1000 items would probably be slow, and to create the new one too.
Hi, Richard.
Technically, in AppleScript, you don’t remove items from a list: you make a new list which doesn’t contain those items. So, if you have a lot of items to “remove”, you want a method which makes as few new lists as possible in the process. That, of course, depends on what’s in the list and what you want to remove.
To drop a whole chunk from a list, the concatenation of the remaining bits is good:
-- Omit items 11 thru 19.
set newList to items 1 thru 10 of oldList & items 20 thru 30 of oldList
Obviously the concatenation’s not needed if the dropped chunk is at either end of the list.
If the items are all of the same class, you could replace the unwanted ones with an item of another class, then get a new list containing just the class you want:
set theList to {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
set mv to missing value
set item 1 of theList to mv
set item 5 of theList to mv
set item 8 of theList to mv
set newList to theList's integers
--> {2, 3, 4, 6, 7, 9, 10}
This method’s also good when you only want to keep items of a certain class anyway.
If the list contains only text and you want to remove certain values, you could use text item delimiters ” but you also need a character you know isn’t in any of the texts:
set theList to {"the", "long", "and", "the", "short", "and", "the", "tall"}
set unusedChr to "|" -- A character which doesn't appear in any item in the list.
-- Remove the "the"s:
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to unusedChr
set x to unusedChr & theList & unusedChr
set AppleScript's text item delimiters to unusedChr & "the" & unusedChr
set x to x's text items
set AppleScript's text item delimiters to unusedChr
set newList to text items 2 thru -2 of (x as text)
set AppleScript's text item delimiters to astid
newList
It’s a lot of code but it’s very fast.
I believe there’s also a command in the Satimage OSAX which does the job pretty quickly.
Hope that’s given you a few pointers.
In addition to the methods Nigel has outlined, it’s also possible to substantially speed up list processing by embedding the lists in a script object. Here’s an example (from Nigel Garvey) on a list sorter. Notice that the first block in the handler is script bs.
set the composer_list to {"Ellington", "Copland", "Bach", "Mozart", "Chopin"}
stillSort(the composer_list)
on stillSort(theList) -- Bubble sort without the bubbles.
-- defining an internal script makes for faster run times!
script bs
property alist : theList
end script
set theCount to length of bs's alist
if (theCount < 2) then return bs's alist
repeat with sortLimit from theCount to 2 by -1
set hiVal to beginning of bs's alist
set hiPos to 1
repeat with thisPos from 2 to sortLimit
set thisVal to item thisPos of bs's alist
if (thisVal > hiVal) then
set hiVal to thisVal
set hiPos to thisPos
end if
end repeat
set item hiPos of bs's alist to item sortLimit of bs's alist
set item sortLimit of bs's alist to hiVal
end repeat
return bs's alist
end stillSort
Bear in mind though that the script object hack only speeds up the getting and setting of items in the list, particularly where a lot of items are handled. It doesn’t speed up concatenation, coercion, or the extraction of text items from text.
Defining the list as a property fasten its treatment with no need for the script object structure when we call the list with the possessive my as I does in this example :
set my property_list to item 3 thru -5 of my property_list
Yvan KOENIG (VALLAURIS, France) samedi 7 août 2010 23:46:41
Hello.
Will this code
set ml to {"Bananas", "Ananas", "Strawberries", "Blue Berries", "Huckle Berries"}
rm5First(ml)
log ml
on rm5First(aTextList)
script o
property l : aTextList
end script
set a to (count aTextList)
repeat with i from 1 to a
set item i of o's l to text 5 thru -1 of item i of o's l
end repeat
end rm5First
Execute slower than this?
set ml to {"Bananas", "Ananas", "Strawberries", "Blue Berries", "Huckle Berries"}
rm5First(ml)
log ml
on rm5First(aTextList)
set a to (count aTextList)
repeat with i from 1 to a
set item i of aTextList to text 5 thru -1 of item i of aTextList
end repeat
end rm5First
I thought that the access to the list items before the operation counted for something, and today I’m too lazy to time anything, but I have wondered about this.
Possibly, given the shortness of the list. But today I’m too lazy to tell you.
Hello.
I tested the code I wrote with the words file of /usr/share/dict, that is every word that was 6 characters or longer.
-I ended up with a list of some 215,000 words. -And I removed the last word from the count. since that only contained a linefeed.
cat words |grep "[a-z]\{6,\}" >~/Desktop/words.txt
Then I timed the example with an internal script object, and it took 2 seconds with the date function to run it.
I had to kill Apple Script Editor when running the second example, after 10 minutes or so. (It felt like it)
Thank you thats exactly what I wanted (2nd way) I will find out which is best. One more thing, is there a limit on the size of a list?
Only the available memory, I think.