if aVariable contains [ANY out of multiple list items] then ** How to?

I have a variable theDueDate, which can either be OR contain one if the following indicators. The indicators are kept in lists. If theDueDate consists solely of a word which contains the indicator, it is no problem to use is in followed by the indicator list (see first 3 if cases below for yesterday, today, tomorrow).

My problem are those cases where theDueDate in addition to the indicator also consists of a number, e.g. “-10d” is short for a date 10 days ago". Because in those cases I have to use contains (instead of is in), which seems NOT to be able to deal with a list of values:

set theDueDate to "2d" -- short for "2 days"

set theDueDelimiters to {"#due:", "#d:", "#fällig:", "#f:", "#echeance:", "#ech:", "#e:", "#vencimiento:", "#ven:", "#v:"}
set theYesterdayIndicators to {"yes", "ges", "hie", "aye"} -- for yesterday, gestern, hier, ayer
set theTodayIndicators to {"tod", "heu", "auj", "hoy"} -- for today, heute, aujourd'hui, hoy
set theTomorrowIndicators to {"tom", "mor", "dem", "mañ", "man"} -- for tomorrow, morgen, demain, mañana, manana
set theDayIndicators to {"d", "t", "j"} -- for day/día, Tag, jour
set theWeekIndicators to {"w", "s"} -- for week/Woche, semain/semana


if theDueDate is in theYesterdayIndicators then -- Yesterday
	set theDueDate to date string of ((current date) - (1 * days))
else if theDueDate is in theTodayIndicators then -- Today
	set theDueDate to date string of (current date)
else if theDueDate is in theTomorrowIndicators then -- Tomorrow
	set theDueDate to date string of ((current date) + (1 * days))
	
	-- ### The following line relates to my question ###
else if theDueDate contains item 1 in theDayIndicators or theDueDate contains item 2 in theDayIndicators or theDueDate contains item 3 in theDayIndicators then -- Days
	set AppleScript's text item delimiters to theDayIndicators
	set numberofDays to text item 1 of theDueDate
	set theDueDate to date string of ((current date) + (numberofDays * days))
else if theDueDate contains item 1 in theWeekIndicators or theDueDate contains item 2 in theWeekIndicators then -- Weeks
	set AppleScript's text item delimiters to theWeekIndicators
	set numberofWeeks to text item 1 of theDueDate
	set theDueDate to date string of ((current date) + (numberofWeeks * weeks))
end if

Question: Is there any working syntax like the following?

else if theDueDate contains any of the items in theDayIndicators then

(This one leads to error number -1700).

Hi Wookie,

Had to correct the other post.

You should get the current date one time. Although in this case it might not matter because of the if-then statement. Anyway, this seems to work:


-- note: get the current date one time
set curDate to current date

set theDueDate to "2d"
set theDayIndicators to {"d", "t", "j"}
theDayIndicators contains some text item of theDueDate

-- result: true

Had to change “item” to “text item”

gl

Model: MacBook Pro
AppleScript: 2.2.3
Browser: Safari 536.26.17
Operating System: Mac OS X (10.8)

This doesn’t work. I also came across some. That one however picks randomly one of list items, which means that your proposed script randomly returns true OR false.

HI Wookie,

I just found that out. Trying to remember.

gl,

Model: MacBook Pro
AppleScript: 2.2.3
Browser: Safari 536.26.17
Operating System: Mac OS X (10.8)

I had a feeling that wasn’t going to work. Guess you have to do it the long way:


set d to "2t"
"d" is in d or "t" is in d or "j" is in d

gl

Well thanks, but that’s pretty much the same like what I currently have:

else if theDueDate contains item 1 in theDayIndicators or theDueDate contains item 2 in theDayIndicators or theDueDate contains item 3 in theDayIndicators then

An improvement would be if there wouldn’t be any repetition and the syntax would be independent from the number of entries in the theDayIndicators list.

Hi,

if the day indicator is always the last character of the string you can write


set theDueDate to "2d"
set theDayIndicators to {"d", "t", "j"}
set indicatorExists to last character of theDueDate is in theDayIndicators


