Removing List Items

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. :slight_smile:

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. :wink:

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.