Thursday, July 19, 2018
  • Index
  •  » Code Exchange
  •  » How to effectively converting a list of list into a single list

#1 2012-11-07 06:17:09 am

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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.

Applescript:


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!


Mercurial vcs is a joy to use for scripting.


Filed under: Transposition

Offline

 

#2 2012-11-07 06:46:31 am

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

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

McUsr wrote:

Applescript:


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


Or:

Applescript:

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

smile


NG

Offline

 

#3 2012-11-07 07:33:00 am

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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! 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!

Applescript:


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 [url]http://macscripter.net/viewtopic.php?id=17340[/url]
   -- 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

Last edited by McUsr (2012-11-07 09:52:11 am)


Mercurial vcs is a joy to use for scripting.


Filed under: Transposition

Offline

 

#4 2012-11-07 07:48:58 am

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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.


Mercurial vcs is a joy to use for scripting.


Filed under: efficiency

Offline

 

#5 2012-11-07 08:44:40 am

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

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

McUsr wrote:

The point was more about how to get stuff into a single list than scripting Safari.


Yes of course. That's why I didn't interfere with your actual list-flattening code.  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.


NG

Offline

 

#6 2012-11-07 09:24:19 am

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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. smile

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

Last edited by McUsr (2012-11-07 09:48:35 am)


Mercurial vcs is a joy to use for scripting.


Filed under: Transposition.

Offline

 

#7 2012-11-07 02:38:16 pm

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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!
smile

Applescript:


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 [url]http://macscripter.net/viewtopic.php?id=17340[/url]
       -- 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

Last edited by McUsr (2012-11-12 05:59:24 am)


Mercurial vcs is a joy to use for scripting.


Filed under: safari

Offline

 

#8 2012-11-11 01:41:13 pm

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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.

Last edited by McUsr (2012-11-11 01:42:36 pm)


Mercurial vcs is a joy to use for scripting.


Filed under: SafariTabs

Offline

 

#9 2012-11-11 06:24:26 pm

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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

Last edited by McUsr (2012-11-11 06:25:00 pm)


Mercurial vcs is a joy to use for scripting.


Filed under: Search, safari, tabs

Offline

 

#10 2012-11-12 04:53:53 am

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2777
Website

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

McUsr wrote:

The code above isn't meant to work...


lol

Offline

 

#11 2012-11-12 05:01:05 am

McUsr
Member
From:: Southern Norway
Registered: 2010-04-07
Posts: 1776

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

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.

Last edited by McUsr (2012-11-12 05:58:41 am)


Mercurial vcs is a joy to use for scripting.


Filed under: Search, safari, tabs

Offline

 

#12 2012-11-27 08:52:16 pm

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

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

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! smile

Applescript:


-- Release 2.2.1
-- [url]http://macscripter.net/viewtopic.php?pid=158082#p158082[/url]
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: [url]http://macscripter.net/edit.php?id=157665[/url]
   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 ≠ 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 [url]http://macscripter.net/viewtopic.php?id=17340[/url]
       -- 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

Last edited by McUsrII (2015-01-19 06:15:51 am)


Filed under: Search, safari, Tab, tabs

Offline

 

#13 2015-01-19 05:23:06 am

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

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

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! smile

Last edited by McUsrII (2015-01-19 05:23:43 am)

Offline

 

#14 2015-01-19 06:14:02 am

McUsrII
Member
Registered: 2012-11-21
Posts: 3046
Website

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

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.

Offline

 
  • Index
  •  » Code Exchange
  •  » How to effectively converting a list of list into a single list

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)