How do I specify an element in a list of lists

I’m trying to get a tab in Safari with a specific name…

I want to write something like: get first tab of (tabs of windows) whose name starts with MaxScripter

I use: set matching_tabs to (tabs of windows whose name begins with “MaxScripter”)

which returns:
{{}, {tab 1 of window id 296 of application “Safari”}, {}, {}}

How do I get the “tab 1 of…”? If there are multiple tabs, I just want the first one.

I tried various things like:
first item of litespeed_console_tabs that is not equal to {}
but couldn’t get it working

Thanks!

Sean DeNigris

This works for me:

tell application "Safari"
	try
		set matching_tabs to (first tab of window 1 whose name begins with "MacScripter")
	on error
		display dialog "No tabs found that begin with MacScripter"
	end try
end tell

Notice that I am only addressing window 1, the front most window. I am calling for the first tab. Finally it is wrapped inside a try statement to catch the error if there are no tabs in the window that begin with “MacScripter”. If you use windows instead of window 1 then you get a missing value for the windows that do not have a tab that matches the test:

This method does eliminate the nested list but still returns unwanted items, so addressing the window directly would work best.

Thanks for the idea, but I specifically want a list of all the open tabs in Safari, not just the ones in the first window :frowning:

Sean

something like this


tell application "Safari"
	tell (windows whose visible is true) to set matching_tabs to (tabs whose name begins with "MacScripter")
end tell

Thanks!

But that got the same result as my first post :frowning:

Getting all the tabs that match is not the problem. The problem is that they are returned as a list of lists. Once I have “{{tab 1 of window id 6241 of application “Safari”}, {}},” how do I extract a tab from that construct, and can it all be done in one statement?

Sean

I do not think Safari can be made to return the exact list you want “all in one go”. You could build it yourself by looping across the windows and collecting the list of matching tabs, but that is bound to be slower since you have to send a separate query for each window. So I think you will have to do some “post processing”.

In some circles, the function you want is called “flatten” (make nested lists into a single “flat” list).

tell application "Safari"
	tell (windows whose visible is true) to set matching_tabs to (tabs whose name begins with "MacScripter")
end tell

to flatten(|list|)
	-- There are other ways to write "flatten". This is one of the shorter ones (though it may not be the fastest).
	if |list| is missing value then return {} -- a "true" (functional programming) flatten would likely leave this kind of filtering to another function
	if |list| is {} then return {}
	if class of |list| is not list then return {|list|}
	return flatten(first item of |list|) & flatten(rest of |list|)
end flatten

set orig to matching_tabs
set matching_tabs to flatten(matching_tabs)
return {orig:orig, flat:matching_tabs} --> {orig:{{tab 1 of window id 10763 of application "Safari", tab 2 of window id 10763 of application "Safari"}, missing value, missing value, missing value}, flat:{tab 1 of window id 10763 of application "Safari", tab 2 of window id 10763 of application "Safari"}}

(*
 * The code below just makes sure flatten works like we expect.
 * If you make changes to flatten, you should use and extend the tests to make sure there are no regressions in functionality.
 *)

to assert(|result|, expected_result)
	-- This may not work for general AppleScript references since it may not be possible to coerce all classes to strings. But it works OK for the testing done below (though the message can still be misleading since the text version of the list does not show the actual list structure).
	if |result| is not expected_result then error "Expected " & expected_result & "; got " & |result| & "."
end assert

assert(flatten({}), {})
assert(flatten(1), {1})

assert(flatten({1}), {1})
assert(flatten({1, 2}), {1, 2})
assert(flatten({1, 2, 3}), {1, 2, 3})

assert(flatten({{}, 1, 2, 3}), {1, 2, 3})
assert(flatten({1, {}, 2, 3}), {1, 2, 3})
assert(flatten({1, 2, {}, 3}), {1, 2, 3})
assert(flatten({1, 2, 3, {}}), {1, 2, 3})

assert(flatten({missing value, 1, 2, 3}), {1, 2, 3})
assert(flatten({1, missing value, 2, 3}), {1, 2, 3})
assert(flatten({1, 2, missing value, 3}), {1, 2, 3})
assert(flatten({1, 2, 3, missing value}), {1, 2, 3})

assert(flatten({{1}, 2, 3}), {1, 2, 3})
assert(flatten({1, {2}, 3}), {1, 2, 3})
assert(flatten({1, 2, {3}}), {1, 2, 3})

assert(flatten({{{1}}, 2, 3}), {1, 2, 3})
assert(flatten({1, {{2}}, 3}), {1, 2, 3})
assert(flatten({1, 2, {{3}}}), {1, 2, 3})

assert(flatten({{{1}, 2}, 3}), {1, 2, 3})
assert(flatten({{1, {2}}, 3}), {1, 2, 3})
assert(flatten({{1, 2}, {3}}), {1, 2, 3})

assert(flatten({{1}, {2, 3}}), {1, 2, 3})
assert(flatten({1, {{2}, 3}}), {1, 2, 3})
assert(flatten({1, {2, {3}}}), {1, 2, 3})

assert(flatten({{1, {}}, {}, {2, {}, 3}}), {1, 2, 3})
assert(flatten({1, {}, {{2, {}}, 3, {}}}), {1, 2, 3})
assert(flatten({1, {}, {2, {}, {3, {}}}}), {1, 2, 3})

assert(flatten({{1, missing value}, missing value, {2, missing value, 3}}), {1, 2, 3})
assert(flatten({1, missing value, {{2, missing value}, 3, missing value}}), {1, 2, 3})
assert(flatten({1, missing value, {2, missing value, {3, missing value}}}), {1, 2, 3})

Model: iBook G4 933
AppleScript: 1.10.7
Browser: Safari Version 4 Public Beta (4528.16)
Operating System: Mac OS X (10.4)

As you’ve probably gathered, the list of lists results from the fact that the reference is to multiple elements of multiple elements. (Tabs of windows of Safari.) I don’t have a scriptable-tab version of Safari to test, but you probably can’t get the flat result you want with a single statement. However, the “window” sublists are easily concatenated together:

tell application "Safari"
	tell (windows whose visible is true) to set matching_tabs to (tabs whose name begins with "MacScripter")
end tell

set flattenedList to beginning of matching_tabs
repeat with thisList in rest of matching_tabs
	set flattenedList to flattenedList & thisList
end repeat
set matching_tabs to flattenedList

To be very literal about answering your original question, the way you would reference that item in the original list of lists you posted would be:

set theList to {{}, {tab 1 of window id 296 of application "Safari"}, {}, {}}
set theFinalItem to (item 1 of item 2 of theList)