Get index of element, chozen in choose from list dialog

Hi, all.
I don’t know is the solution something simple, or solution doesn’t exist at all.

I have one big list, which contains some duplicate elements. Then I provide to user choose from list dialog, and user does his choice. I want get index of this chosen by user element in my list to continue process from it.

Simplified example of list and dialog:

set aList to {"item 1", "item 2", "Item 3", "item 2", "item 6", "item 7"}
set aChoise to choose from list aList

So, how can I do that? Any idea?

Using choose from list, you can’t. Using Myriad Tables Lib you would do it like this:

use scripting additions
use script "Myriad Tables Lib" version "1.0.10"

set aList to {"item 1", "item 2", "Item 3", "item 2", "item 6", "item 7"}
set theResult to display table with data aList
return item 1 of rows selected of theResult

Thanks, Shane. I suspected you had something better than choose from list of Standard Additions. :slight_smile: As for the Standard Additions: I consider the lack of this basic functionality a big mistake of developers…

P.S. I tested with “Myriad Tables Lib” version “1.0.9”. Works perfect.

As I see, Standard Additions’s choose from list has other one lake too. The following script instead of choose 2 items “test2”, selects only the 1st “test2”:


set aList to {"test1", "test2", "test3", "test2"}
set anItem to (choose from list aList default items {"test2"}) as string

Up to a point, that’s right, because only a single selection’s allowed unless you specify ‘with multiple selections allowed’. But even then, and with two "test2"s in the default items, only the first is selected.

Try this:

use scripting additions
use script "Myriad Tables Lib" version "1.0.10"

set aList to {"item 1", "item 2", "Item 3", "item 2"}
set theResult to display table with data aList initially selected rows {2, 4} with multiple selections allowed
return rows selected of theResult

Myriad Tables Lib is great, I’m using it for some things.

But if you want a clunky workaround in pure Applescript for some reason, I came across this issue a long time ago (before Myriad Tables) and I solved it this way:


set originalData to {"item 1", "item 2", "Item 3", "item 2", "item 6", "item 7"}
set displayData to {}
set choiceCount to count of originalData
repeat with i from 1 to choiceCount
	set newEntry to "#" & i & ". " & item i of originalData
	set displayData to displayData & newEntry
end repeat
set resultChoice to item 1 of (choose from list displayData)
repeat with i from 1 to choiceCount
	if item i of displayData is resultChoice then set resultIndex to i
end repeat
return resultIndex

Here’s a variation of t.spoon’s workaround if you want the items to be displayed in the dialog window without an entry number prefix. The “trick” is to mark each entry’s index in the dialog window with a prefix string of zero-width space characters equal in number to the entry’s index in the list (and then to return the original, not the prefixed, versions of the chosen items.) All instances of a default item will be pre-selected, unlike choose from list’s current behavior. If multiple selections are allowed, results will be returned as lists; otherwise, results will be returned as non-list values. If an empty selection is allowed and no selection is made, results will be returned as empty lists.


set aList to {"item 1", "item 2", "Item 3", "item 2", "item 6", "", "item 7"}
set defaultItems to {"item 2"}
set multipleSelectionsAllowed to true -- or false
set emptySelectionAllowed to true -- or false

