What’s the method to sort an array of dictionaries according to a list?
Let’s say the dictionaries contains a key named “category”.
The values can be {“bird”,“fish”,“dog”,“cat”}.
I want the array sorted in this order an not in alphabetical.
What’s the method to sort an array of dictionaries according to a list?
Let’s say the dictionaries contains a key named “category”.
The values can be {“bird”,“fish”,“dog”,“cat”}.
I want the array sorted in this order an not in alphabetical.
There isn’t such an animal.
and
With this sort saved in a Script Libraries folder as “Custom Iterative Ternary Merge Sort.scpt”:
use framework "Foundation"
use sorter : script "Custom Iterative Ternary Merge Sort"
local dictionaryArray, sortedArray
set dictionaryArray to current application's class "NSArray"'s arrayWithArray:({{category:"fish", thing:"guppy"}, {category:"dog", thing:"chihuahua"}, {category:"fish", thing:"hammerhead shark"}, {category:"cat", thing:"tiger"}, {category:"bird", thing:"cuckoo"}, {category:"dog", thing:"husky"}, {category:"cat", thing:"wildcat"}, {category:"dog", thing:"border collie"}, {category:"bird", thing:"bald eagle"}})
set sortedArray to my sortDictionaryArray:dictionaryArray withCategoryOrder:{"bird", "fish", "dog", "cat"}
(* Sort an array of category/thing dictionaries with the categories in a specified order and the things sorted naturally in them. *)
on sortDictionaryArray:dictionaryArray withCategoryOrder:categoryList
-- Script object containing a custom comparison handler for the sort.
script o
property categoryOrder : categoryList
property categoryCount : (count categoryList)
on isGreater(a, b) -- Return true if a should be moved to after b, otherwise false.
set categoryA to a's category
set categoryB to b's category
if (categoryA = categoryB) then
-- Are the categories are the same with a's thing coming after b's thing?
return (a's thing > b's thing)
else
-- Does a's category come after b's in the specified order?
repeat with i from 1 to categoryCount
if (item i of my categoryOrder is categoryA) then exit repeat
end repeat
repeat with j from 1 to categoryCount
if (j < i) then
if (item j of my categoryOrder is categoryB) then return true
else
return false
end if
end repeat
end if
end isGreater
end script
-- Coerce the dictionary array to a list of records.
set recordList to dictionaryArray as list
-- Sort items 1 thru -1 of the list using the comparison script above.
tell sorter to sort(recordList, 1, -1, {comparer:o})
-- Return an array version of the sorted list.
return current application's class "NSArray"'s arrayWithArray:(recordList)
end sortDictionaryArray:withCategoryOrder:
Very impressive!
I think it will take me some time to understand all that!
What matters for now is that everything works well, and fast.
Thank you for your help.
If you wanted to stay in ASObjC, you could perhaps do it this way:
use framework "Foundation"
local dictionaryArray, subsortedArray, sortedArray, sortOnThing, filter
set dictionaryArray to current application's class "NSArray"'s arrayWithArray:({{category:"fish", thing:"guppy"}, {category:"dog", thing:"chihuahua"}, {category:"fish", thing:"hammerhead shark"}, {category:"cat", thing:"tiger"}, {category:"bird", thing:"cuckoo"}, {category:"dog", thing:"husky"}, {category:"cat", thing:"wildcat"}, {category:"dog", thing:"border collie"}, {category:"bird", thing:"bald eagle"}})
-- Initially sort on the 'thing' values.
set sortOnThing to current application's class "NSSortDescriptor"'s sortDescriptorWithKey:("thing") ascending:(true)
set subsortedArray to dictionaryArray's sortedArrayUsingDescriptors:({sortOnThing})
-- Then extract dictionary arrays in the required 'category' order and stick them together.
set sortedArray to current application's class "NSMutableArray"'s new()
repeat with thisCategory in {"bird", "fish", "dog", "cat"}
set filter to current application's class "NSPredicate"'s predicateWithFormat_("category == %@", thisCategory)
tell sortedArray to addObjectsFromArray:(subsortedArray's filteredArrayUsingPredicate:(filter))
end repeat
sortedArray
You are reading my mind!
Thank you again.