How can I refer abstractly to a list?

I’m hoping to learn from the pros:

In the following script, I can construct the name of the list but can’t figure out how to tell AppleScript I’m referring to a list and not a string:


set email_list to {"someone@somewhere.com", "anotherone@elsewhere.com"}

-- setup lists:
set a_list to {}
set s_list to {}

------------------------------------------------------------


-- SORT THE LIST OF EMAIL ADDRESSES:

repeat with this_address in email_list
	-- get the first letter of this_address:
	set first_letter to character 1 of this_address as string
	-- set the correct list to look into:
	set correct_list to first_letter & "_list" as string
	log "correct_list is:"
	log correct_list
	
	-- ******************************
	-- HOW CAN I REFER ABSTRACTLY TO A LIST?:
	copy this_address to end of my correct_list -- AS sees correct_list simply as a string
	
end repeat

Hello.

I understand your question as you you want your script to get the correct list to store the value into.

An interesting problem. -But I can’t se any way that abstraction would help you.
I have a method that should work for you though.

I think it is best solved with having a list of lists, containing each and every list from a-z.

Then I’d use a construct like this to get the proper list within the list of lists:


property list_of_list : {}
 
repeat with i from 1 to 26 -- a - z
	set end of list_of_list to {}
end repeat
log "" & (count of list_of_list) -- should return 26
-- your code to get first letter from address goes here ..

--  then we must find the correct offset number in the list of lists to use.

-- you have to make sure that the case of the first_letter is lowercase first.
if ((ASCII number of first_letter) is greater than 64) and ¬
		((ASCII number of first_letter) is less than 91) then
		set first_letter to  (ASCII character ((ASCII number of first_letter) + 32))
end if 

set the list_number to (ASCII number of first_letter) - (ASCII number of "a") + 1

set correct_list to item list_number of list_of_lists

It might not be a really elegant solution but this kluge should work.
I stole the code for converting from uppercsae to lowercase from hubion mac’s Addressbook scripts, which are great for unicode string handling and can be found here.

If it is always email addresses then I guess it would be ok to remove the test for uppercase.
I only added that for completeness.

I hope this helps and works

Best Regards

McUser

Hi.

Both ‘ASCII number’ and ‘ASCII character’ are deprecated as from Leopard. The preferred equivalents are now ‘id of’ and ‘character id’.

If we’re assuming just the 26 letters of the English alphabet, both the upper case and lower case versions have ids (or ASCII numbers) that are a multiple of 32 + the letters’ positions in the alphabet. So a simple ‘mod 32’ will return that position and index the corresponding list.


set email_list to {"someone@somewhere.com", "anotherone@elsewhere.com", "Aardvark@nowhere.com"}

set {a_list, b_list, c_list, d_list, e_list, f_list, g_list, h_list, i_list, j_list, k_list, l_list, m_list, n_list, o_list, p_list, q_list, r_list, s_list, t_list, u_list, v_list, w_list, x_list, y_list, z_list} to {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}
set all_lists to result

repeat with this_address in email_list
	set i to (id of character 1 of this_address) mod 32
	set end of item i of all_lists to contents of this_address
end repeat

return {a_list, s_list}
--> {{"anotherone@elsewhere.com", "Aardvark@nowhere.com"}, {"someone@somewhere.com"}}

What you want is an associative array/hash/map/dictionary; basically, some sort of data structure that lets you store and retrieve values using strings as keys. Most scripting languages come with such a data structure already built in; AppleScript doesn’t, but you can roll your own. Here’s one I wrote a while back:

on make_hash()
	set the_list to {missing value}
	repeat 12 times
		set the_list to the_list & the_list
	end repeat
	script HashObj
		property _List : the_list
		
		on _hash(s) -- RSHash modified for AppleScript
			set max_size to 2 ^ 16
			set a to 63689
			set b to 378551
			set hash_num to 0
			repeat with c in (id of s) as list
				set hash_num to (hash_num * a + c) mod max_size
				set a to (a * b) mod max_size
			end repeat
			return hash_num
		end _hash
		
		on _find_entry_for_key(the_key)
			considering case, diacriticals, hyphens, punctuation and white space
				set list_index to (_hash(the_key) mod (length of my _List)) + 1
				set sub_list to item list_index of my _List
				if sub_list is missing value then
					set sub_list to {}
					set item list_index of my _List to sub_list
				end if
				repeat with entry_ref in sub_list
					if the_key of entry_ref = the_key then return {true, list_index, entry_ref}
				end repeat
				return {false, list_index, missing value}
			end considering
		end _find_entry_for_key
		
		on has_item(the_key)
			return item 1 of _find_entry_for_key(the_key)
		end has_item
		
		on count_items()
			set n to 0
			repeat with sub_list_ref in my _List
				if contents of sub_list_ref is not missing value then
					set n to n + (count sub_list_ref)
				end if
			end repeat
			return n
		end count_items
		
		on set_item(the_key, the_value)
			set {was_entry_found, list_index, entry_ref} to _find_entry_for_key(the_key)
			if was_entry_found then
				set the_value of entry_ref to the_value
			else
				set end of item list_index of my _List to {the_key:the_key, the_value:the_value}
			end if
			return
		end set_item
		
		on get_item(the_key)
			set {was_entry_found, list_index, entry_ref} to _find_entry_for_key(the_key)
			if not was_entry_found then error "Key not found." number -1728 from the_key
			return the_value of entry_ref
		end get_item
		
		on delete_item(the_key)
			set {was_entry_found, list_index, entry_ref} to _find_entry_for_key(the_key)
			if not was_entry_found then error "Key not found." number -1728 from the_key
			set contents of entry_ref to missing value
			set item list_index of my _List to (get every record of item list_index of my _List)
			return
		end delete_item
	end script
end make_hash


set h to make_hash()

repeat with c in "abcdefghijklmnopqrstuvwxyz"
	h's set_item(c's contents, {})
end repeat

set email_list to {"someone@somewhere.com", "anotherone@elsewhere.com"}
repeat with address_ref in email_list
	set end of h's get_item(character 1 of address_ref) to address_ref's contents
end repeat

h's get_item("a")
--> {"anotherone@elsewhere.com"}

Nothing too fancy; you can get/set/delete items, though it could be expanded to do more. Reasonable performance by AppleScript standards. Main limitations are that it accepts only strings as keys, is case-sensitive, and requires 10.5+. Another option would be the AppleMods Types library, which has a couple of alternative implementations with different pros and cons.

You might also like to check out the new edition of Apress’s Learn AppleScript: The Comprehensive Guide to Scripting and Automation on Mac OS X (I was co-author on this edition). The lists and records chapter includes a similar exercise to count the frequency of words in text, and later chapters expand on this to develop a simple associative list object that you can use in your own code. I think you’d find it very helpful - I put that material in precisely because I was asking exactly the same questions back when I was learning AppleScript.

HTH

Thank you McUsr, Nigel and hhas for your fascinating and enlightening answers. As always, I am amazed at the wealth of insight to be gleaned from each person’s approach to solving a problem. And thanks, particularly, for your willingness to help!

Mahalo (thanks),
icta