Hi Wookie,

Also, I finally found another scripting addition besides Smile’s:

http://www.latenightsw.com/freeware/RecordTools/index.html

You can use it to get the intersection of two lists.

gl,

The AS which will contain this snippet will be distributed, so I can’t incorporate scripting additions.

It is indeed always the last character, so this works. Thx, mate!

StefanK… This is what I end up with. Not really shorter, unless theDayIndicators list has lots of entries. Ideas on further streamlining?

set theDueDate to "2d" -- short for "2 days"

set theDueDelimiters to {"#due:", "#d:", "#fällig:", "#f:", "#echeance:", "#ech:", "#e:", "#vencimiento:", "#ven:", "#v:"}
set theYesterdayIndicators to {"yes", "ges", "hie", "aye"} -- for yesterday, gestern, hier, ayer
set theTodayIndicators to {"tod", "heu", "auj", "hoy"} -- for today, heute, aujourd'hui, hoy
set theTomorrowIndicators to {"tom", "mor", "dem", "mañ", "man"} -- for tomorrow, morgen, demain, mañana, manana
set theDayIndicators to {"d", "t", "j"} -- for day/día, Tag, jour
set theWeekIndicators to {"w", "s"} -- for week/Woche, semain/semana


if theDueDate is in theYesterdayIndicators then -- Yesterday
	set theDueDate to date string of ((current date) - (1 * days))
else if theDueDate is in theTodayIndicators then -- Today
	set theDueDate to date string of (current date)
else if theDueDate is in theTomorrowIndicators then -- Tomorrow
	set theDueDate to date string of ((current date) + (1 * days))
else
	set indicatorExists to last character of theDueDate is in theDayIndicators
	if indicatorExists is true then -- Days
		set AppleScript's text item delimiters to theDayIndicators
		set numberofDays to text item 1 of theDueDate
		set theDueDate to date string of ((current date) + (numberofDays * days))
	else -- ... so it must be Weeks then (I dropped the testing here, which I could also in my initial script)
		set AppleScript's text item delimiters to theWeekIndicators
		set numberofWeeks to text item 1 of theDueDate
		set theDueDate to date string of ((current date) + (numberofWeeks * weeks))
	end if
end if

