Reputation: 149
I have this list
set myList to {{1, "Bob"}, {1.5, "Jane"}, {3, "Joe"}, {1, "Jack"}}
repeat with a in myList
--something like that a+
if item 1 of a is equal to item 1 of a+ then
--create list
end if
end repeat
Is there a way to compare the items based on their first property? I want to add to a list all the items that their first item is the same
Upvotes: 0
Views: 469
Reputation: 6092
Here's one way to do it, which also happens to be a very efficient way if you have very large lists:
to filterItems from (L as list) ¬
into |L*| as list : null ¬
thru filter as handler
local L, |L*|, filter
if |L*| = null then set |L*| to {}
script filteredItems
property array : L
property fn : filter
property list : |L*|
end script
tell the filteredItems to repeat with x in its array
if fn(x) = true then set ¬
end of its list ¬
to x's contents
end repeat
return the list of filteredItems
end filterItems
This function (handler), as its name suggests, takes a list and filters it according to some criterion. It then optionally stores the filtered items in a new list that you can pass into the handler to be populated; or you can allow the handler to return the filtered list as its result, then assign that to a new variable.
The filter is provided by means of another handler, which you define. This is a function that acts upon each element of the list, returning true
or false
depending on whether or not it passes some test that you define.
In your specific case, the test you want it to pass is that first element of each item is a specific value, e.g. 1. That way, you end up with a filtered list containing items with matching first elements.
The handler for this filter looks like this:
on firstItemEquals1(x)
item 1 of x = 1
end firstItemEquals1
A simple, one-line test to make sure the first element of each item is 1.
Then, when you run the following line:
filterItems from myList thru firstItemEquals1
the result is this:
{{1, "Bob"}, {1, "Jack"}}
We could, instead, define another filter handler that picks out only those items where its first element is an odd number; or where its second element begins with the letter "J":
on firstItemIsOdd(x)
(item 1 of x) mod 2 = 1
end firstItemIsOdd
on secondItemStartsWithJ(x)
item 2 of x starts with "J"
end secondItemStartsWithJ
Then:
filterItems from myList thru firstItemIsOdd
--> {{1, "Bob"}, {3, "Joe"}, {1, "Jack"}}
filterItems from myList thru secondItemStartsWithJ
--> {{1.5, "Jane"}, {3, "Joe"}, {1, "Jack"}}
what if I have a lot of duplicate first values? I have to create handlers for each one of them ?
No, but we do have to make a slight adjustment to the filterItems
handler, and create another handler that works alongside it that will allow us to use nested functions to perform laborious tasks repeatedly without having to then create additional handlers for each task.
to filterItems from (L as list) ¬
into |L*| as list : null ¬
thru filter
local L, |L*|, filter
if |L*| = null then set |L*| to {}
script itemsToBeFiltered
property array : L
end script
script filteredItems
property list : |L*|
end script
repeat with x in the array of itemsToBeFiltered
tell wrapper(filter) to if fn(x) = true ¬
then set end of filteredItems's list ¬
to contents of x
end repeat
return the list of filteredItems
end filterItems
The main change of significance is the introduction of the tell wrapper(filter)
line. We're introducing a helper function to nest our filter handlers inside a script object. The wrapper
function looks like this:
on wrapper(function)
if function's class = script then return function
script
property fn : function
end script
end wrapper
Now, we can define filter handlers that look like this:
on firstItemEquals(N)
script
on fn(x)
item 1 of x = N
end fn
end script
end firstItemEquals
This new version of the handler from before allows us to choose the value with which to compare the first element of each item in your list. This way, we don't need to write separate handlers; we can just do this:
filterItems from myList thru firstItemEquals(1)
--> {{1, "Bob"}, {1, "Jack"}}
and then this:
filterItems from myList thru firstItemEquals(3)
--> {{3, "Joe"}}
Upvotes: 1