Check if Item in an Excel list exisst in another Excel list

The script below is very simple I am trying in the example to see if items in ColorNew exists in ColorExists.

I pick up the two lists from sheets 8 & 9 and at present rebuild the ColorExists list. In practice both those list will be large so I am trying to avoid the loop that presently rebuilds the existing list. As you can see I have left in (but grayed out) my attempt to coerce the list I am checking against into a list.

tell application "Microsoft Excel"
	set ColorExists to {}
	set RowsNew to (first row index of (last cell of used range of (worksheet "sheet8"))) --Blue,Black,Red,Yellow,Pink
	set ColorNew to string value of range ("A3:A" & RowsNew) of (worksheet "sheet8")
	log ColorNew
	set RowsExist to (first row index of (last cell of used range of (name of worksheet "sheet9"))) --Blue,Black,Red,Orange,Purple
	set ColorExists to (value of range ("A3:A" & RowsExist) of worksheet "sheet9")
	log ColorExists
	--set end of ColorExists to (value of range ("A3:A" & RowsExist) of worksheet "Sheet9") as list
	repeat with CntSym from 1 to count of ColorExists
		set end of ColorExists to item 1 of item CntSym of ColorExists
	end repeat
	repeat with cnt from (count of ColorNew) to 1 by -1
		if ColorExists contains (item 1 of item cnt of ColorNew) then
			display dialog "it does" & " " & (item 1 of item cnt of ColorNew)
		else
			display dialog "it does not" & " " & (item 1 of item cnt of ColorNew)
		end if
	end repeat
end tell

Browser: Safari 605.1.15
Operating System: macOS 12

This will put “it does ____” or “it does not ____” for each color into a results list. I’ll leave it as an exercise for the reader to use display dialog instead. (Though why you would want to do that if, as you say, “both those list will be large”, I have no idea.)


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

set TRUE_MESSAGE to "it does "
set FALSE_MESSAGE to "it does not "

tell application "Microsoft Excel"
	set rowsNew to column 1 of current region of range "$A$3" of worksheet "sheet8"
	set rowsExistAddress to get address of column 1 of current region of range "$A$3" of worksheet "sheet9" with external
	
	set newColors to string value of rowsNew
	set results to string value of rowsNew -- so we have a list of the same size as newColors
	
	repeat with i from 1 to count of newColors
		
		set searchTerm to (item i of newColors) as text
		set trueResult to "\"" & TRUE_MESSAGE & searchTerm & "\""
		set falseResult to "\"" & FALSE_MESSAGE & searchTerm & "\""
		--"IF(COUNTIF(range, searchTerm)>0, true, false)"
		set item i of results to evaluate name "=IF(COUNTIF(" & rowsExistAddress & ",\"" & searchTerm & "\")>0," & trueResult & "," & falseResult & ")"
		
	end repeat
	
	results
	
end tell

Thanks for your reply. I did not explain my issue well, items on “sheet8” not on “sheet9” will be transferred to that sheet. Running your script did not show the missing items.

The issue is that data I pull from Sheet9. My original solution (and still I think the only way) was to build a list of the data and I was trying to save a little time by avoiding that step.

In the following script I have hard keyed the data I am checking against and it works.
I assume there is no other way but to loop through the existing data to build a new list.

set Results to {}
tell application "Microsoft Excel"
	set ColorExists to {"Blue", "Black", "Red", "Orange", "Purple"}
	set ColorNew to value of range ("A3:A7") of worksheet "Sheet8"
	repeat with cnt from (count of ColorNew) to 1 by -1
		if ColorExists contains (item 1 of item cnt of ColorNew) then
			set end of Results to "it does" & " " & (item 1 of item cnt of ColorNew)
		else
			set end of Results to "it does not" & " " & (item 1 of item cnt of ColorNew)
		end if
	end repeat
end tell
log Results

Here’s the result I get from your code:


{"it does not Pink", "it does not Yellow", "it does Red", "it does Black", "it does Blue"}

Here’s the result I get from my code:


{"it does Blue", "it does Black", "it does Red", "it does not Yellow", "it does not Pink"}

Apart from the order, what is different that doesn’t work for you?

Again my apologies the active sheet was sheet9 if I made the active sheet sheet8 I got the correct result.

However I still have not resolved my issue as I need to process any item that is on sheet8 but not sheet9. Obviously I do not need the “it does” & “it does not” just the way to process those not on sheet9. I cannot figure out how to parse your evaluation line.

I apologise for taking your time and totally understand if you do not want to waste any more of it.

thanks

Peter

Oh and the reason our lists where the other way around is that in actual use I delete the row that will be transferred to sheet9.

