How to effectively converting a list of list into a single list

Hello.

Very often we extract information from a datamodel provided for som application. if the items “hangs” onto another item, like say tabs are a part of windows in Safari, then we’ll end up with a list of lists.

Very often we’d like to have a single list, containing all the items, say if we were to find a specific tab in Safari.

Then we can set the ApplesScript text item delimiters to linefeed, convert the list to text, and then get all the paragraphs from that list, effectively converting a list of lists to a single list.


tell application "Safari"
	set AppleScript's text item delimiters to ""
	set b to count (its every window whose visible is true)
	set i to 1
	set c to {}
	repeat b times
		set end of c to name of tabs of its window i
		set i to i + 1
	end repeat
end tell
log c
return # look at the list in the event pane now! then remove the line and commence!
set AppleScript's text item delimiters to linefeed
# creating a single list that don't give you one level of indirection
set d to every item of c as text
set c to every paragraph of d
log c
set AppleScript's text item delimiters to ""
return # look at the list now, a single list!

Or:

tell application "Safari" to set c to name of tabs of (windows whose visible is true)
set AppleScript's text item delimiters to ""

:slight_smile:

The point was more about how to get stuff into a single list than scripting Safari. But I think the point is here, that you would really try things like that first to avoid creating a nested list in the first place! :slight_smile:

I was thinking more of this, the philosophy behind it all, is to have techniques for creating the necessary datastructures for a script fast. IMHO, I think that a user shouldn’t wait much more than a second or two for things to happen. So the idea is to get data as fast as possible into a format that you can present to a user, in this case, say a choose from list dialog with all open tabs in Safari.

Then you would later have to be able to identify the window. you have chosen from.

Instead of building a datastructure up front, you could just count the tabs of each window, then you could make a list of cumulative sums, before you used a specially tinkered binary search to identify this window. And then just do a little bit afterwards, making the user feel that your script is both responsive and fast.

The code here is still just illustrative, but the tinkered binarysearch handler works!


tell application "Safari"
	set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ""}
	set b to count (its every window whose visible is true)
	set tabCounts to {}
	set i to 1
	repeat b times
		set tabc to count tabs of its window i
		if tabc > 0 then set end of tabCounts to tabc
		set i to i + 1
	end repeat
end tell
# making cumulative sums, this code is still just illustrative
set i to 2
set cumSum to {item 1 of tabCounts}
repeat (b - 1) times
	set end of cumSum to (item (i - 1) of cumSum) + (item i of tabCounts)
	set i to i + 1
end repeat

# Now, say that you have presented the user with a choose from list, and the user
# selected tab nr 95 in that list.
set winNr to binarySearchinCumSum(SafariTabsList, 95)
--> 28

on binarySearchinCumSum(theList, value)
	-- NG http://macscripter.net/viewtopic.php?id=17340
	-- Modified to work with cumulative sums of integers in a list
	--returns the item (or bucket) into which a number belongs
	-- if theList consists of 5,10,15,20 and the handler is given
	-- 19 as value, it will return nr 4. 1 gives 1, 20 gives 4.
	script o
		property lst : theList
	end script
	
	set l to 2
	set R to (count theList)
	if value > item R of o's lst or value < item 1 of o's lst then
		error "the list is either unsorted, or the value is out of bounds"
	else if value ≤ item 1 of o's lst then
		return 1
	else if value > item (R - 1) of o's lst and value ≤ item R of o's lst then
		return R
	else
		set R to R - 1
		set m to (l + R) div 2
		repeat until (value ≤ item m of o's lst and value > item (m - 1) of o's lst)
			
			if value > item m of o's lst then
				set l to m + 1
			else
				set R to m - 1
			end if
			set m to (l + R) div 2
		end repeat
		return m as integer
	end if
end binarySearchinCumSum

The code above isn’t meant to work, then you’d have to add some stuff.

It is just meant to illustrate construction principles to make scripts that works towards application’s datastructures more efficient and responsive. In avoiding having to build full relations between objects, to identify a container for items.

Yes of course. That’s why I didn’t interfere with your actual list-flattening code. :slight_smile:

As always when coercing lists to text and back, the caveat is that the items in the lists should be known to be text already or should be acceptable in coerced form at the end of the process.

Yes. I should have stated just that, that those ways are of course just good for data, that can be interpreted as text, or eventually be coerced back to their original datatype easily, after such a “flattening”.

The context here, is getting textual info out of application, that say a user can choose from a list, or search in, with a flattened list, so the usage of this is basically for lists that contain text, not records or anything like it!

The problem is, that were sought to mend, was that the info we often need is spread in several containers, and we often have to find the container to do something useful. ( A safari Tab resides in a window, so we need both the tab and the window, should we want to activate a user selected tab for instance.)

In the case with Safari, we’d have to either get the window id, or document id, to know which window we’d activate a tab, but not more, as I believe I could use the name of the tab, to activate the tab with!

With the cumulative summing of items per container, for only those containers that gives sense, and the collection of id’s into a list, we can quickly identify the container (document/window) by the item the user has chosen.

We do this by binarysearching for the container afterwards, using the index nr of the selected tab from the choose from list dialog. (the text would still be handy). When we have the container number, then we’d either return the correct window or document id from a list we can make on the fly. or just return directly, depending how cleverly we have filtered out the downloads window and such.

It may surely be quicker to making up all of those relations up front, if you look at the time it takes per item, but not for the user, who wonders if something is happening or working, and besideds that, by doing such, we don’t do unnecessary operations. We only make the relations we need, when we need them. The user will the experience a much more responsive script, even if the total time spent is greater.

Those where just some ideas when I sat pondering “upgrading” a script for searching in Safari tabs. :slight_smile:

I had some bugs I made in the binarySearchCumSum, those are fixed, and the post above updated

Hello.

This is what I meant. by all the writings above. Here is a script that lets you search for tabs in Safari, it also searches through the url’s. Try entering .* as the searchpattern, and you should get a list of all the tabs.

The idea was to make something that was experienced as fast, but I think the script is fast!
:slight_smile:


script searchSafariTabs
	# © 2012 McUsr and put in public domain you may not post this elsewhere, nor put it in a public repository.
	property safariIcon : (a reference to file ((path to applications folder as text) & "Safari.app:Contents:Resources:compass.icns"))
	property scriptTitle : "Search Safari Tabs"
	property cachespath : ((path to library folder from user domain as text) & "caches:" & "net.mcusr.safari.search.tabs")
	
	local script_cache
	try
		set script_cache to load script alias cachespath
	on error
		script newScriptCache
			property searchWord : ""
		end script
		set script_cache to newScriptCache
	end try
	
	local searchWord, tabNames, tabUrls, tabNamesText, tabUrlsText
	set searchWord to script_cache's searchWord
	
	set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ""}
	tell application "Safari" to tell tabs of (windows whose visible is true)
		set tabNames to name of it
		set tabUrls to URL of it
	end tell
	
	# If the user didn't cancel the searchterm, then we'd rather have something entered.
	# we beep if it is empty
	set AppleScript's text item delimiters to linefeed
	# creating a single list that don't give you one level of indirection
	set {tabNamesText, tabUrlsText} to {every item of tabNames as text, every item of tabUrls as text}
	# we remove any "missing tabs" due to the Download window!
	set tabNamesText to do shell script "sed '/^$/d' <<<" & quoted form of tabNamesText without altering line endings
	set tabUrlsText to do shell script "sed '/^$/d' <<<" & quoted form of tabUrlsText without altering line endings
	set {tabNames, tabUrls} to {paragraphs 1 thru -2 of tabNamesText, paragraphs 1 thru -2 of tabUrlsText}
	set AppleScript's text item delimiters to ""
	
	set failed to false
	
	tell application "SystemUIServer"
		activate
		try
			set searchWord to text returned of (display dialog "Enter a search term, Miniminum one letter. Wildcards where you like them!" with title my scriptTitle default answer searchWord with icon safariIcon)
		on error
			set failed to true
		end try
	end tell
	if failed = true or searchWord = "" then return 0
	set searchWord to quoted form of searchWord
	# let the search for tabs begin, we return the linenumbers from the tabnames and taburls lists.
	set namesres to do shell script "echo  " & quoted form of tabNamesText & " |grep -in " & searchWord & " |sed -n 's/^\\([0-9][0-9]*\\)\\(.*\\)/\\1/p'" without altering line endings
	set urlsRes to do shell script "echo  " & quoted form of tabUrlsText & " |grep -in " & searchWord & " |sed -n 's/^\\([0-9][0-9]*\\)\\(.*\\)/\\1/p'" without altering line endings
	
	# we may not get a match both for urls and names of tabs!
	if namesres = "" and urlsRes = "" then
		tell application "Safari"
			try
				display dialog "I didn't find any match" with title my scriptTitle with icon safariIcon buttons {"Ok"} default button 1
			end try
		end tell
		
		set AppleScript's text item delimiters to tids
		return 0
	else if namesres = "" then
		set namesres to urlsRes
	else if urlsRes = "" then
		# do nothing, we got the list we got
	else
		set namesres to namesres & urlsRes
		# we concatenate to make a list we sort to just get unique line numbers
	end if
	local chosenTab, tabIndexes
	set tabIndexes to paragraphs 2 thru -1 of (do shell script "sort -un <<<" & quoted form of namesres)
	set AppleScript's text item delimiters to "p;"
	set tabIndexes to quoted form of ((tabIndexes as text) & "p")
	set AppleScript's text item delimiters to ""
	set displaylist to paragraphs of (do shell script "sed -n " & tabIndexes & "<<<" & quoted form of tabNamesText)
	if item -1 of displaylist = "" then set displaylist to items 1 thru -2 of displaylist
	tell application "SystemUIServer"
		
		set chosenTab to (choose from list displaylist default items item 1 of displaylist with prompt "Select the Safari Tab to activate!" with title scriptTitle) as text
	end tell
	if chosenTab = "false" then
		set AppleScript's text item delimiters to tids
		# Store the searchPattern!
		set script_cache's searchWord to text 2 thru -2 of searchWord
		store script script_cache in cachespath replacing yes
		return 0
	end if
	# if it isn't false its time to do the stuff we have postponed !
	local windowNumbers, tabcounts
	
	set {tabcounts, windowNumbers} to {{}, {}}
	tell application "Safari"
		local tabc, winCount, winDiff, i
		set {i, winCount, winDiff} to {1, count (its every window whose visible is true), 0}
		repeat winCount times
			tell its window i
				try
					set tabc to count of its tabs
					if tabc > 0 then
						set end of tabcounts to tabc
						set end of windowNumbers to i
					else
						set winDiff to winDiff + 1
						set end of rejects to i
					end if
					
				on error
					# download window
					set winDiff to winDiff + 1
				end try
			end tell
			set i to i + 1
		end repeat
	end tell
	set winCount to winCount - winDiff
	# we build a list of cumulative sums to be able to identify what window the tab sought for is in
	
	# making cumulative sums, this code is still just illustrative
	local i, cumsum
	set {i, cumsum} to {2, {}}
	set end of cumsum to item 1 of tabcounts
	repeat (winCount - 1) times
		set end of cumsum to (item (i - 1) of cumsum) + (item i of tabcounts)
		set i to i + 1
	end repeat
	
	
	# Now we are prepared, now we need the index of the item!
	set tabIndex to indexOfItem(chosenTab, tabNames)
	
	set winNr to binarySearchinCumSum(cumsum, tabIndex)
	# some adjustments here. We add every window with index below or equal to ours.
	# that has been tossed.
	
	set winNr to item winNr of windowNumbers
	# the "real" window number amongst those we don't use!
	
	tell application "Safari"
		#  activate
		set winID to id of its window winNr
		set index of its window 1 to 2
		set index of its window id winID to 1
		tell its window id winID
			if name of current tab of it is not chosenTab then
				tell tab chosenTab of it to do JavaScript "self.focus()"
			else
				tell current tab of it to do JavaScript "self.focus()"
			end if
		end tell
	end tell
	set AppleScript's text item delimiters to tids
	set script_cache's searchWord to text 2 thru -2 of searchWord
	store script script_cache in cachespath replacing yes
	do shell script "open -b \"com.apple.safari\""
	return 0
	
	on indexOfItem(theItem, theList) -- credit to Emmanuel Levy but I modified it with the considering case statements
		set text item delimiters to return
		set theList to return & theList & return
		set text item delimiters to {""}
		try
			-1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
		on error
			0
		end try
	end indexOfItem
	
	on binarySearchinCumSum(theList, value)
		-- NG http://macscripter.net/viewtopic.php?id=17340
		-- Modified to work with cumulative sums of integers in a list
		--returns the item (or bucket) into which a number belongs
		-- if a list consists of 5,10,15,20 and the handler is given
		-- 19 as value, it will return nr 4.
		script o
			property lst : theList
		end script
		
		set l to 2
		set R to (count theList)
		if value > item R of o's lst or value < 1 then
			error "the list is either unsorted, or the value is out of bounds"
		else if value ≤ item 1 of o's lst then
			return 1
		else if value > item (R - 1) of o's lst and value ≤ item R of o's lst then
			return R
		else
			set R to R - 1
			set m to (l + R) div 2
			repeat until (value ≤ item m of o's lst and value > item (m - 1) of o's lst)
				
				if value > item m of o's lst then
					set l to m + 1
				else
					set R to m - 1
				end if
				set m to (l + R) div 2
			end repeat
			return m as integer
		end if
	end binarySearchinCumSum
