Sunday, September 22, 2019

#1 2019-06-12 11:42:18 am

Tony
Member
Registered: 2011-05-26
Posts: 4

Can't delete item from custom associative list

I'm working my way through Hamish Anderson and Hanaan Rosenthal's excellent Learn AppleScript. If you know a little about AppleScript but want to get a clearer understanding of what you're doing and how to do it better, I highly recommend this book.

I believe I have found a flaw in one of the exercise scripts in the book, and I've spent a few days trying to figure out why it doesn't work and how to fix it. I have failed.

At first I thought I had a typo somewhere in my script, but then I went to the book's website and downloaded the code. The downloaded script gives me the same error as the one I had typed out myself.

It's script 18-22 in the book. When I run it, I get this error:

Can’t set item 1 of the_items of {class:"associative list", the_items:{{the_key:"Bob", the_value:35}, {the_key:"Jan", the_value:38}}} to missing value.



Script Debugger highlights this line in the delete_associative_item handler:

Applescript:

set contents of record_ref to missing value

The script is below. What's going wrong?

Applescript:



on make_associative_list()
   (* Make a new associative list that stores values using any objects as keys.

       Result: an associative list

       Note: Users should not directly manipulate the contents of the returned
       object. Instead, use the handlers provided to work with it safely.
   *)

   return {class:"associative list", the_items:{}}
end make_associative_list


on find_record_for_key(the_assoc_list, the_key)
   (* This is a private handler. Users should not use it directly. *)
   considering diacriticals, hyphens, punctuation and white space but ignoring case
       repeat with record_ref in the_items of the_assoc_list
           if the_key of record_ref = the_key then return record_ref
       end repeat
   end considering
   return missing value -- The key wasn't found
end find_record_for_key


on get_associative_item(the_assoc_list, the_key)
   (*
       Get the value for the given key in an associative list.

       the_assoc_list : associative list
       the_key : anything -- the key to search for
       Result : anything -- the value, if found

       Note: Raises error -1728 if the key isn't found.
   *)

   set record_ref to find_record_for_key(the_assoc_list, the_key)
   if record_ref = missing value then
       error "The key wasn't found." number -1728 from the_key
   end if
   return the_value of record_ref
end get_associative_item


on set_associative_item(the_assoc_list, the_key, the_value)
   (*
       Set the value for the given key in an associative list.

       the_assoc_list : associative list
       the_key : anything -- the key to use
       the_value : anything -- the new value
   *)

   set record_ref to find_record_for_key(the_assoc_list, the_key)
   if record_ref = missing value then
       set end of the_items of the_assoc_list to {the_key:the_key, the_value:the_value}
   else
       set the_value of record_ref to the_value
   end if
   return -- No return value; the handler modifies the existing associative list.
end set_associative_item


on count_associative_items(the_assoc_list)
   (*
       Return the number of items in an associative list.

       the_assoc_list : associative list
       Result : integer
   *)

   return count the_items of the_assoc_list
end count_associative_items


on delete_associative_item(the_assoc_list, the_key)
   (*
       Delete the value for the given key.

       the_assoc_list : associative list
       the_key : anything -- the key to delete
   *)

   set record_ref to find_record_for_key(the_assoc_list, the_key)
   if record_ref is missing value then
       error "The key wasn't found." number -1728 from the_key
   end if
   set contents of record_ref to missing value
   set the_items of the_assoc_list to every record of the_items of the_assoc_list
   return -- No return value; the handler modifies the existing associative list.
end delete_associative_item


set friends_ages to make_associative_list()
set_associative_item(friends_ages, "Bob", 35)
set_associative_item(friends_ages, "Jan", 38)
delete_associative_item(friends_ages, "Bob")


Offline

 

#2 2019-06-12 01:00:48 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5014

Re: Can't delete item from custom associative list

Hi.

It looks as if it should work, but for some reason, trying to change the 'contents' of the reference value returned by find_record_for_key() is erroring instead of changing the contents. It's possible to get round this by rewriting the script as below so that the handler returns an index instead of a reference, but it doesn't explain why the error occurrs in the first place.  hmm

Another interesting thing about the script is that, whereas the associative list is set as a record containing both a 'class' and a 'the_items' property, looking at the result in Script Debugger only shows the record with the 'the_items' property, while looking at it in Script Editor shows both properties and throws a -1700 error! Both anomalies appear to be connected with the use of the AppleScript 'class' keyword as a property label.

Applescript:



on make_associative_list()
   (* Make a new associative list that stores values using any objects as keys.

       Result: an associative list

       Note: Users should not directly manipulate the contents of the returned
       object. Instead, use the handlers provided to work with it safely.
   *)

   return {class:"associative list", the_items:{}}
end make_associative_list


