Get Desired Array Values

I’ve been working on a project, and a good night’s sleep didn’t suggest a solution. Although not shown, a lexical sort will be done to both of the arrays at the top of the script.

use framework "Foundation"

set listOne to {"line 1", "line 2", "line 3", "line 4", "line 5", "line 5"}
set listTwo to {"line 1", "line 2", "line 5", "line 6", "line 7"}
set arrayOne to current application's NSArray's arrayWithArray:listOne
set arrayTwo to current application's NSArray's arrayWithArray:listTwo

# desired return values
# arrayOneUnique --> {"line 3", "line 4", "line 5"}
# arrayTwoUnique --> {"line 6", "line 7"}
# commonBothArrays --> {"line 1", "line 2", "line 5"}

Hi @peavine. Would some variety of mutable set get you close to what you want?

Nigel. Thanks for looking at my post. I had considered using sets but didn’t pursue that because the arrays will occasionally/often have duplicate items. Perhaps I should reconsider that because of set’s comparing sets methods.

It’s not pretty but it seems to work:

use framework "Foundation"

set listOne to {"line 1", "line 2", "line 3", "line 4", "line 5", "line 5"}
set listTwo to {"line 1", "line 2", "line 5", "line 6", "line 7"}
set arrayOne to current application's NSArray's arrayWithArray:listOne
set arrayTwo to current application's NSArray's arrayWithArray:listTwo

set arrayOneUnique to current application's NSMutableArray's new()
set arrayTwoUnique to arrayTwo's mutableCopy()
set commonArray to current application's NSMutableArray's new()

repeat with anItem in arrayOne
	if (arrayTwoUnique's containsObject:anItem) is true then --item in both arrays
		(commonArray's addObject:anItem)
		(arrayTwoUnique's removeObject:anItem)
	else --item in arrayOne only
		(arrayOneUnique's addObject:anItem)
	end if
end repeat

arrayOneUnique as list --> {"line 3", "line 4", "line 5"}
arrayTwoUnique as list --> {"line 6", "line 7"}
commonArray as list --> {"line 1", "line 2", "line 5"}
1 Like

I think peavine’s nailed it. But just for interest using NSCountedSet methods:

use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"

set listOne to {"line 1", "line 2", "line 3", "line 4", "line 5", "line 5"}
set listTwo to {"line 1", "line 2", "line 5", "line 6", "line 7"}

set setOneUnique to current application's NSCountedSet's alloc()'s initWithArray:(listOne)
set setTwoUnique to current application's NSCountedSet's alloc()'s initWithArray:(listTwo)
set commonSet to setOneUnique's |copy|() -- |copy|() or mutableCopy() doesn't matter. It's an exclusively mutable class.

commonSet's intersectSet:(setTwoUnique)
setOneUnique's minusSet:(commonSet)
setTwoUnique's minusSet:(commonSet)

set sortDescriptor to current application's NSSortDescriptor's sortDescriptorWithKey:("self") ascending:(true)
set listOneUnique to unpackAndSort(setOneUnique, sortDescriptor) --> {"line 3", "line 4", "line 5"}
set listTwoUnique to unpackAndSort(setTwoUnique, sortDescriptor) --> {"line 6", "line 7"}
set commonList to unpackAndSort(commonSet, sortDescriptor) --> {"line 1", "line 2", "line 5"}

on unpackAndSort(countedSet, sortDescriptor)
	-- sortedArrayUsingDescriptors: is inherited from NSSet via NSMutableSet.
	set array to (countedSet's sortedArrayUsingDescriptors:({sortDescriptor}))'s mutableCopy()
	-- Insert any duplicate values.
	repeat with i from ((count array) - 1) to 0 by -1
		set this to (array's objectAtIndex:(i))
		repeat ((countedSet's countForObject:(this)) - 1) times
			(array's insertObject:(this) atIndex:(i))
		end repeat
	end repeat
	return array as list
end unpackAndSort
1 Like

Thanks Nigel for the script suggestion–I’ve never had occasion to use NSCountedSet before. I’ll have to study its operation.

After further testing, my solution doesn’t work, because the removeObject method removes every instance of an object from arrayTwoUnique. However, the following appears to fix this issue:

use framework "Foundation"

set listOne to {"line 1", "line 2", "line 3", "line 4", "line 5", "line 5"}
set listTwo to {"line 1", "line 2", "line 5", "line 5", "line 6", "line 7"}
set arrayOne to current application's NSArray's arrayWithArray:listOne
set arrayTwo to current application's NSArray's arrayWithArray:listTwo

set arrayOneUnique to current application's NSMutableArray's new()
set arrayTwoUnique to arrayTwo's mutableCopy()
set commonArray to current application's NSMutableArray's new()

repeat with anItem in arrayOne
	if (arrayTwoUnique's containsObject:anItem) is true then --item in both arrays
		(commonArray's addObject:anItem)
		set theIndex to (arrayTwoUnique's indexOfObject:anItem)
		(arrayTwoUnique's removeObjectAtIndex:theIndex)
	else --item in arrayOne only
		(arrayOneUnique's addObject:anItem)
	end if
end repeat

arrayOneUnique as list --> {"line 3", "line 4"}
arrayTwoUnique as list --> {"line 6", "line 7"}
commonArray as list --> {"line 1", "line 2", "line 5", "line 5"}

I tested Nigel’s suggestion with the above lists, and it worked great.