set zeroWidthSpace to character id 8203
tell defaultItems to if its class ≠ list then set defaultItems to {it}
set currPrefix to ""
set {modifiedList, modifiedDefaultItems} to {{}, {}}
repeat with currItem in aList
	set currPrefix to currPrefix & zeroWidthSpace
	tell (currPrefix & currItem)
		set end of modifiedList to it
		if ({currItem's contents} is in defaultItems) and (multipleSelectionsAllowed or (modifiedDefaultItems = {})) then set end of modifiedDefaultItems to it
	end tell
end repeat
set modifiedChosenItems to (choose from list modifiedList default items modifiedDefaultItems multiple selections allowed multipleSelectionsAllowed empty selection allowed emptySelectionAllowed)
if modifiedChosenItems = false then error number -128
set {chosenItems, chosenIndices} to {{}, {}}
repeat with currModifiedChosenItem in modifiedChosenItems
	set i to 0
	repeat with currId in (get currModifiedChosenItem's id)
		if currId's contents ≠ 8203 then exit repeat
		set i to i + 1
	end repeat
	set {end of chosenItems, end of chosenIndices} to {aList's item i, i}
end repeat
if not multipleSelectionsAllowed and (chosenItems's length = 1) then set {chosenItems, chosenIndices} to {chosenItems's item 1, chosenIndices's item 1}

return {chosenItems, chosenIndices}

Thank you, bmose, for your “trick”. It is one very nice solution. Thanks to Shane Stanley and to t.spoon too.

:cool: — as long as none of the original items begin with that character themselves, of course. :wink:

It won’t make a lot of difference, but you could perhaps lose the inner repeat towards the end by using TIDs:

-- (Existing script up to 'if modifiedChosenItems = false then error number -128')

set {chosenItems, chosenIndices} to {{}, {}}
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to (character id 8203)
repeat with currModifiedChosenItem in modifiedChosenItems
	set i to (count currModifiedChosenItem) - (count currModifiedChosenItem's last text item)
	set {end of chosenItems, end of chosenIndices} to {aList's item i, i}
end repeat
set AppleScript's text item delimiters to astid
if not multipleSelectionsAllowed and (chosenItems's length = 1) then set {chosenItems, chosenIndices} to {chosenItems's item 1, chosenIndices's item 1}

return {chosenItems, chosenIndices}

Most certainly!

That’s a nice, compact way of counting the number of leading marker characters. Thanks. (It too depends on the zero-width space character not being present in the original list items.)

I wrote my own, simpler version, using ideas from bmose and t.spoon. I believe, it is little more clear and little better:


set aList to {"test1", "test2", "test3", "test2", "", 5}
set {zeroWidthSpace, modifList, indexesList, chosenItemsList} to {"", {}, {}, {}}

repeat with anItem in aList
	set zeroWidthSpace to zeroWidthSpace & character id 8203
	set end of modifList to zeroWidthSpace & anItem
end repeat

set aChoice to (choose from list modifList default items {modifList's item 2, modifList's item 4} ¬
	with multiple selections allowed and empty selection allowed)
if aChoice is false then return

repeat with i from 1 to count of aChoice
	set {anItem, anIndex} to {item i of aChoice, 1}
	set aLength to count of anItem
	repeat until (anIndex = aLength) or (character (anIndex + 1) of anItem is not (character id 8203))
		set anIndex to anIndex + 1
	end repeat
	set end of indexesList to anIndex
	set end of chosenItemsList to item anIndex of aList
end repeat

return {chosenItemsList, indexesList}

Modified version allowing empty string in the original list.

set aList to {"test1", "test2", "test3", "test2", "", 5}
set {zeroWidthSpace, modifList, indexesList, chosenItemsList} to {"", {}, {}, {}}

repeat with anItem in aList
	set zeroWidthSpace to zeroWidthSpace & character id 8203
	set end of modifList to zeroWidthSpace & character id 8204 & anItem
end repeat

set aChoice to (choose from list modifList default items {modifList's item 2, modifList's item 4} ¬
	with multiple selections allowed)
if aChoice is false then return

repeat with anItem in aChoice
	set anIndex to (offset of character id 8204 in anItem) - 1
	set end of indexesList to anIndex
	set end of chosenItemsList to item anIndex of aList
end repeat

return {chosenItemsList, indexesList}

I removed " and empty selection allowed" because, as far as I know, as there is a default selection we can’t select nothing.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 4 décembre 2019 12:03:18

That use of a second zero-width character makes the counting of the number of leading marker characters even more compact. Nice!

We may enhance it a bit:

set aList to {"test1", "test2", "test3", "test2", "", 5}
set {zeroWidthSpace, modifList, indexesList, chosenItemsList} to {"", {}, {}, {}}

repeat with anItem in aList
	set zeroWidthSpace to zeroWidthSpace & character id 8203
	set end of modifList to zeroWidthSpace & character id 8204 & anItem
end repeat
set tag to character id 8203 & character id 8204
set aChoice to (choose from list modifList default items {modifList's item 2, modifList's item 4} ¬
	with multiple selections allowed)
if aChoice is false then return

repeat with anItem in aChoice
	set anIndex to offset of tag in anItem
	set end of indexesList to anIndex
	set end of chosenItemsList to item anIndex of aList # Corrected according to Nigel Garvey's comment
end repeat

return {chosenItemsList, indexesList}

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 4 décembre 2019 16:37:51

Using two separate zero-width characters not only makes the extraction of list item indices straightforward but also allows a list item to begin with a zero-width space character (rare though that case may be!) and yet still be indexed properly.

Hi Yvan.

That should be set end of chosenItemsList to item anIndex of aList, otherwise you’re returning the doctored string instead of the original.

Oops, you are right Nigel.

I edit the message accordingly.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 5 décembre 2019 11:42:00

Somebody wrote this (over ten years or more before ?). It is very simple one.


set aList to {"red", "blue", "green", "white"}
set aMes to "Select Color"
set aRes to retItemFromListByItemNo(aList, aMes) of me


on retItemFromListByItemNo(aList, aMes)
	set aRes to choose from list aList with prompt aMes
	if aRes = false then return 0
	
	set aRes to contents of item 1 of aRes
	set hitNum to 1
	repeat with i in aList
		set j to contents of i
		if j is equal to aRes then
			exit repeat
		end if
		set hitNum to hitNum + 1
	end repeat
	return hitNum
end retItemFromListByItemNo

The problem with this old code is that if a string is available twice in the list, the index returned may be wrong.

try with :
set aList to {“red”, “blue”, “green”, “white”, “blue”}
and select the last “blue”.
You will get 2 when it is at index 5.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 5 décembre 2019 12:03:52