on find_record_for_key(the_assoc_list, the_key)
   (* This is a private handler. Users should not use it directly. *)
   set the_items to the_items of the_assoc_list
   considering diacriticals, hyphens, punctuation and white space but ignoring case
       repeat with item_idx from 1 to (count the_items)
           if the_key of item item_idx of the_items = the_key then return item_idx
       end repeat
   end considering
   return missing value -- The key wasn't found
end find_record_for_key


on get_associative_item(the_assoc_list, the_key)
   (*
       Get the value for the given key in an associative list.

       the_assoc_list : associative list
       the_key : anything -- the key to search for
       Result : anything -- the value, if found

       Note: Raises error -1728 if the key isn't found.
   *)

   set item_idx to find_record_for_key(the_assoc_list, the_key)
   if item_idx = missing value then
       error "The key wasn't found." number -1728 from the_key
   end if
   return the_value of item item_idx of the_items of the_assoc_list
end get_associative_item


on set_associative_item(the_assoc_list, the_key, the_value)
   (*
       Set the value for the given key in an associative list.

       the_assoc_list : associative list
       the_key : anything -- the key to use
       the_value : anything -- the new value
   *)

   set item_idx to find_record_for_key(the_assoc_list, the_key)
   if item_idx = missing value then
       set end of the_items of the_assoc_list to {the_key:the_key, the_value:the_value}
   else
       set the_value of item item_idx of the_items of the_assoc_list to the_value
   end if
   return -- No return value; the handler modifies the existing associative list.
end set_associative_item


on count_associative_items(the_assoc_list)
   (*
       Return the number of items in an associative list.

       the_assoc_list : associative list
       Result : integer
   *)

   return count the_items of the_assoc_list
end count_associative_items


on delete_associative_item(the_assoc_list, the_key)
   (*
       Delete the value for the given key.

       the_assoc_list : associative list
       the_key : anything -- the key to delete
   *)

   set item_idx to find_record_for_key(the_assoc_list, the_key)
   if item_idx is missing value then
       error "The key wasn't found." number -1728 from the_key
   end if
   set item item_idx of the_items of the_assoc_list to missing value
   set the_items of the_assoc_list to every record of the_items of the_assoc_list
   return -- No return value; the handler modifies the existing associative list.
end delete_associative_item


set friends_ages to make_associative_list()
set_associative_item(friends_ages, "Bob", 35)
set_associative_item(friends_ages, "Jan", 38)
delete_associative_item(friends_ages, "Bob")
return friends_ages --> {class:"associative list", the_items:{{the_key:"Jan", the_value:38}}} + error in SE, {the_items:{{the_key:"Jan", the_value:38}}} in SD.

Last edited by Nigel Garvey (2019-06-12 01:04:45 pm)


NG

Offline

 

#3 2019-06-12 01:24:42 pm

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3581

Re: Can't delete item from custom associative list

Hello Nigel

It seems that the problem may be easily solved.

Applescript:

on make_associative_list()
(* Make a new associative list that stores values using any objects as keys.

Result: an associative list

Note: Users should not directly manipulate the contents of the returned
object. Instead, use the handlers provided to work with it safely.
*)

return {|class|:"associative list", the_items:{}} # ADDED pipes enclosing class which is no longer treated as a reserved word
end make_associative_list

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 12 juin 2019  21:24:36

Offline

 

#4 2019-06-12 02:06:42 pm

KniazidisR
Member
Registered: 2019-03-03
Posts: 558

Re: Can't delete item from custom associative list

Yvan Koenig wrote:

Hello Nigel

It seems that the problem may be easily solved.

Applescript:

on make_associative_list()
(* Make a new associative list that stores values using any objects as keys.

Result: an associative list

Note: Users should not directly manipulate the contents of the returned
object. Instead, use the handlers provided to work with it safely.
*)

return {|class|:"associative list", the_items:{}} # ADDED pipes enclosing class which is no longer treated as a reserved word
end make_associative_list

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 12 juin 2019  21:24:36



Not so easily. Exists error in handler "delete"..... I believe that error starts from "find" handler

Last edited by KniazidisR (2019-06-12 02:08:37 pm)


macOS Mojave -- version 10.14.4
Safari -- version 12.1

Offline

 

#5 2019-06-12 02:49:37 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5014

Re: Can't delete item from custom associative list

Yvan Koenig wrote:

Hello Nigel

It seems that the problem may be easily solved.


Hi Yvan.

Adding pipes avoids the anomalies when displaying the record as a result in Script Editor and Script Debugger. But I suspect that Hamish and Hanaan deliberately used the AppleScript keyword in order to be able to do tests like get class of friends_ages.

The error Tony's been getting is to do with the reference returned by the find_record_for_key() handler. It correctly refers to an item in the list, but trying to set its 'contents' causes an error for some reason. Theoretically it should work, and presumably it did when the book was written.


