I am trying to reject array items based on multiple conditions.
The code is as follows
def fetch_items
items= line_items.reject(&driving?)
if new_order_history_enabled?
items = items.reject{ |li| li.expenses == 0 }
end
items
end
def driving?
proc { |line_item| LineItemType.new(line_item, segment).drive? }
end
Is there a one liner or a more cleaner way to write this?
Something like
items= line_items.reject { |li| li.driving? && ( new_order_history_enabled? && li.expenses == 0)}
items= line_items.reject { |li| li.driving? || (new_order_history_enabled? && li.expenses == 0)}
Since you want both to apply here, I think you should use || instead of &&
That way, you are actually doing what you describe in your method. (and you only iterate once over the array, which is cool :) )
Although, and this is stylistic preference. I would prefer to do:
items = line_items.reject do |li|
li.driving? ||
(new_order_history_enabled? && li.expenses == 0)
end
since it might be clearer at a glance what we are doing
Personally I don't think a one-liner is always cleaner, especially when it's a long one-liner. The style that (to me) is cleaner, is to write:
def fetch_items
items= line_items.reject(&:driving?)
items= items.reject(&:zero_expenses?) if new_order_history_enabled?
end
def driving?
proc { |line_item| LineItemType.new(line_item, segment).drive? }
end
# in the LineItem class, define the zero_expenses? method:
def zero_expenses?
expenses.zero?
end
I have some data:
data = {
"total_records"=>3,
"records"=>
[{"title"=>"Val1",
"coins"=>1},
{"title"=>"Val2",
"coins"=>1},
{"title"=>"Val3",
"coins"=>1}]
}
How would I go about deleting the records that have title = 'Val1'||'Val2'?
I tried something along these lines:
#data.records.each_value do |e|
if exceptions.include?(e.title)
delete #envelopes.records.e
end
end
But I get a no method error on #data.records.
So simple
data['records'].delete_if{ |h| %w(Val1 Val2).include?(h['title']) }
You can also do this:
data['records'].delete_if{ |h| (h['title'] == 'Val1') || (h['title'] == 'Val2') }
If I have a hash in ruby like this,
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
How can I achieve this by single line script
second = {:a=>1,:c=>3,:e=>5}
Thank you very much.
Exactly what you want in a single pretty line of code
second = first.slice(:a, :c, :e) # => {:a=>1, :c=>3, :e=>5}
EDIT: previous answer was using Rails. This is a solution just using Ruby
second = first.delete_if {|k,v| ![:a, :c, :e].include?(k) } # => {:a=>1, :c=>3, :e=>5}
Try delete_if or keep_if, both of them are part of core Ruby. Both of them operate on the current hash. slice is also part of core Ruby already.
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
first_clone = first.clone
p first.keep_if { |key| [:a, :c, :e].include?(key) } # => {:a=>1,:c=>3,:e=>5}
p first_clone.delete_if { |key, value| [:b, :d, :f].include?(key) } # => {:a=>1,:c=>3,:e=>5}
Documentation:
delete_if
keep_if
slice
first.keep_if{|key| [:a,:c,:d].include?(key)}
Method 1
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
first.delete(:b)
first.delete(:d)
second = first
Method 2
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
second = first.delete_if {|key, value| key == :b || key == :d }
Assuming you don't know the keys you want to keep / remove ... you could do it like this:
first = {:a=>1,:b=>2,:c=>3,:d=>4,:e=>5}
iterator = 0
second = {}
first.each_pair do |key, value|
second[key] = value if iterator % 2 == 0
iterator += 1
end
second # is now {:a=>1,:c=>3,:e=>5}
Say I have an array, and I use the keep_if or select methods to delete everything but one object for the array -- is there a way to take that object out of the array, so it's not an array any more?
For example, the way my models are set up, previous and current will only ever have one object.
def more_or_less?(project, current_day)
words = project.words
current = words.select {|w| w.wrote_on == current_day}
previous = words.select {|w| w.wrote_on == (current_day - 1.day)}
end
So, if I want to do a comparison, like if current.quantity > previous.quantity -- I've resorted to actually writing if current.last.quantity > previous.last.quantity but I'm assuming there's a more idiomatic way?
If you're deleting all but one entries, why not use find to choose the one thing you care about? Something like this:
def more_or_less?(project, current_day)
words = project.words
current = words.find { |w| w.wrote_on == current_day }
previous = words.find { |w| w.wrote_on == (current_day - 1.day) }
if current.quantity > previous.quantity
#...
end
end
If you're in ActiveRecord land, then you can use detect as derp says or to_a to get a plain array that you can use find on:
def more_or_less?(project, current_day)
words = project.words
current = words.detect { |w| w.wrote_on == current_day }
previous = words.to_a.find { |w| w.wrote_on == (current_day - 1.day) }
if current.quantity > previous.quantity
#...
end
end
detect is an alias for find, maybe that would work
I am using the following logic to update a list item based on a criteria.
def update_orders_list(order)
#orders.delete_if{|o| o.id == order.id}
#orders << order
end
Ideally, I would have preferred these approaches:
array.find_and_replace(obj) { |o| conditon }
OR
idx = array.find_index_of { |o| condition }
array[idx] = obj
Is there a better way?
array.map { |o| if condition(o) then obj else o }
maybe?
As of 1.8.7, Array#index accepts a block. So your last example should work just fine with a minor tweak.
idx = array.index { |o| condition }
array[idx] = obj