In macOS AppleScript, how do I check if a property exists? - macos

In AppleScript I access an object in an application, e.g.:
tell application id "DNtp"
repeat with g in (children of root of (think window 1))
set theAnnotation to annotation of g
end
end tell
I know that depending on the child accessed, g sometimes has no annotation property.
N.B. This is not the same as having a missing value or something like that.
When running the code, AppleScript simply complains that the variable theAnnotation is not defined.
Other than trapping the error with a try ... on error statement, how do I check if g has the property annotation?

If you do
tell application id "DNtp"
annotation of children of root of (think window 1)
end tell
You'll get a list containing annotations and if those are missing, missing values, like:
--{missing value, missing value, missing value, «class DTcn» id 49 of «class DTkb» id 1, missing value}
The problem you describe in DEVONthink (of AS ignoring a declared variable during compile time) is a problem I've seen in other apps, too (but it's fairly rare).
If you want to check for the existence of a property, what usually works (and I've tested this with DEVONthink3) is to use exists, like:
if (exists annotation of g) then
Which will return true or false as you would expect. Not sure how you'd use that in the way you first posted, but I don't really know all the steps you're taking, so ....
I hope this helps

I don't use DEVON-related software, but in ordinary1 situations when dealing with AppleScript records, CRGreen's suggestions won't apply: exists is not a command that properties understand, especially non-existent properties; and referencing a property that does not exist will throw an error.
I'm glad you're looking for an alternative to try...end try. I've seen your previous code samples that are drowning in them, and they are expensive operations when an error is caught, so not ideal for what you're attempting. try really has no place in AppleScript at all.
Rather than testing for the existence of a property within a record, the way to approach this in a general context is to create a record object that contains all the property values that would ever be needed, and to assign default values to each of them.
In AppleScript, a record is observed to follow the following behaviour:
A single record object can only contain one property with a given identifier. Should you attempt to insert two properties both of which are identified the same, the compiler will keep the first property and its associated value, and scrub the rest:
{a:1, b:2, a:3} # will resolve on compilation immediately to {a:1, b:2}
Two record objects can contain properties with shared identifiers, like so:
set L to {a:1, b:2, c:3}
set R to {d:missing value, c:L}
Similarly to list objects, two record objects can be merged into a single record, and the properties will be amalgamated: properties with identifiers unique to each record will simply be inserted without any change into the resulting data structure. Where an identifier occurs in both record objects before the merge, again, precedence is given in a left-to-right reading order, so the properties in the prefixed record (on the left) will prevail and the suffixed record (on the right) will have its non-unique property identifiers (and their values) scrubbed:
L & R --> {a:1, b:2, c:3, d:missing value}
R & L --> {d:missing value, c:{a:1, b:2, c:3}, a:1, b:2}
Your code snippet contains this:
repeat with g in (children of root of (think window 1))
set theAnnotation to annotation of g
end
Therefore, g is an item contained within children (a list object), and the type class of g is a record. Depending on which item of children is being examined, I'm assuming that some of those items are records that do contain a property identified by annotation, and some of those items are records that do not contain such a property.
However, consider the following record that results from this merge:
g & {annotation:missing value}
Here are the two possible scenarios:
g is a record that already contains a property identified as annotation, e.g.:
set g to {cannotation:"doe", bannotation:"ray", annotation:me}
g & {annotation:missing value} --> {cannotation:"doe", bannotation:"ray", annotation:«script»}
set theAnnotation to annotation of (g & {annotation:missing value})
--> «script» (i.e. me)
OR:
g is a record that in which the property identifier annotation does not exist, e.g.:
set g to {doe:"a deer", ray:"a drop of golden sun"}
g & {annotation:missing value} --> {doe:"a deer", ray:"a drop of golden sun", annotation:missing value}
set theAnnotation to annotation of (g & {annotation:missing value})
--> missing value
Therefore, for every place in your scripts where try...end try has been used to catch non-occurrences of properties inside a record data structure, simply delete the try blocks, and wherever you are assign values read from speculative property values, artificially insert default values you can then test for and will know whether or not the value came from your DEVONthink source or from your brain:
tell application id "DNtp"
repeat with g in (children of root of (think window 1))
set theAnnotation to annotation of (g & {annotation:false})
if theAnnotation ≠ false then exit repeat
end
end tell
1This isn't in any way meant to suggest his solution isn't viable. If DEVON returns collections that are not de-referenced--and it very well may do--these can be operated upon as a whole, without looping through individual items, and he, of course, uses DEVON. But the situation I hopefully address above is one that arises much more commonly, and will also work here.

Related

AppleScript to toggle geek tools picture on and off

relatively new to coding - I do some music related things and I'm trying to create an overlay to alert me when certain filters are active
geek tools can display a picture which works fine, I can make it appear and disappear, I'm just trying to toggle it on and off
I've got:
tell application "GeekTool Helper"
set g to geeklet id "C50F8977-88DF-4813-82C3-23D1C23F4624"
set visible of g to true
end tell
and then the same thing as "false" to make it go away. Again just trying to toggle it back and forth for on one key. I'm sure this is super easy - would really appreciate it if anyone has a sec to help out. Guessing it's some sort of if/then or something...
thank you
I'm not familiar with this application, but I'm going to assume that your code snippet exemplifies a fully-working script that displays the referenced picture:
tell application "GeekTool Helper"
set g to geeklet id "C50F8977-88DF-4813-82C3-23D1C23F4624"
set visible of g to true
end tell
In order to toggle the state of the picture's visibility, simply apply standard boolean logic. In case you're not familiar with this, it's basically the notion that true and false are mutually-exclusive (i.e. something cannot be both true and false), and they are each the other's inverse (i.e. something that is not true must therefore be false, and something that is not false must therefore be true).
In many programming and scripting languages, the boolean values of true and false are commonly represented by constants of the same name. In AppleScript, as you already know, these constants are true and false. There are a trio of boolean operators, and, or, and not. The last one of these operators is the key to implementing a state toggle, on the basis that:
not true = false
and:
not false = true
Borrowing the references from your script, toggling works on the underlying principle of evaluating the property to discern its present state, and then assigning a new value to reflect the opposing state. Here's the logic laid out in full:
if the visible of g = true then
set the visible of g to false
else if the visible of g = false then
set the visible of g to true
end
This is one instance where AppleScript's "natural language" paradigm does precisely what it says, so this is hopefully self-explanatory. (This is not always going to be be the case, just to warn you.)
While this is perfectly ready to cut and paste into your script, there's a few things we can do to clean it up a bit, one of which I alluded to earlier, namely using the not operator to reduce some of the repetitive exposition of logic that can become tiresome.
not is acts upon a single boolean value, and gives us back the opposite (or negated) value, e.g:
not true
Result: false
Applying this to one half of the logic from previously, this:
if the visible of g = true then set the visible of g to false
is equivalent to this:
if the visible of g = true then set the visible of g to not true
Likewise, the same applies to the second half of the logic in an identical fashion:
if the visible of g = false then set the visible of g to not false
The not operator needn't only be applied to the explicit true and false constants. After all, if this test is passed:
if the visible of g = true
this implies that visible of g—which is equal to true—is, therefore, itself a boolean value. We can thus apply the not operator directly to the property itself:
if the visible of g = true then set the visible of g to not visible of g
Likewise, for the opposing start state:
if the visible of g = false then set the visible of g to not visible of g
Thus, what initially seemed like two separate conditions being evaluated, and two distinct outcomes being catered for by our code, is, in fact, functionally a singular process that acts upon the state in an identical fashion irrespective of what the initial value of the state is.
What this means is that we need never have to know the starting state in order to effect a toggle, and therefore, we don't actually have to test it at all. We can simply do this:
set the visible of g to not visible of g
Inserting this back into you original script, we get this toggling capability:
tell application "GeekTool Helper"
set g to geeklet id "C50F8977-88DF-4813-82C3-23D1C23F4624"
set visible of g to not visible of g
end tell
Whilst we're done with respect to implementing the logic, we can also clean up the references to the visible property a little bit in much the same way as we did with the boolean expressions above. From hereon, the principles will be much more specific to AppleScript, although it's by no means the only language which defines a clear ownership hierarchy for the various elements that appear in a script. For your script, we begin at the very top-level that isn't immediately obvious, but it always present: that's the AppleScript instance itself, which is represented by the enumerated value assigned to the constant current application. Everything else in a script is ultimately owned by the current application.
Moving down a level, one of the current application's immediate descendants is the application class object for the GeekTool Helper app. This is where relationships and ownership becomes more overt in the code, as AppleScript provides the keyword tell in order for us to access the child objects belonging to an element. So moving down another level, we encounter the geeklet class object, which is the direct descendant of the GeekTool Helper application object. Finally, in our case, this brings us to one of likely many properties belonging to any geeklet class object, in this case, the visible property that reports on as well as dictates back the visibility of the picture referenced by the geeklet element.
The point of this hierarchy is exemplified in the way you've enclosed the progeny of an application class object within a singular tell block, which forwards any statements that arise to the application element without the need to continuously reference the parent object every time we wish to access one of its children. It's not so obvious how convenient this ends up being because your script is very short. Nonetheless, I'm going to milk it for all its worth, and I'll start by removing the declaration of the variable g and, instead, tell-ing the geeklet element what to do:
tell application "GeekTool Helper"
tell geeklet id "C50F8977-88DF-4813-82C3-23D1C23F4624"
set visible to not visible
end tell
end tell
Since this new tell block creates an enclosure similar to the one it itself lives in, commands will get forwarded onto the geeklet without having to be explicit about referring to it each time. This is why we no longer require to say visible of g (or visible of geeklet id "..."), and we can just focus on the property that we intend to manipulate.
As each of the tell blocks only contains a single line or a single block, these can be collapsed into a single-line, compound tell statement by way of the to joiner:
tell application "GeekTool Helper" to tell geeklet id ¬
"C50F8977-88DF-4813-82C3-23D1C23F4624" to set ¬
visible to not visible
It still looks like three lines, but it's one line divided over three lines mostly for readability. The continuation character (¬) lets AppleScript know to continue reading the next line as it's part of the same instruction.
For the final step, let's put this into a handler, which will allow this toggling functionality to become a generalised tool that can be used to toggle any geeklet whose id value is given to the handler:
to toggle(uuid)
tell application "GeekTool Helper" to tell ¬
geeklet id uuid to set visible to ¬
not visible
end toggle
This can appear anywhere in your script, although it's sensible to put it (and any other handlers you create yourself in the future) at the bottom of the script, together in one place. To make us of it, you simply call it with the id value of the geeklet object to be toggled:
toggle("C50F8977-88DF-4813-82C3-23D1C23F4624")
FIN ◻︎
toggle("C50F8977-88DF-4813-82C3-23D1C23F4624")
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
to toggle(uuid)
tell application "GeekTool Helper" to tell ¬
geeklet id uuid to set visible to ¬
not visible
end toggle

Assigning a value to merged cells in Apple Numbers using AppleScript

As part of a longer AppleScript, I'm copying strings from a list variable into ranges of merged cells in a Numbers 10.0 table. E.g., if the list is called form_filler and the first range of merged cells is B6:Y7, I first tried:
set value of cell "B6" to item 1 of form_filler
I thought one addresses merged cells by the top-left cell. But this does something unexpected: it places the string only into cell "B6" and changes the range of merged cells to C6:Y7, excluding the cell I just pasted into. This behavior occurs consistently with different merged cells throughout the table. I then tried:
set value of range "B6:Y7" to item 1 of form_filler
but this returned an error; I can't assign a value to the range.
I'm new to AppleScript, but not programming generally (e.g., Python). What am I missing? Thanks.
It looks like you have to re-merge those cells. Here's code I just tested using my own tell block structure; you should be able to extrapolate from this (if you include your tell block structure I'll edit my code):
tell application "Numbers"
set d to sheet 1 of document 1
tell d
set value of cell "B6" of table 1 of it to "test"
merge range "B6:Y7" of table 1 of it
end tell
end tell
Not sure if this qualifies as a "work-around", but it seems to work, hopefully w/o introducing other issues.

