AppleScript: find out which numbers are missing?

I have a database which only contains records whose name is a number. I’m trying to find out which numbers are missing. Is this possible with AppleScript? Starting with a modified version of this script (Access with AppleScript the history of a DEVONthink database) I’ll get the names of all records in the current database.

But how to find out which numbers are missing?

tell application "DEVONthink Pro"
	set allRecords to contents of current database
	set myData to {}
	repeat with i from 1 to count of allRecords
		set end of myData to name of item i of allRecords
	end repeat
	return myData
	
end tell

What kind of numbers? What range? What are ‘missing numbers’?

Natural numbers starting with 1.

A snippet of the result I get from the code above looks like this {“78.html”, “150.html”, “163.html”}

By missing numbers I mean “1.html” thru “77.html” and “79.html” thru “149.html” (and so on).

What I need as result is something like this I think:

{“1”, “2”, “3”, … , “77”, “79”, …, “149”, “151”, … , “162”} (just the name without the extension).

Make a repeat loop to extract to a string variable the name of each record whose type is not group. Put a & Return at the end of each line. After the repeat, put that string onto the clipboard. Paste it into Excel or Numbers. Sort, parse, and do your evaluation of missing numbers there.

Building a one-time tool in AppleScript to do all the testing and manipulation is not worth the effort, IMO.

-- Find missing numbers

-- This script is a demo, it only works with names like "78.html". To use it it's necessary to additionally extract the desired part of each record name

tell application id "DNtp"
	try
		set theRecords to selected records
		if theRecords = {} then error "Please select some records"
		
		set theNames to name without extension of selected records -- e.g. {"78", "163", "150"}
		set theNames_sorted to my sort_list(theNames)
		
		set theMissingNumbers to {}
		
		if (item 1 of theNames_sorted) as integer ≠ 1 then set theNames_sorted to {0} & theNames_sorted
		
		repeat with i from 1 to ((count theNames_sorted) - 1)
			try
				set startNumber to (item i of theNames_sorted) as integer
				set endNumber to (item (i + 1) of theNames_sorted) as integer
				repeat with j from startNumber + 1 to endNumber - 1
					set end of theMissingNumbers to j
				end repeat
			end try
		end repeat
		
		return theMissingNumbers
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		return
	end try
end tell

on sort_list(theList)
	considering numeric strings
		set theIndexList to {}
		set theSortedList to {}
		repeat (length of theList) times
			set theLowItem to ""
			repeat with a from 1 to (length of theList)
				if a is not in theIndexList then
					set theCurrentItem to item a of theList as text
					if theLowItem is "" then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					else if theCurrentItem comes before theLowItem then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					end if
				end if
			end repeat
			set end of theSortedList to theLowItem
			set end of theIndexList to theLowItemIndex
		end repeat
	end considering
	return theSortedList
end sort_list

1 Like

And?

1 Like

I supposed it works now, showing the progress in @pete31’s AppleScript faculties during the last four years;-) And also that it’s possible to solve any problem in any language, even those that do not have a sort function or a list comparison method…
For the sorting method, I’d suggest MacScripter / QuickSort Algorithm
Should be a bit “quicker” than the merge sort (? it is merge, isn’t it) here.

Personally, I do not even understand the problem. My only files named with numbers are those coming from a camera, and I do not import those in DT. And I couldn’t care less about any “holes” in the list of names.

Because I feel really mean today, the JavaScript version (bar error checking, admittedly)

(()=> {
  let app=Application("DEVONthink 3");
  let nameArray = app.selectedRecords.nameWithoutExtension();
  let namesSorted = nameArray.sort(); // to get "biggest" name 
  let fullArray = [];
  for (i = 1; i <= namesSorted[namesSorted.length -1]; i++) { // Array with all "names" from "1" to the last existing one 
    fullArray.push(""+i); // make sure that elements are strings
  }
  let difference = fullArray.filter(d => !nameArray.includes(d))	
  return difference;
})()

12 LOC vs. 52 (add some more for error handling). Maybe JavaScript does sometimes have some advantages over AppleScript – like C over Basic :wink:

1-77, 79-149 and 151-162 were misssing :wink:

Sorry didn’t meant to reply to your post. Thanks for the suggestion btw. Didn’t try it though, I really don’t like spreadsheet apps.

Actually I forgot to check whether the first item = “1”. Fixed.

Yep, stumbled upon this thread and got curious whether I could do it now.

Looking at the example {“78.html”, “150.html”, “163.html”} it was probably a problem with exporting from Zettelkasten.app. Never exported data from any app before and probably made all mistakes one can make.

Your script gets only the missing numbers from “1” to the first item. Funny as that’s exactly the part that I forgot in my prior version. I’m afraid you have to add some more lines :wink:

This assesment was not correct. However, due to the alphabetical sorting, it missed out on other cases.
So let’s amend that with a proper sort for numerical values:

  let namesSorted = nameArray.sort(function(a,b) {
	  return (parseInt(a) - parseInt(b))
	});

Now I have files "1", "2", "8", "12", and difference is

["3", "4", "5", "6", "7", "9", "10", "11"]

That should be ok, I think. Before the change, it stopped after 7. The script could probably be simplified by using fullArray to calculate the difference: Instead of adding all numbers from 1 to the last one, add only those that are not in namesSorted. 13 LOC, one loop less. Of course a lot less fun if one likes to type much :wink: