i want to be able to create a mehtod that will filter results in an
array. The conditions are known at run time so id like to be able to do
it on the fly / dynamically.
e.g. user wants to filter results in array so that only results which
have the following categories are returned
:scifi
:comedy
:horror
id like that to be placed into a Array.find_all call like below
some_array.find_by {|x| x.category == :scifi OR x.category == :horror OR
x.category == :comedy}
how do i write the {....} bit if i dont know in advance exactly how many
/ which cateogries will be requested?
I know i could take hte users selections and put them in an array and do
this
filtered_results = []
users_filter_choices.each do |filter|
filtered_results += all_results.find_all{|x| x.category == filter
end
i dont know but is that bad for performance?
Also it assumes that the conditions must == , what happens if i want
less than or more thans in there for numerical attributes?
i want to be able to create a mehtod that will filter results in an
array. The conditions are known at run time so id like to be able to do
it on the fly / dynamically.
In general, why not use ActiveRecord and sqlite3? Maybe you have other reasons to use it, and dynamic queries is what AR's find system is all about...
some_array.find_by {|x| x.category == :scifi OR x.category == :horror OR
x.category == :comedy }
There's a very important principle in programming: After you write a unit test for a feature (you _are_ writing unit tests, aren't you?), and after you add the feature and pass the test, you then refactor to remove duplication, and make the code more DRY.
When code obeys "Don't Repeat Yourself", it becomes easier to upgrade. For example, if you had first merged all the duplicate .category == codes, you would get this:
And that's your fix - change cats. This example shows how DRY code is easer to extend and upgrade. This is counterintuitive, but I have yet to see a counterexample.
i want to be able to create a mehtod that will filter results in an
array. The conditions are known at run time so id like to be able to do
it on the fly / dynamically.
e.g. user wants to filter results in array so that only results which
have the following categories are returned
:scifi
:comedy
:horror
id like that to be placed into a Array.find_all call like below
some_array.find_by {|x| x.category == :scifi OR x.category == :horror OR
x.category == :comedy}
how do i write the {....} bit if i dont know in advance exactly how many
/ which cateogries will be requested?
I know i could take hte users selections and put them in an array and do
this
filtered_results =
users_filter_choices.each do |filter|
filtered_results += all_results.find_all{|x| x.category == filter
end
i dont know but is that bad for performance?
Could be because you traverse the array multiple times. IMHO it is generally better to do that only once, e.g.
Set is used primarily for membership testing and is thus great for lookup.
It also of course is good to eliminate duplicates thus preventing any
inefficiencies in your code.
Jayanth
···
On Mon, Apr 6, 2009 at 3:58 PM, Adam Akhtar <adamtemporary@gmail.com> wrote:
ahh robert, sorry i missed your reply there, i was looking at
unrefreshed page from earlier today.
Many thanks for your response too. Im going to use that tip above plus
the odd sprinkling of eval("") for the conditions.
i noticed you used to_set. Ive never used sets before. Is there a reason
why you chose that over a standard array?
ahh robert, sorry i missed your reply there, i was looking at unrefreshed page from earlier today.
No problem.
Many thanks for your response too. Im going to use that tip above plus the odd sprinkling of eval("") for the conditions.
I am not sure I get why you want to use eval. I try to avoid whenever possible. Why do you believe you need it?
i noticed you used to_set. Ive never used sets before. Is there a reason why you chose that over a standard array?
Set has O(1) lookup (because it is backed by a hash table internally), while an Array has O(1). Whether it makes a difference in practice needs to be measured. But if your number of categories grows then it's almost certainly more efficient then an Array.
Of course, if you get categories in an Array you pay the extra price for the conversion. If you can avoid that by directly passing a Set to the method then this would be better of course.
Kind regards
robert
···
On 06.04.2009 12:28, Adam Akhtar wrote:
--
remember.guy do |as, often| as.you_can - without end
Ahh sorry Robert re: eval - categories are only half of the problem. Im
also allowing the user to filter by max and min stuff too.
So depending on what filtering options a user has set Ill have a varying
number of conditions in my code.
So one time it could be results.find_all{|x| x.rating <= some_parameter
&& x.rating>= some_other_parameter && etc etc etc }
Since iterating over the array only once is best for performance I
thought it would be best to call find_all once and stuff all the
required conditions into it. This was where i was stumped on how ot do
that so i thought i could use eval
like so:
#conditions are collected from users choices and are stored in a hash
some_conditions = {:max_rating => 10, :min_rating => 2, :categories =>
[:horror, :scifi] }
def filter (some_films, some_conditions)
condition_string = []
condition_string.push "x.rating <= #{some_conditions[max_rating]}" if
some_conditions[max_rating]
condition_string.push "x.rating >= #{some_conditions[min_rating}" if
some_conditions[min_rating]
The above code didnt filter by categories as its just for example
purposes but I was thinking of simply calling find_all again using the
code offered before rather than drafting up a complicated eval string
involving && and ||. Id love ot hear how i can better this code.
Ahh sorry Robert re: eval - categories are only half of the problem. Im also allowing the user to filter by max and min stuff too.
So depending on what filtering options a user has set Ill have a varying number of conditions in my code.
So one time it could be results.find_all{|x| x.rating <= some_parameter && x.rating>= some_other_parameter && etc etc etc }
Since iterating over the array only once is best for performance I thought it would be best to call find_all once and stuff all the required conditions into it. This was where i was stumped on how ot do that so i thought i could use eval
like so:
#conditions are collected from users choices and are stored in a hash
some_conditions = {:max_rating => 10, :min_rating => 2, :categories => [:horror, :scifi] }
def filter (some_films, some_conditions)
condition_string =
condition_string.push "x.rating <= #{some_conditions[max_rating]}" if some_conditions[max_rating]
condition_string.push "x.rating >= #{some_conditions[min_rating}" if some_conditions[min_rating]
The above code didnt filter by categories as its just for example purposes but I was thinking of simply calling find_all again using the code offered before rather than drafting up a complicated eval string involving && and ||. Id love ot hear how i can better this code.
See above. An alternative approach would be to implement fixed conditions and combine them, e.g.
CondLess = Struct.new :cmp_val do
def ===(val)
val < cmp_val
end
end