List compare

Hi everyone,

I have two lists:

set list_1 to ¬
	{{"ABC", "123"}, {"DEF", "123"}}
set list_2 to ¬
	{{"ABC", "123", "000"}, {"ABC", "444", "111"}, {"DEF", "123", "222"}, {"DEF", "123", "333"}}

  1. I want to be able to see if (items 1 thru 2 of item x) of list_2 are in list_1
  2. If they aren’t, return (items 1 thru 2 of item x) of list_2 (i.e., the INCORRECT value) AND what the item in list_1 is (i.e., the CORRECT value).

So for the above example lists, (items 1 thru 2 of item 2) of list_2 are NOT in list_1. So I want to return the value from list_1 of {“ABC”, “123”} to give me the correct value. I hope this makes sense. Thanks ahead of time for the help.

So it seems you only need some repeat loops and if statements… that seems like you should be able to figure it out. Is there anything in particular you’re having trouble with? What do you have so far?

Hello.

This is the brute force approach it will iterate through your list i * j times worst, case where i is the number of sublists to check, and j is the number of valid sublists.

If you have really many items in both lists, then it would be best to presort them and use a binary search algorithm to find the primary key way faster: i * ( log j + 1) comparisons (log2).

ASSUMPTIONS:
The primary keys in list one is unique, that is there is only one sublist which has “ABC” as its first item.
I also assume that there is no primary keys in the list to check which are not represented in the valid list.
I further assume that all primary keys are uppercase


set list_1 to ¬
	{{"ABC", "123"}, {"DEF", "123"}}
set list_2 to ¬
	{{"ABC", "123", "000"}, {"ABC", "444", "111"}, {"DEF", "123", "222"}, {"DEF", "123", "333"}}

log validateList(list_1, list_2) ”> *invalid entry : {ABC,444,111} should be{ ABC,123}
on validateList(theValid, toCheck)
	script o
		property l : theValid
		property m : toCheck
	end script
	set {tids, text item delimiters} to {text item delimiters, ","}
	set output to {}
	set numValid to (count theValid)
	considering case
		repeat with i from 1 to (count toCheck)
			repeat with j from 1 to numValid
				if item 1 of item i of o's m is item 1 of item j of o's l then
					if item 2 of item i of o's m is item 2 of item j of o's l then
						exit repeat
					else
						set end of output to {"invalid entry : {" & item i of o's m & "} should be{ " & item j of o's l & "}" }
						exit repeat
					end if
				end if
			end repeat
		end repeat
	end considering
	set text item delimiters to tids
	return output
end validateList


Wow, McUsr. When I run your script, it seems to give the right answer, but I was hoping for a more simple solution. I actually don’t care about the case-sensitivity, sorry for not clarifying that. I was just trying to give simple list item examples. To answer Hank’s question, I do have something so far, but for me, it wasn’t as easy as it was to Hank (which is why I’m asking for help :D)

Here’s what I have so far:


set list_1 to ¬
	{{"ABC", "123"}, {"DEF", "123"}}
set list_2 to ¬
	{{"ABC", "123", "000"}, {"ABC", "444", "111"}, {"DEF", "123", "222"}, {"DEF", "123", "333"}}

--Do the comparison
set diff_list to {}
repeat with x from 1 to length of list_1
	set second_pt_list1 to (item 2 of item x of list_1)
	repeat with x from 1 to length of list_2
		set templist to items 1 thru 2 of (item x of list_2)
		if list_1 does not contain {templist} then
			set diff_list's end to templist & item -1 of (item x of list_2) & second_pt_list1
		end if
	end repeat
end repeat

return diff_list
--Result: {{"ABC", "444", "111", "123"}, {"ABC", "444", "111", "123"}}

The result gives me what I’m looking for, but I get two copies of the result instead of only one. I’m sure it has something to do with the first repeat, but I’m kinda stumped at this point. Thanks for the help.

Here’s my take. It returns, as asked, items 1 thru 2 of the offending item from list_2, the “correct” item from list_1 (assuming they have to begin with the same code), and, for good measure, the offending item’s position in list_2:

set list_1 to ¬
	{{"ABC", "123"}, {"DEF", "123"}}
set list_2 to ¬
	{{"ABC", "123", "000"}, {"ABC", "444", "111"}, {"DEF", "123", "222"}, {"DEF", "123", "333"}}

repeat with x from 1 to (count list_2)
	set thisValue to items 1 thru 2 of item x of list_2
	if ({thisValue} is not in list_1) then
		set v to beginning of thisValue
		repeat with y from 1 to (count list_1)
			if (item y of list_1 begins with v) then return {x, thisValue, item y of list_1}
		end repeat
	end if
end repeat
--> {2, {"ABC", "444"}, {"ABC", "123"}}

Thank you for your post, Nigel! That works wonderfully.

Hello here is another one, Nigel gave me some ideas.
This falls totally in under the readability section (hopefully).

I think it is natural to have the fewest iterations in the outer loop, and that is what Nateh did.
I have used his example. I then inverted his logic, so that I test for containment of an element in the first list.
If the element of the first list is not contained in the second list, then we are having an issue, and writes that
current element of list 2 out together with the current list element in list 1.
I should of course have added an index.
Nigels example is best as usual! :slight_smile: Readable, and with a high potential for optimization, can just plug it right in, no rework.

If you search for it, you will find some other clever routines for finding differences and their positions here at MacScripter.

Have a good night.



set list_1 to ¬
	{{"ABC", "123"}, {"DEF", "123"}}
set list_2 to ¬
	{{"ABC", "123", "000"}, {"ABC", "444", "111"}, {"DEF", "123", "222"}, {"DEF", "123", "333"}}

--Do the comparison
set diff_list to {}
repeat with x from 1 to (count list_1)
	set template to item x of list_1
	repeat with y from 1 to (count list_2)
		set templist to item y of list_2
		if templist does not contain template and beginning of templist is beginning of template then
			set diff_list's end to templist & template
		end if
	end repeat
end repeat
diff_list
--Result: {{"ABC", "444", "111", "ABC", "123"}}

Hello.

And here is the last one, a minor variation of the previous.


set list_1 to ¬
	{{"ABC", "123"}, {"DEF", "123"}}
set list_2 to ¬
	{{"ABC", "123", "000"}, {"ABC", "444", "111"}, {"DEF", "123", "222"}, {"DEF", "123", "333"}}

--Do the comparison
set diff_list to {}
repeat with template in list_1
	repeat with idx from 1 to (count list_2)
		set aSublist to item idx of list_2
		if aSublist does not contain template and beginning of aSublist is beginning of template then
			set diff_list's end to {idx, contents of aSublist, contents of template}
		end if
	end repeat
end repeat
diff_list
--Result: {{2, {"ABC", "444", "111"}, {"ABC", "123"}}}