end script
tell searchSafariTabs to run
return 0 # to save the props

The script in post #7 :

  • Is corrected with regards to getting the right window by index, the new scheme should always work.

  • Has gotten an externally saved property for remembering the last search.

  • Has had its “ui” adjusted so it is just the window you want that comes to the foreground, when found.

I have weeded out some more bugs concerning addressing the correct window, and updated the script in post #7.

:lol:

It didn’t at that point, it started out as an idea of doing stuff that involves UI differently:

Do as little as you have before you have something showing up in the user’s face.

Do whatever it takes, as fast as possible in the aftermath of a user choice.

I commenced with the script, as it seemed like a good idea. the script in post #7 should work fairly well, but I’ll update it just a little more with some polishing, and making a choice for Showing all tabs sorted, and sort any result sets from the queries.

Edit
It seems to be one more bug (at least when invoked from Quicksilver), in there, that I haven’t been able to pin it down just yet. Its kind of unfathomable, as the indexes of the windows should be perfectly related to the tabs.

I think it has something to do with activating window and the tab in Safari. It comes to life sometimes, but disappears in Script Debugger, (or so it seems).

That particular bug may disappear during “polishing”, thinking activating Safari briefly may solve it. I have changed the copy statement of a new script in post #7, into a set statement, to see if not saving that context helps.

Hello.

SearchSafariTabs is now very much overhauled, and polished. There was one bug, that would appear if you searched for a window that resided in another space. I didn’t notice it before I saw it in the Debugger.

The script, should be as user friendly as it can get, and almost as fast as well.

It lets you display all your tabs in one go, so you can see the index of your open tabs sort of.

It only saves a new search word, when it is logical, that is not when you have searched for all tabs.

It takes measures to display the tab you selected, and tells you if it couldn’t, (that you have to change space).

I can’t for obvious reasons edit post #7, so here it is, and enjoy! :slight_smile:



-- Release 2.2.1
-- http://macscripter.net/viewtopic.php?pid=158082#p158082
property parent : AppleScript
property tlvl : me
script genUI
	-- © McUsr 2012 and put in public domain, you may not post this as a stand-alone thing 
	-- anywhere else, nor put into a public repository. 
	-- Please refer to this link: http://macscripter.net/edit.php?id=157665
	on posFrontWinForAppAndDisplay(appname, x1, y1, w, h)
		-- Width is the actual width, Height the actual height for how apps treats those parameters differs slightly.
		-- you'll have to figure out the formulaes outside the handler.
		-- most often the formula for the width is width - x1, and  height respective is height - y1. 
		local success
		set success to my getFrontWinForAppAndDisplay(appname)
		if success then
			tell application id "sevs"
				tell application process appname
					try
						set position of its front window to {x1, y1}
						set size of its front window to {w, h}
					end try
				end tell
			end tell
		end if
	end posFrontWinForAppAndDisplay
	
	on getFrontWinForAppAndDisplay(appname)
		-- gets the front window to front for an app
		-- returns true if it manages, false otherwise
		local bid, success, failure, pass, curAppName, curBid
		set {success, failure, pass} to {false, false, 1}
		repeat while success = false
			tell application id "sevs"
				if pass = 1 then
					set curAppName to name of first application process whose frontmost is true
					set curBid to bundle identifier of application process curAppName
					if {appname} is not in name of application processes then
						set failure to true # it wasn't running so we're exiting
					else
						if {appname} is not in name of (application processes whose visible is true) then
							set visible of application process appname to true
							set frontmost of application process curAppName to true
						end if
					end if
				end if
				
				if not failure then tell application process appname
					local wnCount
					set wnCount to count its windows
					if wnCount = 0 then
						# no regular windows open, there may be, however, in other spaces.
						if pass = 2 then set failure to true
						# there are no open windows in any space and we fail.
					else
						set bid to bundle identifier of it
						try
							local dummy
							set dummy to position of its front window
							# if this doesn't fail, then we do have a window in the current space
							set success to true
						end try
					end if
				end tell
				
			end tell
			
			if failure then
				do shell script "open -b \"" & curBid & "\""
				return false
			else if not success then
				do shell script "open -a \"" & appname & "\""
				# some apps open an empty window if there was none.
				set pass to pass + 1
			end if
		end repeat
		do shell script "open -b \"" & bid & "\""
		# no use in this if we have made the app visible!
		# then we activate the previous app, and the bring the window forward.
		return true
	end getFrontWinForAppAndDisplay
	