I came up with a solution based on count. The advantage is that doesn’t care about the position of the Indicator list items in theDueDate, and that ” if I drop the final, not really required test against theWeekIndicators (-> remove the 2 lines marked with “#)”) ” the routine is even 1 line shorter.

set theDueDate to "2d" -- short for "2 days"

set theDueDelimiters to {"#due:", "#d:", "#fällig:", "#f:", "#echeance:", "#ech:", "#e:", "#vencimiento:", "#ven:", "#v:"}
set theYesterdayIndicators to {"yes", "ges", "hie", "aye"} -- for yesterday, gestern, hier, ayer
set theTodayIndicators to {"tod", "heu", "auj", "hoy"} -- for today, heute, aujourd'hui, hoy
set theTomorrowIndicators to {"tom", "mor", "dem", "mañ", "man"} -- for tomorrow, morgen, demain, mañana, manana
set theDayIndicators to {"d", "t", "j"} -- for day/día, Tag, jour
set theWeekIndicators to {"w", "s"} -- for week/Woche, semain/semana


if theDueDate is in theYesterdayIndicators then -- Yesterday
	set theDueDate to date string of ((current date) - (1 * days))
else if theDueDate is in theTodayIndicators then -- Today
	set theDueDate to date string of (current date)
else if theDueDate is in theTomorrowIndicators then -- Tomorrow
	set theDueDate to date string of ((current date) + (1 * days))
else -- THE DESCRIBED count-based IMPROVEMENT BEGINS HERE
	set AppleScript's text item delimiters to theDayIndicators -- Days
	if (count text items of theDueDate) > 1 then
		set numberofDays to text item 1 of theDueDate
		set theDueDate to date string of ((current date) + (numberofDays * days))
	else
		set AppleScript's text item delimiters to theWeekIndicators -- Weeks
		if (count text items of theDueDate) > 1 then -- #)
			set numberofWeeks to text item 1 of theDueDate
			set theDueDate to date string of ((current date) + (numberofWeeks * weeks))
		end if -- #)
	end if
end if

-- #) if "Weeks" is the last possibility, one can even drop the test against theWeekIndicators, thus remove the lines marked #)

Assuming that you’ve already reduced any “yesterday”, “today”, or “tomorrow” input to three letters, here’s a version which doesn’t need TIDs:

set theDueDate to "2d" -- short for "2 days"

set theDueDelimiters to {"#due:", "#d:", "#fällig:", "#f:", "#echeance:", "#ech:", "#e:", "#vencimiento:", "#ven:", "#v:"}
set theYesterdayIndicators to {"yes", "ges", "hie", "aye"} -- for yesterday, gestern, hier, ayer
set theTodayIndicators to {"tod", "heu", "auj", "hoy"} -- for today, heute, aujourd'hui, hoy
set theTomorrowIndicators to {"tom", "mor", "dem", "mañ", "man"} -- for tomorrow, morgen, demain, mañana, manana
set theDayIndicators to {"d", "t", "j"} -- for day/día, Tag, jour
set theWeekIndicators to {"w", "s"} -- for week/Woche, semain/semana

set now to (current date)
if (theDueDate is in theYesterdayIndicators) then -- Yesterday
	set theDueDate to now - days
else if (theDueDate is in theTodayIndicators) then -- Today
	set theDueDate to now
else if (theDueDate is in theTomorrowIndicators) then -- Tomorrow
	set theDueDate to now + days
else if (last character of theDueDate is in theDayIndicators) then
	set theDueDate to now + (text 1 thru -2 of theDueDate) * days
else -- ... so it must be Weeks then (I dropped the testing here, which I could also in my initial script)
	set theDueDate to now + (text 1 thru -2 of theDueDate) * weeks
end if
set theDueDate to theDueDate's date string

It strikes me is that, since you have to go some way to getting the date in your other thread in order to validate it, the validation and interpretation could be usefully combined. This returns an AppleScript date if the input’s valid, or ‘false’ if it’s not:

property theYesterdayIndicators : {"yes", "ges", "hie", "aye"} -- for yesterday, gestern, hier, ayer
property theTodayIndicators : {"tod", "heu", "auj", "hoy"} -- for today, heute, aujourd'hui, hoy
property theTomorrowIndicators : {"tom", "mor", "dem", "mañ", "man"} -- for tomorrow, morgen, demain, mañana, manana
property theDayIndicators : {"d", "t", "j"} -- for day/d'a, Tag, jour
property theWeekIndicators : {"w", "s"} -- for week/Woche, semain/semana

-- Construct a regex to parse non-date input in the languages provided for in the script properties.
on getPlugin()
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ""
	set validateDayWeekIndicator to (theDayIndicators as text) & theWeekIndicators
	set AppleScript's text item delimiters to "|"
	set validateYesTodTomIndicator to {theYesterdayIndicators, theTodayIndicators, theTomorrowIndicators} as text
	set AppleScript's text item delimiters to astid
	
	return "[" & validateDayWeekIndicator & "]|(" & validateYesTodTomIndicator & ")[[:alpha:]'\\'']*"
end getPlugin

-- Is the passed date input valid in this script's terms? If so, return the date as an AppleScript date object. If not, return the boolean 'false'.
on validateDateInput(dateInput)
	-- A string of the short-date separators to be recognised. (Edit to taste.)
	-- These will be used in a regex class, so the hyphen must be first or last.
	set allowedSeparators to "-/."
	-- A list of short-date part regexes, in day, month, year order.
	set datePartRegices to {"(0?[1-9]|[12][0-9]|3[01])", "(0?[1-9]|1[0-2])", "([1-9][0-9])?[0-9]{2}"}
	
	-- Get the local short-date string for 1st February 4003, strip out everything except the "1", the "2", and the "3", and turn these into a 3-digit integer. Use the digits to index the short-date part regexes and to arrange them into the equivalent order in a full short-date regex.
	set order to (do shell script ("<<<" & quoted form of short date string of («data isot343030332D30322D3031» as date) & " sed -E 's/[^123]//g'")) as integer
	set separatorClass to "[" & allowedSeparators & "]"
	tell datePartRegices to set shortDateRegex to item (order div 100) & separatorClass & item (order mod 100 div 10) & separatorClass & item (order mod 10)
	
	set today to (current date)
	set today's time to 0
	if ((do shell script ("<<<" & quoted form of dateInput & " sed -E '/^" & shortDateRegex & "$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean) then
		-- If dateInput is a valid date string in any of the allowed formats, test to see if it represents a valid Gregorian or Proleptic Gregorian date.
		set astid to AppleScript's text item delimiters
		set AppleScript's text item delimiters to characters of allowedSeparators -- Requires Snow Leopard or later.
		tell dateInput's text items
			set {item (order div 100), item (order mod 100 div 10), item (order mod 10)} to {beginning as integer, item 2 as integer, end as integer}
			set {d, m, y} to it
			-- If the "year" has only two digits, count it as being in the current century.
			if (y < 100) then set y to (today's year) div 100 * 100 + y
		end tell
		set AppleScript's text item delimiters to astid
		
		-- If the date's valid, revalue the 'today' date object to it and set 'ValidDueDate' to that.
		set ValidDueDate to ((d < 29) or (m is in {1, 3, 5, 7, 8, 10, 12}) or ((d < 31) and (m > 2)) or ((d is 29) and (y mod 4 is 0) and (y mod 400 is not in {100, 200, 300})))
		if (ValidDueDate) then tell today to set {day, year, its month, day, ValidDueDate} to {1, y, m, d, it}
	else
		-- Otherwise test for any of the alternative valid inputs.
		set ValidDueDate to (do shell script ("<<<" & quoted form of dateInput & " sed -E 'y/ABCDEFGHIJKLMNÑOPQRSTUVWXYZ/abcdefghijklmnñopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*" & getPlugin() & ")$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean
		if (ValidDueDate) then
			-- If this is a valid alternative, test to see if if contains any digit characters.
			set astid to AppleScript's text item delimiters
			set AppleScript's text item delimiters to characters of "123456789" -- Requires Snow Leopard or later.
			set inputContainsDigit to ((count dateInput each text item) > 1)
			set AppleScript's text item delimiters to astid
			
			if (inputContainsDigit) then
				-- If it contains digits, add the relevant number of days or weeks to 'today' and set 'ValidDueDate' to the result.
				set ValidDueDate to today + (text 1 thru -2 of dateInput) * (((last character of dateInput is in theWeekIndicators) as integer) * 6 + 1) * days
			else
				-- Otherwise it's a word of which only the first three letters have to be present or correct. Test the first three letters against the indicators in this script's properties and add or subtract the appropriate number of days.
				tell text 1 thru 3 of dateInput to set ValidDueDate to today + (((it is in theTomorrowIndicators) as integer) - ((it is in theYesterdayIndicators) as integer)) * days
			end if
		end if
	end if
	
	return ValidDueDate -- Either an AppleScript date object or the boolean 'false'.
end validateDateInput

validateDateInput("mañana")

Woo-hooo… Nigel you are plain nuts! :smiley: (Sorry for my late response, other things to do here).

Cool stuff, although I understand only half of it. Currently in the process of implementing it into my hack. Guess if I present you the result, you will change every single except yours… :stuck_out_tongue:

What I don’t quite understand: Why is your script able to properly validate “mañana” although the following line of code doesn’t contain the special character ñ and Ñ?

		set ValidDueDate to (do shell script ("<<<" & quoted form of dateInput & " sed -E 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*" & getPlugin() & ")$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean

Is it because of validateYesTodTomIndicator on on getPlugin? I don’t quite get it, as adding those foreign characters was mandatory in the former code (see the other thread. I mean what does my here above quoted line do in this full-fledged scriptlet? If I want to add another language, under which circumstances do I need to add special characters?

I see that your post is timed at getting on for 04:00 your local time. You must be working overtime to catch up!

Yes. The result from the handler contains the possibilities allowed by the code you wrote, including one where the input consists solely of “mañ” followed by zero or more alphabetical characters. (Apostrophes are also allowed among the trailing characters in case the input’s “aujourd’hui”.)

The two alphabets are part of a preprocessing stage which converts any upper-case characters in the input to lower case, because ‘sed’ is case sensitive. In fact I forgot to include “Ñ” and “ñ”, so the script didn’t recognise “MAÑANA”. I’ve now corrected that above. But perhaps getPlugin() should provide the necessary alphabets too…

With an input of “MAÑANA”, the shell script constructed is “<<<‘MAÑANA’ sed -E ‘y/ABCDEFGHIJKLMNÑOPQRSTUVWXYZ/abcdefghijklmnñopqrstuvwxyz/ ; /^([-+]?[1-9][0-9][dtjws]|(yes|ges|hie|aye|tod|heu|auj|hoy|tom|mor|dem|mañ|man)[[:alpha:]‘\’'])$/ !s/.*/false/; /false/ !s/.+/true/ ;’”, which breaks down to:

Wouldn’t it be easier if every character in the input string was converted to the equivalent, without a diacritical?

If you test for likeness while you ignore diacriciticals then you would find the correct character on a general basis.
Maybe this is a tad slow, but it shouldn’t be noticable. It would be much easier to get right.

Like below, and then pass the resulting string to sed.


set inputstr to "tåpelig"
set slen to length of inputstr
set newStr to ""
ignoring case and diacriticals
	” Default values above, here for illustrative purposes
	repeat slen times
		set pos to offset of (text 1 of inputstr) in "abcdefghijklmnopqrstuvwxyz"
		if pos ≠ 0 then
			set newStr to newStr & text pos of "abcdefghijklmnopqrstuvwxyz"
		else
			set newStr to newStr & text 1 of inputstr
		end if
		try
			set inputstr to text 2 thru -1 of inputstr
		on error
			set inputstr to text -1 of inputstr
		end try
	end repeat
end ignoring

This is made under the assumption that every character with a diacritical is can be made to one without, and that we are only dealing with languages that use the western alphabet as a starting point for eventually making new characters.

By the way. I think Apple may use loose utf-16 encoding, using one utf-16 code point for each character and accent, where it could have been represented by one code point, that would at least speed up the ignoring the diacrictical thing internally. :slight_smile:

Nigel, the reason I’m asking and why I’m so confused: Your script in #13 flawless turns MAÑANA / mañana / MaÑANa into today’s date, WITHOUT the addition of Ñ and ñ to that alphabetic string!?!

It should also be noted that the script might need to understand russian (cyrillic) and japanese (is it called kanji?), given that a Cultured Code Things user ” again this snippet will be part of AS extension to Things/Mail ” asks for it and provides the translation.

That’s true, actually, although I might do the conversion something like this:


set inputstr to "TÅPELIG, MAÑANA , Í DAG, VČEREJŠEK"

set baseLetters to "abcdefghijklmnopqrstuvwxyz"

set astid to AppleScript's text item delimiters
ignoring diacriticals and case
	repeat with i from 1 to 26
		set AppleScript's text item delimiters to character i of baseLetters
		set inputstr to inputstr's text items as string
	end repeat
end ignoring
set AppleScript's text item delimiters to astid

inputstr

That is odd. It works as of this morning, because I’ve now inserted those letters. But if they’re removed, it doesn’t work with an “Ñ” on my machine.

My idea was that the OS might have some info in the memory, so I tested #13 on a Mac which hadn’t seen the script before: Flawless. A OK with mañana, MAÑANA etc. Could the LOCALE setting (system’s language) affect it? My LOCALEs here are “de” (so german; though german doesn’t have ñ/Ñ).