NG

Offline

 

#6 2019-06-12 08:52:59 pm

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3581

Re: Can't delete item from custom associative list

KniazidisR wrote:


Not so easily. Exists error in handler "delete"..... I believe that error starts from "find" handler



I was writing about the script revised by Nigel, not about the original version.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 13 juin 2019  04:51:59

Offline

 

#7 2019-06-12 09:03:39 pm

Tony
Member
Registered: 2011-05-26
Posts: 4

Re: Can't delete item from custom associative list

Nigel Garvey wrote:

It looks as if it should work, but for some reason, trying to change the 'contents' of the reference value returned by find_record_for_key() is erroring instead of changing the contents. It's possible to get round this by rewriting the script as below so that the handler returns an index instead of a reference, but it doesn't explain why the error occurrs in the first place.  hmm



Thank you! Not to be overdramatic about it, but I thought I understood the relatively simple idea of "contents of" a reference, yet suddenly what I thought I had learned stopped making sense. That's a relief.

Offline

 

#8 2019-06-13 03:57:39 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5014

Re: Can't delete item from custom associative list

It seems that in Mojave at least:
• It's possible to get the 'contents' of a reference to an item in a list which is a property of a record or script object.
• It's possible to use the reference to change values associated with the item.
• It's not possible to replace the item itself using the 'contents' operator.
• However, if the reference is just to an item in a list, with no mention of a containing record or script object, it is possible to use the 'contents' operator to replace the item. So while this self-contained version of the deleting handler errors …

Applescript:

on delete_associative_item(the_assoc_list, the_key)
   (*
       Delete the value for the given key.

       the_assoc_list : associative list (which is actually a record!)
       the_key : anything -- the key to delete
   *)

   repeat with record_ref in the_items of the_assoc_list -- record_ref is a reference to an item in the list property of the record.
       if the_key of record_ref = the_key then -- Works.
           set contents of record_ref to missing value -- Errors.
           set the_items of the_assoc_list to every record of the_items of the_assoc_list
           return
       end if
   end repeat
   
   error "The key wasn't found." number -1728 from the_key
end delete_associative_item

… this version works:

Applescript:

on delete_associative_item(the_assoc_list, the_key)
   (*
       Delete the value for the given key.

       the_assoc_list : associative list (which is actually a record!)
       the_key : anything -- the key to delete
   *)

   set the_items to the_items of the_assoc_list -- Set a variable directly to the list.
   repeat with record_ref in the_items -- record_ref is now just a reference to an item in the list.
       if the_key of record_ref = the_key then -- Works.
           set contents of record_ref to missing value -- Also works.
           set the_items of the_assoc_list to every record of the_items -- of the_assoc_list
           return
       end if
   end repeat
   
   error "The key wasn't found." number -1728 from the_key
end delete_associative_item

Last edited by Nigel Garvey (2019-06-13 04:03:48 am)


NG

Offline

 

#9 2019-06-13 11:02:00 am

Tony
Member
Registered: 2011-05-26
Posts: 4

Re: Can't delete item from custom associative list

Nigel Garvey wrote:

… this version works:

Applescript:

on delete_associative_item(the_assoc_list, the_key)
   (*
       Delete the value for the given key.

       the_assoc_list : associative list (which is actually a record!)
       the_key : anything -- the key to delete
   *)

   set the_items to the_items of the_assoc_list -- Set a variable directly to the list.
   repeat with record_ref in the_items -- record_ref is now just a reference to an item in the list.
       if the_key of record_ref = the_key then -- Works.
           set contents of record_ref to missing value -- Also works.
           set the_items of the_assoc_list to every record of the_items -- of the_assoc_list
           return
       end if
   end repeat
   
   error "The key wasn't found." number -1728 from the_key
end delete_associative_item



Yes. Beautiful. And I believe I understand it.

So when we create this handler's the_items, any already-existing reference to any of the items in (the_items of the assoc_list) will also refer to that item as it exists in this handler's the_items. Correct?

Offline

 

#10 2019-06-13 12:37:40 pm

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 5014

Re: Can't delete item from custom associative list

Tony wrote:

So when we create this handler's the_items, any already-existing reference to any of the items in (the_items of the assoc_list) will also refer to that item as it exists in this handler's the_items. Correct?


Yes. When you set a variable or, say, a record property or a list item to an existing list, it's the same list object. You're just creating another way to access it. But if you copy a list to a variable (or record property or list item), the variable (or whatever) is set to a duplicate of the list. Changing the contents of this doesn't affect what's in the original.

By the way, I've now had a chance to try the original script on my El Capitan and Leopard systems and the same error occurs there. So the restriction on setting the 'contents' of a reference has been around for a while. I've not heard of it before, so I imagine not many people have actually had cause to run into it.


NG

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)