end script

script searchSafariTabs
	# © 2012 McUsr and put in public domain you may not post this elsewhere, nor put it in a public repository.
	property safariIcon : (a reference to file ((path to applications folder as text) & "Safari.app:Contents:Resources:compass.icns"))
	property scripttitle : "Search Safari Tabs"
	property cachespath : ((path to library folder from user domain as text) & "caches:" & "net.mcusr.safari.search.tabs")
	
	local script_cache
	try
		set script_cache to load script alias cachespath
	on error
		script newScriptCache
			property searchWord : ""
		end script
		
		set script_cache to newScriptCache
	end try
	
	local searchWord, tabNames, tabUrls, tabNamesText, tabUrlsText
	set searchWord to script_cache's searchWord
	
	set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ""}
	tell application "Safari" to tell tabs of (windows whose visible is true)
		set tabNames to name of it
		set tabUrls to URL of it
	end tell
	local prevBid
	tell application id "sevs"
		local prevAppName
		set prevAppName to name of first application process whose frontmost = true
		set prevBid to bundle identifier of application process prevAppName
	end tell
	
	if tabNames = {} then
		with timeout of 300 seconds
			tell application "SystemUIServer"
				activate
				try
					display dialog "There aren't any tabs open!" with title my scripttitle with icon safariIcon buttons {"Ok"} default button 1
				end try
			end tell
		end timeout
		set AppleScript's text item delimiters to tids
		do shell script "open -b \"" & prevBid & "\""
		return 0
	end if
	
	local tabNamesList # Bugfix:
	
	# If the user didn't cancel the searchterm, then we'd rather have something entered.
	# we beep if it is empty
	set AppleScript's text item delimiters to linefeed
	# creating a single list that don't give you one level of indirection
	set {tabNamesText, tabUrlsText} to {every item of tabNames as text, every item of tabUrls as text}
	# we remove any "missing tabs" due to the Download window!
	set tabNamesText to do shell script "sed '/^$/d' <<<" & quoted form of tabNamesText without altering line endings
	set tabUrlsText to do shell script "sed '/^$/d' <<<" & quoted form of tabUrlsText without altering line endings
	set {tabNamesList, tabUrls} to {paragraphs 1 thru -2 of tabNamesText, missing value}
	set AppleScript's text item delimiters to ""
	
	set failed to false
	with timeout of 300 seconds
		tell application "SystemUIServer"
			activate
			try
				set dlgResult to (display dialog "Enter a search term, Miniminum one letter. Wildcards where you like them!" with title my scripttitle default answer searchWord giving up after 300 with icon safariIcon buttons {"Cancel", "All", "Ok"} cancel button 1 default button 3)
			on error
				set failed to true
			end try
		end tell
	end timeout
	if failed = true then
		set AppleScript's text item delimiters to tids
		do shell script "open -b \"" & prevBid & "\""
		return 0
	end if
	local allTabs
	set allTabs to false
	if button returned of dlgResult = "All" then
		set searchWord to ".*"
		set allTabs to true
	else
		set searchWord to text returned of dlgResult
		if searchWord = "" then
			set searchWord to ".*"
			set allTabs to true
		else if searchWord = ".*" then
			set allTabs to true
		end if
	end if
	
	set searchWord to quoted form of searchWord
	# let the search for tabs begin, we return the linenumbers from the tabnames and taburls lists.
	set namesres to do shell script "echo  " & quoted form of tabNamesText & " |grep -in " & searchWord & " |sed -n 's/^\\([0-9][0-9]*\\)\\(.*\\)/\\1/p'" without altering line endings
	if not allTabs then
		set urlsRes to do shell script "echo  " & quoted form of tabUrlsText & " |grep -in " & searchWord & " |sed -n 's/^\\([0-9][0-9]*\\)\\(.*\\)/\\1/p'" without altering line endings
		# we may not get a match both for urls and names of tabs!
		if namesres = "" and urlsRes = "" then
			with timeout of 300 seconds
				tell application "SystemUIServer"
					activate
					try
						display dialog "I didn't find any match" with title my scripttitle with icon safariIcon buttons {"Ok"} default button 1
					end try
				end tell
			end timeout
			set AppleScript's text item delimiters to tids
			do shell script "open -b \"" & prevBid & "\""
			return 0
		else if namesres = "" then
			set namesres to urlsRes
		else if urlsRes = "" then
			# do nothing, we got the list we got
		else
			set namesres to namesres & urlsRes
			# we concatenate to make a list we sort to just get unique line numbers
		end if
		
		local chosenTab, tabIndexes
		set tabIndexes to paragraphs 2 thru -1 of (do shell script "sort -un <<<" & quoted form of namesres)
		-- unique numeric sort, to avoid redundant tabs
		set AppleScript's text item delimiters to "p;"
		set tabIndexes to quoted form of ((tabIndexes as text) & "p")
		set AppleScript's text item delimiters to ""
		set displaylist to paragraphs of (do shell script "sed -n " & tabIndexes & "<<<" & quoted form of tabNamesText)
	else
		-- we are going to show all tabs, and we do create a sorted list of them
		set displaylist to paragraphs of (do shell script "export LC_ALL='C' ; sort -fud <<<" & quoted form of tabNamesText & "|sed 1d")
	end if
	
	if item -1 of displaylist = "" then set displaylist to items 1 thru -2 of displaylist
	tell application "SystemUIServer"
		
		set chosenTab to (choose from list displaylist default items item 1 of displaylist with prompt "Select the Safari Tab to activate!" with title scripttitle) as text
	end tell
	if chosenTab = "false" then
		set AppleScript's text item delimiters to tids
		# Store the searchPattern!
		if not allTabs then
			set script_cache's searchWord to text 2 thru -2 of searchWord
			store script script_cache in cachespath replacing yes
		end if
		do shell script "open -b \"" & prevBid & "\""
		return 0
	end if
	# if it isn't false its time to do the stuff we have postponed !
	# We count the tabs of the different windows, and makes cumulative sums
	local windowNumbers, cumSum
	set {windowNumbers, cumSum} to {{}, {}}
	tell application "Safari"
		local tabc, tabCounts, winCount, i
		set {i, winCount, tabCounts} to {1, count its every window, {}}
		repeat winCount times
			tell its window i
				try
					set tabc to count of its tabs
					try
						# add up the cumulative sums
						set end of cumSum to (end of cumSum) + tabc
					on error
						set end of cumSum to tabc
						# It was the initial element
					end try
					set end of tabCounts to tabc
					set end of windowNumbers to i
				end try
				# download window errors
			end tell
			set i to i + 1
		end repeat
	end tell
	
	# Now we are prepared, now we need the index of the item!
	set tabIndex to indexOfItem(chosenTab, tabNamesList)
	
	set winNr to binarySearchinCumSum(cumSum, tabIndex)
	# some adjustments here. We add every window with index below or equal to ours.
	# that has been tossed. 
	
	set winNr to item winNr of windowNumbers
	# the real window number amongst those we don't use!
	
	tell application "Safari"
		set winOfTabID to id of its window winNr
		tell its window id winOfTabID
			if name of current tab of it is not chosenTab then
				set wlist to name of every tab
				set tabnr to my indexOfItem(chosenTab, wlist)
				set current tab to tab tabnr
			end if
			set winOfTabIdDoc to its document
		end tell
		set frontWindoc to document of its front window
		if winOfTabIdDoc is not frontWindoc then
			make new document
			set newDocId to id of front window
			set index of window id winOfTabID to 1
			close document of window id newDocId
		end if
		
	end tell
	set AppleScript's text item delimiters to tids
	if not allTabs then
		set script_cache's searchWord to text 2 thru -2 of searchWord
		store script script_cache in cachespath replacing yes
	end if
	-- Aftermath: it may be that we are having Safari windows in the current space
	-- and that the window resides in another space, we'll then be left in the current space.
	-- We check for this situation, and gives proper information if it is so.
	local theNm
	tell application "Safari" to set theNm to name of its window 1
	
	if theNm is not chosenTab then
		with timeout of 300 seconds
			tell application "Safari"
				try
					display dialog "The Safari Tab you searched for resides in another space!" with title my scripttitle with icon safariIcon buttons {"Ok"} default button 1 giving up after 300
				end try
			end tell
		end timeout
	end if
	tlvl's genUI's getFrontWinForAppAndDisplay("Safari")
	return 0
	
	on indexOfItem(theItem, theList) -- credit to Emmanuel Levy but I modified it with the considering case statements
		set text item delimiters to return
		set theList to return & theList & return
		set text item delimiters to {""}
		try
			-1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
		on error
			0
		end try
	end indexOfItem
	
	on binarySearchinCumSum(theList, value)
		-- NG http://macscripter.net/viewtopic.php?id=17340
		-- Modified to work with cumulative sums of integers in a list 
		--returns the item (or bucket) into which a number belongs
		-- if a list consists of 5,10,15,20 and the handler is given 
		-- 19 as value, it will return nr 4.
		script o
			property lst : theList
		end script
		
		set l to 2
		set R to (count theList)
		if value > item R of o's lst or value < 1 then
			error "the list is either unsorted, or the value is out of bounds"
		else if value ≤ item 1 of o's lst then
			return 1
		else if value > item (R - 1) of o's lst and value ≤ item R of o's lst then
			return R
		else
			set R to R - 1
			set m to (l + R) div 2
			repeat until (value ≤ item m of o's lst and value > item (m - 1) of o's lst)
				
				if value > item m of o's lst then
					set l to m + 1
				else
					set R to m - 1
				end if
				set m to (l + R) div 2
			end repeat
			return m as integer
		end if
	end binarySearchinCumSum