Using selectAll in d3.js

I would like to selectAll X that have the properties Y and Z. For instance, I would specifically like to select all lines with an ID of d.id AND a target of d.target. It's the 'and' part that I can't figure out; I assumed from the documentation that it should be something like this:
selectedLine = d3.selectAll("line[id="+d.id+"]").selectAll(line[target="+d.target+"]");
But calling selectAll on a selection always returns an empty selection to me, regardless of the contents (In this case, I'm certain that exists one line with an ID of d.id and a target of d.target).
I suspect that target is not an attribute that the line element but a data property instead. Besides that, calling selectAll on a selection does not return an empty selection: you probably have some other problem. Finally, pay attention to the fact that you are using selectAll with an ID, which makes little sense: IDs have to be unique.
Anyway, whatever you are using in those attribute selectors, you can simply use multiple attribute selectors:
selectedLine = d3.selectAll("line[id='foo'][target='bar']")
According to the documentation:
Multiple attribute selectors can be used to refer to several attributes of an element, or even several times to the same attribute.
Here, the selector matches all SPAN elements whose "hello" attribute has exactly the value "Cleveland" and whose "goodbye" attribute has exactly the value "Columbus":
span[hello="Cleveland"][goodbye="Columbus"]

Hashing table design in C

I have a design issue regarding HASH function.
In my program I am using a hash table of size 2^13, where the slot is calculated based on the value of the node(the hash key) which I want to insert.
Now, say my each node has two value |A|B| however I am inserting value into hash table using A.
Later on, I want to search a particular node which B not A.
Is it possible to that way? Is yes, could you highlight some design approaches?
The constraint is that I have to use A as the hash key.
Sorry, I can't share the code. Small example:
Value[] = {Part1, Part2, Part3};
insert(value)
check_for_index(value.part1)
value.part1 to be used to calculate the index of the slot.
Once slot is found then insert the "value"
Later on,
search_in_hash(part2)
check_for_index("But here I need the value.part1 to check for slot index")
So, how can I relate the part1, part2 & part3 such that I later on I can find the slot by either part2 or part3
If the problem statement is vague kindly let me know.
Unless you intend to do a search element-by-element (in which case you don't need a hash, just a plain list), then what you basically ask is - can I have a hash such that hash(X) == hash(Y), but X!=Y, so that you could map to a location using part1 and then map to the same one using part2 or 3. That completely goes against what hashing stands for.
What you should do is (as viraptor also suggested), create 3 structures, each hashed using a different part of the value, and push the full value to all 3. Then when you need to search use the proper hash by the part you want to search by.
for e.g.:
value[] = {part1, part2, part3};
hash1.insert(part1, value)
hash2.insert(part2, value)
hash3.insert(part3, value)
then
hash2.search_in_hash(part2)
or
hash3.search_in_hash(part3)
The above 2 should produce the exact same values.
Also make sure that all data manipulations (removing values, changing them), is done on all 3 structures simultaneously. For e.g. -
value = hash2.search_in_hash(part2)
hash1.remove(value.part1)
hash2.remove(part2) // you can assert that part2 == value.part2
hash3.remove(value.part3)

How to generate new unique name for context?

What is the best way to generate a name for some temporary context which is guaranteed to be unique (context with this name must not exist in the system)?
The following expression will generate a context name that is guaranteed not to conflict with any loaded context:
First#Contexts[] //.
c_ /; MemberQ[Contexts[], c] :>
"Context"~~ToString[RandomInteger[1000000]]~~"`"
It makes no attempt to account for contexts that are not yet loaded. As written, this expression could be used up to 1,000,000 times before running out of names. Adjust the fixed string ("Context") and name count (1000000) to suit your taste.
Update
As #Leonid points out in a comment, empty contexts will not be listed in Contexts[]. Therefore, it is strictly speaking possible that this expression could return the name of an existing empty context.
UUIDs
For all practical purposes, generating a name from a number randomly selected from a large enough range would work, e.g.
"Context"~~ToString[RandomInteger[2^128]]~~"`"
In a similar vein, one could use a UUID. UUIDs are routinely used as identifiers that are phenomenally likely to be unique across all network nodes as well:
Needs["JLink`"]
LoadJavaClass["java.util.UUID"]
"Context"~~
StringReplace[JavaBlock#java`util`UUID`randomUUID[]#toString[], "-" -> ""]~~
"`"
I can suggest a function I used here:
Clear[unique];
unique[sym_] :=
ToExpression[
ToString[Unique[sym]] <>
StringReplace[StringJoin[ToString /# Date[]], "." :> ""]];
You can replace the ToExpression by StringJoin[...,"`"] to tailor it to your needs.
Another option would be to look at all starting contexts (before the first backquote), find their string length and then generate a string (maybe random, but that isn't necessary) that is at least one character longer than the others. This is guaranteed to be unique, and there isn't even a theoretical possibility of a collision as in some of the other solutions.
sl = (StringSplit[#, "`"][[1]] & /# Contexts[] // StringLength // Max )
Out[349]= 30
In[353]:= "c" ~~ ToString[10^sl] ~~ "`"
Out[353]= "c1000000000000000000000000000000`"
A disadvantage of this method would be that the context names get longer after each repeated application of this method. ;-) If that's a problem we could create a unique name based on the set of longest context names using Cantor's diagonal procedure.
Is Unique perhaps what you're looking for?
This is really an example illustrating Alexey's response to Sjoerd's answer/question. From a fresh kernel on my machine, the following code
Begin["myContext3`"];
Unique["myContext"]
Yields "myContext3". Thus, clearly, Unique (the first thing I thought of) does not work.
Incidentally, I would have just added a comment to Sjoerd's response, but I don't know how to include the accent symbol used to denote a context inline. Does anyone here know how to do this?

Resources