Peter Barrett

Q: Set an indeterminate number of variables

Hi fellow scriptologists... I thought I was good at this but I'm not as good as I thought I was...

 

I need to create a number of variables (Variable1, Variable2 etc) based on the contents of a list of indeterminate length - headings of a document that's likely to change each time it's run. I'd like to assign a name to each variable that reflects its initial value ("Camera", "Shot", "Take", "Slate") although generic names would do, but my impression is that there's no way to ascribe a string to a variable name based on anything done within the script - you simply have to name it or you end up redefining another variable. And if you don't know how many you need, do you need to declare them all up front in order to be able to set them as required?

 

There may be a way, but I can't figure out how... any input appreciated.

Posted on Apr 17, 2015 11:17 PM

Close

Q: Set an indeterminate number of variables

  • All replies
  • Helpful answers

  • by red_menace,

    red_menace red_menace Apr 18, 2015 5:26 AM in response to Peter Barrett
    Level 6 (15,526 points)
    Desktops
    Apr 18, 2015 5:26 AM in response to Peter Barrett

    You can create arbitrary names for the keys in a record/dictionary using Cocoa via AppleScriptObj-C, but in regular AppleScript variable names are evaluated at compile time, so it's not really possible to create them when the script is run.  Depending on exactly what you are wanting to do, a solution would be to use a list (or a list of records), and index into the list. 

  • by Hiroto,

    Hiroto Hiroto Apr 18, 2015 11:53 AM in response to Peter Barrett
    Level 5 (7,286 points)
    Apr 18, 2015 11:53 AM in response to Peter Barrett

    Hello

     

    You might be looking for something like hash table perhaps? Unlike other modern scripting languages, AppleScript has never had built-in hash table. Here's some options.

     

    Option 1. Use unnamed collection in lieu of named variables. This is straightforward method which should suffice in most cases.

     

    E.g., use:

     

    set vars to {1, 2, 3, 4, 5}
    

     

     

    in lieu of:

     

    set var1 to 1
    set var2 to 2
    set var3 to 3
    set var4 to 4
    set var5 to 5
    

     

     

     

    Option 2. Implement your own hash table (associative array) if you really want to.

     

    E.g.,

     

    set h to assoc()'s new(127)
    h's store({"var1", 1, "var2", 2, "var3", 3, "var4", 4, "var5", 5})
    --return h's ht
    --return h's fetch("var4")
    return h's fetch({"var3", "var5"})
    
    on assoc()
        script o
            property name : "assoc"
            property version : "0.12"
            property ht : {} -- ht entry is {} or list of {key, value}
            property sz : 0 -- given in new();  prime number for better performance
            property xx : {}
            property cc : {}
            on hash(s)
                (*
                    anything s : key coercible to string
                    return integer : hash code
                *)
                set cc to (s as string)'s id as list -- OS X 10.5 or later
                set result to 73
                repeat with c in my cc
                    (result * 37 + c) mod 1048573 as integer
                end repeat
                result mod sz + 1 as integer
            end hash
            on fetch(argv)
                (*
                    anything or list argv : key or list of keys (key can be anything coercible to string)
                    return anything or list : value or list of values associated with given key(s)
                        (returns missing value for non-existent key)
                *)
                if argv's class = list then
                    set xx to {}
                    repeat with a in argv
                        set my xx's end to fetch(a's contents)
                    end repeat
                    return my xx's contents
                else
                    repeat with h in my ht's item hash(argv) -- linear search in collision
                        if h's item 1 is argv then return h's item 2
                    end repeat
                    return missing value
                end if
            end fetch
            on store(argv)
                (*
                    list argv : {key, value} pair or {key1, value1, key2, value2, ...} pairs
                    return list : for given pair {k, v}, 
                            {k, v} if k does not exist in table; or
                            {{k, v}, {k, v1}} if k exists in table, where v1 is old value for k
                *)
                local argc
                set argc to count argv
                if argc > 2 and argc mod 2 = 0 then
                    set xx to {}
                    repeat with i from 1 to argc by 2
                        set my xx's end to store(argv's items i thru (i + 1))
                    end repeat
                    return my xx's contents
                else if argc = 2 then
                    -- separate chaining
                    local p, v1
                    set {k, v} to argv
                    set p to hash(k)
                    repeat with h in my ht's item p -- linear search in collision
                        if h's item 1 = k then
                            set v1 to h's item 2
                            set h's item 2 to v
                            return {{k, v}, {k, v1}} -- {new entry, old entry}
                        end if
                    end repeat
                    set end of my ht's item p to {k, v}
                else
                    error my name & "::store(): odd number of arguments." number 8001
                end if
            end store
            on scratch(argv)
                (*
                    anything or list argv : key or list of keys to delete
                    return anything or list : value or list of values associated with deleted key(s)
                        (returns missing value for non-existent key)
                *)
                if argv's class = list then
                    set xx to {}
                    repeat with a in argv
                        set my xx's end to scratch(a's contents)
                    end repeat
                    return my xx's contents
                else
                    -- delete key and return key's value if any
                    local p, v, h
                    set p to hash(argv)
                    repeat with i from 1 to count my ht's item p -- linear search in collision
                        set h to my ht's item p's item i
                        if h's item 1 = argv then
                            set v to h's item 2
                            set my ht's item p's item i to false
                            set my ht's item p to my ht's item p's lists
                            return v -- key's value
                        end if
                    end repeat
                    return missing value
                end if
            end scratch
            on keys()
                (*
                    return list : list of stored keys (in internal storage order)
                *)
                set xx to {}
                repeat with p in my ht
                    repeat with h in p
                        set end of my xx to h's item 1
                    end repeat
                end repeat
                return my xx's contents
            end keys
            on values()
                (*
                    return list : list of stored values (in internal storage order)
                *)
                set xx to {}
                repeat with p in my ht
                    repeat with h in p
                        set end of my xx to h's item 2
                    end repeat
                end repeat
                return my xx's contents
            end values
            on new(m)
                (*
                    integer m : size of hash table to be created (prime number for better performance)
                    return script : initialized hash table object
                *)
                if m's class is integer and m > 0 then
                    set sz to m
                    repeat sz times
                        set end of my ht to {}
                    end repeat
                    return o
                else
                    error my name & "::new(): invalid size." number 8000
                end if
            end new
        end script
    end assoc
    

     

     

     

    Regards,

    H

  • by fearless,

    fearless fearless Apr 18, 2015 1:50 PM in response to Hiroto
    Level 6 (9,532 points)
    Servers Enterprise
    Apr 18, 2015 1:50 PM in response to Hiroto

    Wow! I'm sure Option A will suffice, thank you - but Option B is an awesome journey through the code base - thanks, I'll spend the rest of my weekend trying to figure out how it all works. Impressive, thanks again.

     

    Peter (using my other AppleId)