end script
tell searchSafariTabs to run
return 0 # to save the props 

Hello.

I have updated the script above in post # 12, so it should work from Lion to at least Maverics. I stole some snippets from Chris Stone’s “Choose Safari Tabs” script. (Problem was that the do javascript “self.Focus” way of selecting a tab ceased to work for me.)

Enjoy! :slight_smile:

Hello.

I have just removed 3 lines of code that didn’t do anything except make the script longer from the script in post 12.

There are still a bug in it, that only kick in sometimes, like 1 of 50 times I use the script. Then the script will say that the tab you sought must be in another space. Thing is, the activation of that tab then fails, then the front window becomes a different one, so the script thinks it must reside in another space.

This bug is hard to reproduce, but I will get back on the issue.

I know this is old but could someone fix the text encoding issues of this script. This script was created and posted on StackOverflow by McUsr.

Done. In all three scripts (posts #s 3, 7, & 12). Fortunately, I still had a plain text version of this topic from before MacScripter’s text encoding changed. I hadn’t saved the 2015 edits, though. Luckily I noticed them just as I was about to restore the post #12 script, so it is still the 2015 version!

NOTE: The technique described in this post is now in its own Code Exchange listing here.

The original impetus behind this old post was to present a way to flatten a list of nested sublists into a single unnested list. The text item delimiters technique described may be used in the specialized case where the nested list items are text strings. This technique had also been described by Adam Bell. Nigel Garvey described a nice solution involving recursive handler calls that could be extended to lists containing nested items of any arbitrary class. A potential problem with the latter approach is the possibility of a stack overflow error with very deeply nested lists involving many recursive handler calls.

The following approach is an alternative solution for flattening a list of nested items of any arbitrary class that avoids the stack overflow possibility with the recursive-handler-call technique. The handler executes efficiently, aided by processing list items as property values in the repeat loop. The handler works by flattening only top-level sublists in each repeat loop cycle via list concatenation (set util’s tmpList to util’s tmpList & it) until there are no remaining sublists (repeat while util’s flattenedList’s lists’s length > 0).

Handler:


on flattenList(theList)
	script util
		property flattenedList : theList
		property tmpList : missing value
	end script
	repeat while util's flattenedList's lists's length > 0
		set util's tmpList to {}
		repeat with currItem in util's flattenedList
			tell currItem's contents
				if its class = list then
					set util's tmpList to util's tmpList & it
				else
					set end of util's tmpList to it
				end if
			end tell
		end repeat
		set util's flattenedList to util's tmpList
	end repeat
	return util's flattenedList as list -- "as list" assures that the return value is dereferenced
end flattenList

Examples:


set theList to {1, 2, {{3, {4, 5}, 6}, 7}, 8}

flattenList(theList)
--> {1, 2, 3, 4, 5, 6, 7, 8}

set deeplyNestedList to {1, {2, {3, {4, {5, {6, {7, {8, {9, {10, {11, {12, {13, {14, {15, {16, {17, {18, {19, {20, {21, {22, {23, {24, {25, {26, {27, {28, {29, {30, {31, {32, {33, {34, {35, {36, {37, {38, {39, {40, {41, {42, {43, {44, {45, {46, {47, {48, {49, {50, {51, {52, {53, {54, {55, {56, {57, {58, {59, {60, {61, {62, {63, {64, {65, {66, {67, {68, {69, {70, {71, {72, {73, {74, {75, {76, {77, {78, {79, {80, {81, {82, {83, {84, {85, {86, {87, {88, {89, {90, {91, {92, {93, {94, {95, {96, {97, {98, {99, {100, {101, {102, {103, {104, {105, {106, {107, {108, {109, {110, {111, {112, {113, {114, {115, {116, {117, {118, {119, {120, {121, {122, {123, {124, {125, {126, {127, {128, {129, {130, {131, {132, {133, {134, {135, {136, {137, {138, {139, {140, {141, {142, {143, {144, {145, {146, {147, {148, {149, {150, {151, {152, {153, {154, {155, {156, {157, {158, {159, {160, {161, {162, {163, {164, {165, {166, {167, {168, {169, {170, {171, {172, {173, {174, {175, {176, {177, {178, {179, {180, {181, {182, {183, {184, {185, {186, {187, {188, {189, {190, {191, {192, {193, {194, {195, {196, {197, {198, {199, {200, {201, {202, {203, {204, {205, {206, {207, {208, {209, {210, {211, {212, {213, {214, {215, {216, {217, {218, {219, {220, {221, {222, {223, {224, {225, {226, {227, {228, {229, {230, {231, {232, {233, {234, {235, {236, {237, {238, {239, {240, {241, {242, {243, {244, {245, {246, {247, {248, {249, {250, {251, {252, {253, {254, {255, {256}, 257}}, 258}}, 259}}, 260}}, 261}}, 262}}, 263}}, 264}}, 265}}, 266}}, 267}}, 268}}, 269}}, 270}}, 271}}, 272}}, 273}}, 274}}, 275}}, 276}}, 277}}, 278}}, 279}}, 280}}, 281}}, 282}}, 283}}, 284}}, 285}}, 286}}, 287}}, 288}}, 289}}, 290}}, 291}}, 292}}, 293}}, 294}}, 295}}, 296}}, 297}}, 298}}, 299}}, 300}}, 301}}, 302}}, 303}}, 304}}, 305}}, 306}}, 307}}, 308}}, 309}}, 310}}, 311}}, 312}}, 313}}, 314}}, 315}}, 316}}, 317}}, 318}}, 319}}, 320}}, 321}}, 322}}, 323}}, 324}}, 325}}, 326}}, 327}}, 328}}, 329}}, 330}}, 331}}, 332}}, 333}}, 334}}, 335}}, 336}}, 337}}, 338}}, 339}}, 340}}, 341}}, 342}}, 343}}, 344}}, 345}}, 346}}, 347}}, 348}}, 349}}, 350}}, 351}}, 352}}, 353}}, 354}}, 355}}, 356}}, 357}}, 358}}, 359}}, 360}}, 361}}, 362}}, 363}}, 364}}, 365}}, 366}}, 367}}, 368}}, 369}}, 370}}, 371}}, 372}}, 373}}, 374}}, 375}}, 376}}, 377}}, 378}}, 379}}, 380}}, 381}}, 382}}, 383}}, 384}

flattenList(deeplyNestedList)
--> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384}

The second example involving a very deeply nested list caused a stack overflow error with two different recursive-handler-call solutions I tried, but runs without problems with the current handler.

P.S. For completeness, it should be mentioned that an ASObjC solution is also available for flattening lists that involves the application of the unionOfArrays operator to an NSArray. Two requirements are that (1) all items of the top-level list must also be lists, and (2) the sublist nesting must be only one level deep, as shown in this post. Whether this approach could be extended to the general case of list items of any arbitrary class and nesting depth remains to be determined.