Extracting a String from another String without looping

I am trying to check if an element from one string is contained in another without looping. As the loop results in a significant increase in run time.

The routine below does that but I am concerned that it may also cause a problem as it may find a result it should not.

To explain this is the log of the following script
Vals1= (PBCT, STBA, PNFP, BK, AOS, SXT, BMTC, LNT, PNM, WBS, SON, EIG, EWBC)

Vals2 = (AOS, BK, BUSE, CAC, EGBN, GGG, GSBC, LNT, NBHC, PNM, RY, STBA, PNFP, WBS, PBCT, SXT, BMTC, SON, EIG, EWBC)

The first line contains the element I am looking for in the second line. An example of my problem. If I was looking for “BCTS” , my script finds it in the second line “PBCT,SXT”.


tell application "Microsoft Excel" --
	set Check to name of last sheet of active workbook --
	select worksheet Check --Have to Switch so the "String value " command works
	tell active sheet to set UsedRows to ((first row index of (get end (last cell of column 1) direction toward the top)))
	set Rng1 to "A3:A" & UsedRows
	set Vals1 to string value of range Rng1
	log Vals1
	select worksheet "TestOptionData" --
	tell worksheet "TestOptionData" to set UsedRows to ((first row index of (get end (last cell of column 1) direction toward the top)))
	set Rng2 to "A3:A" & UsedRows
	set Vals2 to string value of range Rng2
	log Vals2
	select worksheet Check
end tell

repeat with NewStock from 1 to count of Vals1
	set StockSYMnew to (item NewStock of Vals1)
	if (Vals2 as string) contains StockSYMnew then
		display dialog Vals2 as string
	else
		display dialog "Does not"
	end if
end repeat

When you do

(Vals2 as string)

you are concatenating the items in the list into one big string. You should not do this because, as you found out, it prevents you from testing against each item as a discrete thing.

Instead, do this:


repeat with NewStock from 1 to count of Vals1
	set StockSYMnew to (item NewStock of Vals1)
	if Vals2 contains StockSYMnew then -- change to this
		display dialog Vals2 as string
	else
		display dialog "Does not"
	end if
end repeat

And unless you actually need the index for some reason you aren’t showing here, you could just do this:


repeat with NewStock in Vals1
	if Vals2 contains NewStock then
		display dialog Vals2 as string
	else
		display dialog "Does not"
	end if
end repeat

It is not very clear what the OP wants to achieve. If the OP just wants to check if the 2 given scripts contain a common element(s) or they are unique, then this can be done without a repeat loop:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set Vals1 to {1, "r", file, "b"} -- one list
set Vals2 to {3, 4, 5, home folder, "r", "c"} -- other list

-- get concatated list from 2 lists
set concatatedList to Vals1 & Vals2
-- get set of unique elements of concatated list (that is, remove duplicates)
set aSet to current application's NSSet's setWithArray:concatatedList

-- compare length of unique set with length of concatated list
set |Two Lists Contain Common Items ?| to (aSet's |count|()) < (count concatatedList)

--> RESULT: true

The suggestion by roosterboy uses a repeat loop but should be plenty fast unless the lists in question are extremely large. If they are, then the suggestion by KniazidisR will be very fast. FWIW, I’ve included an additional suggestion below, which took 0.002 second to run. It returns true or false with true indicating that one or more items in Val1 are in Val2.

use framework "Foundation"
use scripting additions

set Vals1 to {"PBCT", "STBA", "PNFP", "BK", "AOS", "SXT", "BMTC", "LNT", "PNM", "WBS", "SON", "EIG", "EWBC"}
set Vals2 to {"AOS", "BK", "BUSE", "CAC", "EGBN", "GGG", "GSBC", "LNT", "NBHC", "PNM", "RY", "STBA", "PNFP", "WBS", "PBCT", "SXT", "BMTC", "SON", "EIG", "EWBC"}

set setOne to current application's NSSet's setWithArray:Vals1
set setTwo to current application's NSSet's setWithArray:Vals2
set theResult to ((setOne's intersectsSet:setTwo) as integer = 1) --> true

If the OP wants a list of the items in Vals1 that are in Vals2 then:

use framework "Foundation"
use scripting additions

set Vals1 to {"PBCT", "STBA", "PNFP", "BK", "AOS", "SXT", "BMTC", "LNT", "PNM", "WBS", "SON", "EIG", "EWBC", "XXX"}
set Vals2 to {"AOS", "BK", "BUSE", "CAC", "EGBN", "GGG", "GSBC", "LNT", "NBHC", "PNM", "RY", "STBA", "PNFP", "WBS", "PBCT", "SXT", "BMTC", "SON", "EIG", "EWBC"}

set setOne to current application's NSMutableOrderedSet's orderedSetWithArray:Vals1
set setTwo to current application's NSSet's setWithArray:Vals2
setOne's intersectSet:setTwo
set setOneInSetTwo to (setOne's allObjects()) as list

thank you all.

Roosterboy I can not get yours to work. This the end of my previous script changed to what you suggested. I added log comments so I could show you the result. It think its because “set Vals1 to string value of range Rng1” does not provide a list .I hope I have not missed something.

repeat with NewStock from 1 to count of Vals1
	set StockSYMnew to (item NewStock of Vals1)
	log StockSYMnew & "Stock in Vals1"
	if Vals2 contains StockSYMnew then
		log StockSYMnew & "Is in Vals2"
	else
		log StockSYMnew & "Not in Vals2"
	end if
end repeat

And this is the log I only ran it twice as the first Stock is not in the last but the second is.

(PBCT, STBA, PNFP, BK, AOS, SXT, BMTC, LNT, PNM, WBS, SON, EIG, EWBC)
(*Stocks in Vals 1 *)
(*AOS, BK, BUSE, CAC, EGBN, GGG, GSBC, LNT, NBHC, PNM, RY, STBA, PNFP, WBS, PBC, SXT, BMTC, SON, EIG, EWBC, Stocks in Vals2 *)
(PBCT, Stock in Vals1)
(PBCT, Not in Vals2)
(STBA, Stock in Vals1)
(STBA, Not in Vals2)

KniazidisR, I apologise was my request was not clear but I do need to process the stocks in Vals1 not in Vals2, so just know that there is one not in the other list does not help.

peavine, Been working with yours as that seems to be on the right track but can not work out how to manipulate it so as I can locate the one element in Vals1 not in Vals2, which in your second script would be “XXX”.

MitchBVI. The following script returns every item in Vals1 not in Vals2. There’s really no way that I’m aware of to stop the script after just one item is found in Vals1 that is not in Vals2. However, it’s easy just to take the first item in the returned list if that’s what you want.

I don’t have Excel and have no way to know what Vals1 or Vals2 are set to. If it’s a list then the following should work but if it’s a string then it needs to be made into a list.

use framework "Foundation"
use scripting additions

set Vals1 to {"AAA", "BBB", "CCC", "DDD"}
set Vals2 to {"AAA", "CCC", "XXX", "YYY"}

set setOne to current application's NSMutableOrderedSet's orderedSetWithArray:Vals1
set setTwo to current application's NSSet's setWithArray:Vals2
setOne's minusSet:setTwo
set uniqueToVals1 to (setOne's allObjects()) as list --> {"BBB", "DDD"}

Brilliant .3 second process 500 lines, my loop was about 4 times that. Setting Vals1 & Vals2 as "string value " still returns a list so I can process each element the result of your solution.

With a lot of help I had managed to work out how to manipulate data outside of Excel but ran into a problem working out what data had to be processed, without reverting to Excel, your solution solved that.

Thank you

You’re welcome. I’m glad that worked.