Sorry I was not thinking I have worked it out , thank you again. I managed to confuse myself with the terms I was using for testing. The "with external " is something I have never used. I looked in the AppleScript and Excel dictonary but failed to look under get address.

You didn’t mention in your original post that you were wanting to delete rows. You indicated you wanted to build a list of “it does” and “it does not”. In the future, please state what you actually want to do, as it can affect how people answer your questions.

Anyway, here’s a script that should do what you want:


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

(*
Utility function to take a nested list of text items, like:
    {{"Blue"},{"Black"},{"Purple"}}
and flatten it into a single-level list of text items, like:
    {"Blue","Black","Purple"}

REMEMBER: Excel ranges are returned as nested AppleScript lists!
*)
to flatten:aList
	set outputList to {}
	repeat with anItem in aList
		set end of outputList to anItem as text
	end repeat
	return outputList
end flatten:

on run
	-- the row where our ranges begin
	set startRow to 3
	
	-- get the data from Excel
	tell application "Microsoft Excel"
		-- keep a reference to the sheet to make things easier later
		set newColorsSheet to worksheet "sheet8"
		tell newColorsSheet
			-- where does our list of colors start?
			set rngStart to range ("$A$" & startRow)
			-- now go to the end, just like using Cmd+down arrow
			set rngEnd to get end rngStart direction toward the bottom
			-- make our range using the start and end cell addresses
			set newColorsRange to range ({(get address rngStart), ":", (get address rngEnd)} as text)
			-- and get a list of the colors inn the range
			-- this is in the format {{"Blue"},{"Black"},{"Purple"}}
			set newColors to string value of newColorsRange
		end tell
		
		-- keep a reference to the sheet to make things easier later
		set existingColorsSheet to worksheet "sheet9"
		tell existingColorsSheet
			-- where does our list of colors start?
			set rngStart to range ("$A$" & startRow)
			-- now go to the end, just like using Cmd+down arrow
			set rngEnd to get end rngStart direction toward the bottom
			-- make our range using the start and end cell addresses
			set existingColorsRange to range ({(get address rngStart), ":", (get address rngEnd)} as text)
			-- and get a list of the colors inn the range
			-- this is in the format {{"Blue"},{"Black"},{"Purple"}}
			set existingColors to string value of existingColorsRange
		end tell
	end tell
	
	--and we're done with Excel for now!
	
	-- set up a list to hold the rows we want to remove when we're done
	set rowsToDelete to {}
	-- flatten the list of colors to make it easier to search
	-- this will be in the format {"Blue","Black","Purple"}
	set colorsForTest to my flatten:existingColors
	
	-- now, loop through the list of newColors
	repeat with idx from 1 to count of newColors
		-- get the item as "Blue" instead of {"Blue"}
		set aColor to (item idx of newColors) as text
		-- do we already have this color in the existing list?
		if colorsForTest does not contain aColor then
			-- no? okay then, add it to the list
			set end of existingColors to {aColor}
			-- we want to delete this row
			-- we get the index of the current item and add it to the row we started from
			-- remembering to subtract 1 to make the math work
			set deleteRow to (idx - 1) + startRow
			-- now add this range address to the list
			-- in the format "$1:$1" to indicate to Excel that we want the entire row
			set end of rowsToDelete to "$" & deleteRow & ":$" & deleteRow
		end if
	end repeat
	
	-- check if we actually found some new colors
	-- we know whether this is the case if we have some rows to delete
	if rowsToDelete is {} then
		-- nope! so we can quit in good conscience
		error number -128
	end if
	
	-- our list of rows to delete looks like this: {"$1:$1", "$5:$5"}
	-- and we need it to be a string like this: "$1:$1,$5:$5"
	set {oldDelims, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ","}
	set rowsToDelete to rowsToDelete as text
	set AppleScript's text item delimiters to oldDelims
	
	-- now we can jump back into Excel for finishing up
	tell application "Microsoft Excel"
		-- remember how we saved a reference to the sheet for later? smart!
		tell existingColorsSheet
			-- resize our existingColorsRange to accomodate all the new colors we're going to add
			set newExistingColorsRange to get resize existingColorsRange row size (count of existingColors)
			-- set the value of the expanded range
			set value of newExistingColorsRange to existingColors
		end tell
		
		-- finally, get rid of the rows from the newColorsSheet that were added to existingColorsSheet
		tell newColorsSheet
			-- using a range like "$1:$1,$5:$5" lets us get rid of entire rows all at once
			-- avoiding having to loop through the rows backwards
			delete range range rowsToDelete
		end tell
	end tell
end run

Given these starting lists:

It ends up like this: